diff --git a/.gitignore b/.gitignore index 5cc7faa8b479..0a3a9ae82643 100644 --- a/.gitignore +++ b/.gitignore @@ -78,6 +78,7 @@ packages/documentation/output/attribution.json packages/sandbox/src/releases.json packages/sandbox/src/release_data.ts packages/typescriptlang-org/src/lib/documentationNavigation.ts +packages/typescriptlang-org/static/play/7/ .idea/ diff --git a/packages/ts7-playground/package.json b/packages/ts7-playground/package.json new file mode 100644 index 000000000000..02d95e179089 --- /dev/null +++ b/packages/ts7-playground/package.json @@ -0,0 +1,30 @@ +{ + "name": "@typescript/ts7-playground", + "private": true, + "type": "module", + "scripts": { + "build": "node scripts/build.mjs", + "dev": "node scripts/build.mjs --serve", + "test": "node scripts/smoke-test.mjs", + "typecheck": "tsc --noEmit", + "vendor-typescript": "node scripts/vendor-typescript.mjs" + }, + "dependencies": { + "@bjorn3/browser_wasi_shim": "^0.4.2", + "@fontsource/nunito-sans": "^5.2.6", + "@typescript/typescript": "file:vendor/typescript", + "@typescript/typescript-wasip1-wasm": "file:vendor/typescript-wasip1-wasm", + "@vscode/monaco-lsp-client": "file:vendor/vscode-monaco-lsp-client-0.1.0.tgz", + "coi-serviceworker": "^0.1.7", + "hack-font": "^3.3.0", + "lz-string": "^1.5.0", + "monaco-editor": "0.56.0", + "monaco-editor-core": "0.56.0", + "vscode-json-languageservice": "^5.3.11", + "vscode-languageserver-textdocument": "^1.0.11" + }, + "devDependencies": { + "esbuild": "^0.27.3", + "typescript": "*" + } +} diff --git a/packages/ts7-playground/scripts/build.mjs b/packages/ts7-playground/scripts/build.mjs new file mode 100644 index 000000000000..4cdf6cacedf7 --- /dev/null +++ b/packages/ts7-playground/scripts/build.mjs @@ -0,0 +1,98 @@ +import { context } from "esbuild" +import { createHash } from "node:crypto" +import { cp, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises" +import { basename, dirname, resolve } from "node:path" +import { fileURLToPath } from "node:url" + +const packageDirectory = resolve(dirname(fileURLToPath(import.meta.url)), "..") +const websiteDirectory = resolve(packageDirectory, "../..") +const vendorDirectory = resolve(packageDirectory, "vendor") +const outputDirectory = resolve(packageDirectory, "dist") +const websiteStaticDirectory = resolve(websiteDirectory, "packages/typescriptlang-org/static/play/7") +const serve = process.argv.includes("--serve") +const playgroundBase = serve ? "/" : process.env.PLAYGROUND_BASE ?? "/play/7/" + +const wasmFile = resolve(vendorDirectory, "typescript-wasip1-wasm/dist/tsc.wasm") +const libDirectory = resolve(vendorDirectory, "lib") +const configSchema = resolve(websiteDirectory, "packages/tsconfig-reference/scripts/schema/result/schema.json") +const editorWorker = fileURLToPath(import.meta.resolve("monaco-editor/editor/editor.worker")) +const coiServiceWorker = fileURLToPath(import.meta.resolve("coi-serviceworker/coi-serviceworker.min.js")) + +const version = (await readFile(resolve(vendorDirectory, "version.txt"), "utf8")).trim() +const indexHtml = (await readFile(resolve(packageDirectory, "src/index.html"), "utf8")) + .replaceAll("__PLAYGROUND_BASE__", playgroundBase) + .replace("", `<title data-typescript-version="${version}">`) + +await rm(outputDirectory, { force: true, recursive: true }) +await mkdir(outputDirectory, { recursive: true }) +const libFileNames = (await readdir(libDirectory)).filter(fileName => /^lib(?:\..+)?\.d\.ts$/.test(fileName)).sort() +const libFiles = Object.fromEntries( + await Promise.all( + libFileNames.map(async fileName => [ + `/${basename(fileName)}`, + await readFile(resolve(libDirectory, fileName), "utf8"), + ]) + ) +) +const libFilesJSON = JSON.stringify(libFiles) +const [wasmBytes, schemaBytes] = await Promise.all([readFile(wasmFile), readFile(configSchema)]) +const assetCacheVersion = createHash("sha256") + .update(wasmBytes) + .update(libFilesJSON) + .update(schemaBytes) + .digest("hex") + .slice(0, 16) +await Promise.all([ + writeFile(resolve(outputDirectory, "index.html"), indexHtml), + cp(coiServiceWorker, resolve(outputDirectory, "coi-serviceworker.min.js")), + cp(configSchema, resolve(outputDirectory, "tsconfig.schema.json")), + cp(wasmFile, resolve(outputDirectory, "tsc.wasm")), + writeFile(resolve(outputDirectory, "lib-files.json"), libFilesJSON), +]) + +const buildContext = await context({ + absWorkingDir: packageDirectory, + bundle: true, + conditions: ["browser", "default"], + define: { + __ASSET_CACHE_VERSION__: JSON.stringify(assetCacheVersion), + __LOAD_ASSET_SIZES__: JSON.stringify({ + libraries: Buffer.byteLength(libFilesJSON), + schema: schemaBytes.byteLength, + wasm: wasmBytes.byteLength, + }), + __TS_VERSION__: JSON.stringify(version), + }, + entryNames: "[name]", + entryPoints: { + main: resolve(packageDirectory, "src/main.ts"), + "editor.worker": editorWorker, + "tsgo-lsp.worker": resolve(packageDirectory, "src/tsgo-lsp.worker.ts"), + }, + format: "esm", + loader: { + ".ttf": "file", + ".woff": "file", + ".woff2": "file", + }, + outdir: outputDirectory, + platform: "browser", + sourcemap: true, + target: ["es2022"], +}) + +if (serve) { + await buildContext.watch() + const server = await buildContext.serve({ + host: "127.0.0.1", + port: 4173, + servedir: outputDirectory, + }) + console.log(`TypeScript 7 playground: http://${server.host}:${server.port}`) +} else { + await buildContext.rebuild() + await buildContext.dispose() + await rm(websiteStaticDirectory, { force: true, recursive: true }) + await cp(outputDirectory, websiteStaticDirectory, { recursive: true }) + console.log(`Built TypeScript ${version} playground in ${outputDirectory}`) +} diff --git a/packages/ts7-playground/scripts/smoke-test.mjs b/packages/ts7-playground/scripts/smoke-test.mjs new file mode 100644 index 000000000000..6920df911df8 --- /dev/null +++ b/packages/ts7-playground/scripts/smoke-test.mjs @@ -0,0 +1,66 @@ +import assert from "node:assert/strict" +import { readFile, readdir } from "node:fs/promises" +import { resolve } from "node:path" +import { API } from "@typescript/typescript/unstable/sync" +import { instantiateWasm, WasmTransport, wasmURL } from "@typescript/typescript-wasip1-wasm" + +const packageDirectory = resolve(import.meta.dirname, "..") +const wasm = await readFile(wasmURL) +const module = await WebAssembly.compile(wasm) +const instance = await instantiateWasm(module) +const transport = new WasmTransport({ instance, cwd: "/workspace" }) +const api = new API({ transport }) + +try { + const libDirectory = resolve(packageDirectory, "vendor/lib") + const libFileNames = (await readdir(libDirectory)).filter(fileName => /^lib(?:\..+)?\.d\.ts$/.test(fileName)) + for (const fileName of libFileNames) { + transport.setFile(`/${fileName}`, await readFile(resolve(libDirectory, fileName), "utf8")) + } + + const files = { + "/workspace/tsconfig.json": JSON.stringify({ + compilerOptions: { + declaration: true, + module: "CommonJS", + strict: true, + target: "ES2022", + }, + include: ["./src/**/*"], + }), + "/workspace/src/greet.ts": "export const greet = (name: string) => `Hello, ${name}!`;", + "/workspace/src/index.ts": 'import { greet } from "./greet"; console.log(greet("TS7"));', + } + for (const [fileName, source] of Object.entries(files)) { + transport.setFile(fileName, source) + } + + const config = api.readConfigFile("/workspace/tsconfig.json") + assert.equal(config.error, undefined) + const parsed = api.parseJsonConfigFileContent(config.config, { + configFileName: "/workspace/tsconfig.json", + }) + assert.deepEqual(parsed.fileNames, ["/workspace/src/greet.ts", "/workspace/src/index.ts"]) + const program = api.createProgram(parsed.fileNames, { + compilerOptions: parsed.options, + projectReferences: parsed.projectReferences, + configFileParsingDiagnostics: parsed.errors, + }) + try { + assert.equal(program.getSyntacticDiagnostics().length, 0) + assert.equal(program.getSemanticDiagnostics().length, 0) + const emit = program.emitToString() + assert.equal(emit.emitSkipped, false) + assert.deepEqual( + [...emit.outputFiles.keys()], + ["/workspace/src/greet.d.ts", "/workspace/src/greet.js", "/workspace/src/index.d.ts", "/workspace/src/index.js"] + ) + assert.match(emit.outputFiles.get("/workspace/src/index.js").text, /require\("\.\/greet"\)/) + } finally { + program.dispose() + } +} finally { + api.close() +} + +console.log("TypeScript WASM API smoke test passed") diff --git a/packages/ts7-playground/scripts/vendor-typescript.mjs b/packages/ts7-playground/scripts/vendor-typescript.mjs new file mode 100644 index 000000000000..faa704617765 --- /dev/null +++ b/packages/ts7-playground/scripts/vendor-typescript.mjs @@ -0,0 +1,97 @@ +import { cp, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises" +import { dirname, resolve } from "node:path" +import { fileURLToPath } from "node:url" +import { spawnSync } from "node:child_process" + +const packageDirectory = resolve(dirname(fileURLToPath(import.meta.url)), "..") +const websiteDirectory = resolve(packageDirectory, "../..") +const typescriptDirectory = resolve( + process.env.TYPESCRIPT_REPO || resolve(websiteDirectory, "../TypeScript"), +) +const vendorDirectory = resolve(packageDirectory, "vendor") +const typescriptPackage = resolve(typescriptDirectory, "packages/typescript") +const wasmPackage = resolve(typescriptDirectory, "packages/typescript-wasip1-wasm") +const libDirectory = resolve(typescriptDirectory, "built/local") +const packageNames = ["typescript", "typescript-wasip1-wasm"] +const legalFiles = ["LICENSE.txt", "NOTICE.txt"] + +await Promise.all([ + rm(resolve(vendorDirectory, "lib"), { force: true, recursive: true }), + rm(resolve(vendorDirectory, "typescript"), { force: true, recursive: true }), + rm(resolve(vendorDirectory, "typescript-wasip1-wasm"), { force: true, recursive: true }), + rm(resolve(vendorDirectory, "version.txt"), { force: true }), +]) +await Promise.all( + packageNames.map(packageName => mkdir(resolve(vendorDirectory, packageName), { recursive: true })), +) +await Promise.all([ + cp(resolve(typescriptPackage, "dist"), resolve(vendorDirectory, "typescript/dist"), { + recursive: true, + }), + cp(resolve(wasmPackage, "dist"), resolve(vendorDirectory, "typescript-wasip1-wasm/dist"), { + recursive: true, + }), + writeVendorManifest(typescriptPackage, "typescript"), + writeVendorManifest(wasmPackage, "typescript-wasip1-wasm"), + ...packageNames.flatMap(packageName => + legalFiles.map(fileName => + cp( + resolve(typescriptDirectory, fileName), + resolve(vendorDirectory, packageName, fileName), + ) + ) + ), +]) + +const vendorLibDirectory = resolve(vendorDirectory, "lib") +await mkdir(vendorLibDirectory, { recursive: true }) +const libFileNames = (await readdir(libDirectory)) + .filter(fileName => /^lib(?:\..+)?\.d\.ts$/.test(fileName)) +await Promise.all( + libFileNames.map(fileName => + cp(resolve(libDirectory, fileName), resolve(vendorLibDirectory, fileName)) + ), +) + +const versionResult = spawnSync( + resolve(typescriptDirectory, "built/local/tsc"), + ["--version"], + { encoding: "utf8" }, +) +if (versionResult.status !== 0) { + throw new Error(versionResult.stderr || "Unable to read the TypeScript version") +} +const version = versionResult.stdout.trim().replace(/^Version\s+/, "") +await writeFile(resolve(vendorDirectory, "version.txt"), `${version}\n`) + +console.log(`Vendored TypeScript ${version} from ${typescriptDirectory}`) + +async function writeVendorManifest(sourceDirectory, vendorName) { + const manifest = JSON.parse(await readFile(resolve(sourceDirectory, "package.json"), "utf8")) + const exports = { ...manifest.exports } + const imports = { ...manifest.imports } + if (vendorName === "typescript") { + delete exports["."] + delete imports["#getExePath"] + delete imports["#vscode-jsonrpc/node"] + imports["#asyncClient"] = "./dist/api/async/browserClient.js" + imports["#syncClient"] = "./dist/api/sync/browserClient.js" + imports["#enums/*"] = { + types: "./dist/enums/*.enum.d.ts", + default: "./dist/enums/*.js", + } + } + const vendoredManifest = { + name: manifest.name, + version: manifest.version, + license: manifest.license, + type: manifest.type, + files: ["dist", ...legalFiles], + exports, + imports, + } + await writeFile( + resolve(vendorDirectory, vendorName, "package.json"), + `${JSON.stringify(vendoredManifest, undefined, 2)}\n`, + ) +} diff --git a/packages/ts7-playground/src/config-schema.ts b/packages/ts7-playground/src/config-schema.ts new file mode 100644 index 000000000000..2614d27c2487 --- /dev/null +++ b/packages/ts7-playground/src/config-schema.ts @@ -0,0 +1,209 @@ +import { + CompletionItemKind, + DiagnosticSeverity, + getLanguageService, + InsertTextFormat, + MarkupKind, + type CompletionItem, + type Diagnostic, + type Hover, + type JSONSchema, + type MarkedString, + type MarkupContent, + type Range, +} from "vscode-json-languageservice" +import { TextDocument } from "vscode-languageserver-textdocument" +import { monaco } from "./tsgo-lsp" + +const schemaUri = "https://json.schemastore.org/tsconfig" +const markerOwner = "tsconfig-schema" + +export function registerConfigSchema(schema: JSONSchema) { + const service = getLanguageService({}) + service.configure({ + allowComments: true, + schemas: [ + { + fileMatch: ["**/tsconfig.json", "**/jsconfig.json"], + schema, + uri: schemaUri, + }, + ], + validate: true, + }) + + monaco.languages.registerCompletionItemProvider("json", { + triggerCharacters: ['"', ":"], + async provideCompletionItems(model, position) { + if (!isConfigModel(model)) return { suggestions: [] } + const document = createDocument(model) + const jsonDocument = service.parseJSONDocument(document) + const completions = await service.doComplete(document, toLspPosition(position), jsonDocument) + return { + incomplete: completions?.isIncomplete, + suggestions: (completions?.items ?? []).map(item => toMonacoCompletion(model, position, item)), + } + }, + }) + + monaco.languages.registerHoverProvider("json", { + async provideHover(model, position) { + if (!isConfigModel(model)) return undefined + const document = createDocument(model) + const hover = await service.doHover(document, toLspPosition(position), service.parseJSONDocument(document)) + return hover ? toMonacoHover(hover) : undefined + }, + }) + + const timers = new Map<string, number>() + const registerModel = (model: monaco.editor.ITextModel) => { + if (!isConfigModel(model)) return + const validate = () => { + window.clearTimeout(timers.get(model.uri.toString())) + timers.set( + model.uri.toString(), + window.setTimeout(async () => { + if (model.isDisposed()) return + const document = createDocument(model) + const diagnostics = ( + await service.doValidation(document, service.parseJSONDocument(document), { + comments: "ignore", + trailingCommas: "ignore", + }) + ).filter(diagnostic => !isCaseInsensitiveEnumMatch(document, diagnostic)) + monaco.editor.setModelMarkers(model, markerOwner, diagnostics.map(toMonacoDiagnostic)) + }, 150) + ) + } + model.onDidChangeContent(validate) + validate() + } + + monaco.editor.getModels().forEach(registerModel) + monaco.editor.onDidCreateModel(registerModel) +} + +function isCaseInsensitiveEnumMatch(document: TextDocument, diagnostic: Diagnostic) { + const message = typeof diagnostic.message === "string" ? diagnostic.message : diagnostic.message.value + if (!message.startsWith("Value is not accepted. Valid values:")) return false + const source = document.getText(diagnostic.range) + let value: unknown + try { + value = JSON.parse(source) + } catch { + return false + } + if (typeof value !== "string") return false + const allowed = [...message.matchAll(/"([^"]+)"/g)].map(match => match[1]) + return allowed.some(candidate => candidate.toLowerCase() === value.toLowerCase()) +} + +function isConfigModel(model: monaco.editor.ITextModel) { + return /\/(?:js|ts)config\.json$/i.test(model.uri.path) +} + +function createDocument(model: monaco.editor.ITextModel) { + return TextDocument.create(model.uri.toString(), "json", model.getVersionId(), model.getValue()) +} + +function toMonacoCompletion( + model: monaco.editor.ITextModel, + position: monaco.Position, + item: CompletionItem +): monaco.languages.CompletionItem { + const word = model.getWordUntilPosition(position) + const fallbackRange = new monaco.Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn) + const textEdit = item.textEdit && "range" in item.textEdit ? item.textEdit : undefined + return { + detail: item.detail, + documentation: toMarkdown(item.documentation), + filterText: item.filterText, + insertText: textEdit?.newText ?? item.insertText ?? item.label, + insertTextRules: + item.insertTextFormat === InsertTextFormat.Snippet + ? monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet + : undefined, + kind: toMonacoCompletionKind(item.kind), + label: item.label, + range: textEdit ? toMonacoRange(textEdit.range) : fallbackRange, + sortText: item.sortText, + } +} + +function toMonacoCompletionKind(kind: CompletionItemKind | undefined) { + switch (kind) { + case CompletionItemKind.Value: + return monaco.languages.CompletionItemKind.Value + case CompletionItemKind.Enum: + case CompletionItemKind.EnumMember: + return monaco.languages.CompletionItemKind.Enum + case CompletionItemKind.Keyword: + return monaco.languages.CompletionItemKind.Keyword + case CompletionItemKind.Property: + case CompletionItemKind.Field: + return monaco.languages.CompletionItemKind.Property + default: + return monaco.languages.CompletionItemKind.Text + } +} + +function toMonacoHover(hover: Hover): monaco.languages.Hover { + return { + contents: Array.isArray(hover.contents) + ? hover.contents.map(toMarkdown).filter(isMarkdown) + : [toMarkdown(hover.contents)].filter(isMarkdown), + range: hover.range ? toMonacoRange(hover.range) : undefined, + } +} + +function toMarkdown(value: string | MarkedString | MarkupContent | undefined): monaco.IMarkdownString | undefined { + if (value === undefined) return undefined + if (typeof value === "string") return { value } + if ("language" in value) { + return { value: `\`\`\`${value.language}\n${value.value}\n\`\`\`` } + } + return { + value: value.kind === MarkupKind.Markdown ? value.value : value.value.replace(/[\\`*_{}[\]()#+\-.!]/g, "\\$&"), + } +} + +function isMarkdown(value: monaco.IMarkdownString | undefined): value is monaco.IMarkdownString { + return value !== undefined +} + +function toMonacoDiagnostic(diagnostic: Diagnostic): monaco.editor.IMarkerData { + return { + code: diagnostic.code?.toString(), + endColumn: diagnostic.range.end.character + 1, + endLineNumber: diagnostic.range.end.line + 1, + message: typeof diagnostic.message === "string" ? diagnostic.message : diagnostic.message.value, + severity: toMonacoSeverity(diagnostic.severity), + source: diagnostic.source ?? "TSConfig", + startColumn: diagnostic.range.start.character + 1, + startLineNumber: diagnostic.range.start.line + 1, + } +} + +function toMonacoSeverity(severity: DiagnosticSeverity | undefined) { + switch (severity) { + case DiagnosticSeverity.Error: + return monaco.MarkerSeverity.Error + case DiagnosticSeverity.Warning: + return monaco.MarkerSeverity.Warning + case DiagnosticSeverity.Information: + return monaco.MarkerSeverity.Info + default: + return monaco.MarkerSeverity.Hint + } +} + +function toLspPosition(position: monaco.Position) { + return { + character: position.column - 1, + line: position.lineNumber - 1, + } +} + +function toMonacoRange(range: Range) { + return new monaco.Range(range.start.line + 1, range.start.character + 1, range.end.line + 1, range.end.character + 1) +} diff --git a/packages/ts7-playground/src/global.d.ts b/packages/ts7-playground/src/global.d.ts new file mode 100644 index 000000000000..a65f4858c64d --- /dev/null +++ b/packages/ts7-playground/src/global.d.ts @@ -0,0 +1,11 @@ +declare module "*.css" + +declare module "monaco-editor/languages/definitions/javascript/javascript.js" { + export const conf: import("monaco-editor-core").languages.LanguageConfiguration + export const language: import("monaco-editor-core").languages.IMonarchLanguage +} + +declare module "monaco-editor/languages/definitions/typescript/typescript.js" { + export const conf: import("monaco-editor-core").languages.LanguageConfiguration + export const language: import("monaco-editor-core").languages.IMonarchLanguage +} diff --git a/packages/ts7-playground/src/index.html b/packages/ts7-playground/src/index.html new file mode 100644 index 000000000000..c517bc47b369 --- /dev/null +++ b/packages/ts7-playground/src/index.html @@ -0,0 +1,92 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <base href="__PLAYGROUND_BASE__" /> + <script> + const playgroundBase = "__PLAYGROUND_BASE__" + if (playgroundBase !== "/" && location.pathname === playgroundBase.slice(0, -1)) { + const url = new URL(location.href) + url.pathname = playgroundBase + location.replace(url) + } + </script> + <script src="./coi-serviceworker.min.js"></script> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1" /> + <meta name="color-scheme" content="light dark" /> + <title>TypeScript 7 Playground + + + +
+
+
+

TypeScript 7 Playground

+
+
+ + Loading compiler... +
+
+
+ +
+
+

index.ts

+ Type query: align ^? below an expression +
+
+
+ +
+
+
+ +
+

Downloading TypeScript...

+ + Preparing downloads +
+
+
+
+ + + diff --git a/packages/ts7-playground/src/main.ts b/packages/ts7-playground/src/main.ts new file mode 100644 index 000000000000..414172890046 --- /dev/null +++ b/packages/ts7-playground/src/main.ts @@ -0,0 +1,1000 @@ +import { API, DiagnosticCategory, type Diagnostic } from "@typescript/typescript/unstable/sync" +import { instantiateWasm, WasmTransport } from "@typescript/typescript-wasip1-wasm" +import LZString from "lz-string" +import { registerConfigSchema } from "./config-schema" +import { monaco, registerPlaygroundLanguages, startTsgoLsp, type TsgoStatus } from "./tsgo-lsp" +import "./styles.css" + +declare const __TS_VERSION__: string +declare const __ASSET_CACHE_VERSION__: string +declare const __LOAD_ASSET_SIZES__: { + libraries: number + schema: number + wasm: number +} + +type CompilerNode = { + forEachChild(visitor: (node: CompilerNode) => T): T | undefined + getEnd(): number + getFullStart(): number +} + +type TypeQuery = { + lineNumber: number + column: number + label: string +} + +type ProjectFile = { + path: string + language: "javascript" | "json" | "typescript" + text: string +} + +type ProjectState = { + activeFile?: string + files: Record +} + +type RuntimeLog = { + level: "debug" | "error" | "info" | "log" | "warn" + text: string +} + +declare global { + interface Window { + ts: API & { + API: typeof API + DiagnosticCategory: typeof DiagnosticCategory + version: string + } + } +} + +;( + self as typeof self & { + MonacoEnvironment: { getWorker(): Worker } + } +).MonacoEnvironment = { + getWorker() { + return new Worker(new URL("./editor.worker.js", import.meta.url), { type: "module" }) + }, +} + +const projectRoot = "/workspace" +const configFileName = `${projectRoot}/tsconfig.json` +const entryFileName = `${projectRoot}/src/index.ts` +const storageKey = "ts7-playground-project" +const defaultFiles: ProjectFile[] = [ + { + path: configFileName, + language: "json", + text: `{ + "compilerOptions": { + "target": "ES2022", + "module": "CommonJS", + "strict": true, + "declaration": true, + "esModuleInterop": true, + "skipLibCheck": true + }, + "include": ["./src/**/*"] +} +`, + }, + { + path: entryFileName, + language: "typescript", + text: `import { greet } from "./greet" + +const message = greet("TypeScript 7") +// ^? + +console.log(message) +`, + }, + { + path: `${projectRoot}/src/greet.ts`, + language: "typescript", + text: `export function greet(name: string) { + return \`Hello, \${name}!\` +} +`, + }, +] + +const inputElement = getElement("input-editor") +const fileList = getElement("file-list") +const newFileButton = getElement("new-file-button") +const resetProjectButton = getElement("reset-project-button") +const currentFile = getElement("current-file") +const editorHint = getElement("editor-hint") +const emitOutput = getElement("emit-output") +const emitSummary = getElement("emit-summary") +const runButton = getElement("run-button") +const clearRunOutput = getElement("clear-run-output") +const runLog = getElement("run-log") +const status = getElement("status") +const loader = getElement("loader") +const loadingMessage = getElement("loading-message") +const loadingProgress = getElement("loading-progress") +const loadingDetail = getElement("loading-detail") + +let compilerReady = false +let lspReady = false +let lspStatus: TsgoStatus = "mounting files" +let lspServerInfo: string | undefined +let diagnosticCount = 0 +let compilerFailure: string | undefined +let lspFailure: string | undefined +let projectFailure: string | undefined +let compilerTransport: WasmTransport | undefined +let emittedFiles = new Map() +let emitRenderVersion = 0 +const downloadedAssets = new Map() +const cachedAssets = new Map() +const assetCachePrefix = "ts7-playground-assets-" +let assetCachePromise: Promise | undefined + +const darkMode = matchMedia("(prefers-color-scheme: dark)").matches +monaco.editor.defineTheme("typescript-playground", { + base: darkMode ? "vs-dark" : "vs", + inherit: true, + rules: [ + { token: "comment", foreground: darkMode ? "7caf3d" : "6c6f2d" }, + { token: "keyword", foreground: darkMode ? "569cd6" : "3757ef" }, + { token: "type", foreground: darkMode ? "4ec9b0" : "1142af" }, + { token: "class", foreground: darkMode ? "4ec9b0" : "267f99" }, + { token: "enum", foreground: darkMode ? "4ec9b0" : "267f99" }, + { token: "interface", foreground: darkMode ? "4ec9b0" : "267f99" }, + { token: "function", foreground: darkMode ? "dcdcaa" : "795e26" }, + { token: "method", foreground: darkMode ? "dcdcaa" : "795e26" }, + { token: "parameter", foreground: darkMode ? "9cdcfe" : "001080" }, + { token: "property", foreground: darkMode ? "9cdcfe" : "001080" }, + { token: "variable", foreground: darkMode ? "9cdcfe" : "001080" }, + ], + colors: { + "editor.background": darkMode ? "#1e1e1e" : "#fafafa", + "editor.inlayHint.background": darkMode ? "#333333" : "#eeeeee", + "editor.inlayHint.foreground": darkMode ? "#d4d4d4" : "#333333", + }, +}) + +registerPlaygroundLanguages() +const initialState = loadProjectState() +const initialFiles = { + ...Object.fromEntries(defaultFiles.map(file => [file.path, file.text])), + ...initialState.files, +} +const projectModels = new Map( + Object.entries(initialFiles).map(([fileName, text]) => { + const model = monaco.editor.createModel(text, languageForFile(fileName), monaco.Uri.parse(`file://${fileName}`)) + return [fileName, model] as const + }) +) +const fileButtons = new Map() +const inputEditor = monaco.editor.create(inputElement, { + automaticLayout: true, + fontFamily: "Hack, monospace", + fontLigatures: true, + fontSize: 14, + inlayHints: { enabled: "on" }, + minimap: { enabled: false }, + model: projectModels.get(initialState.activeFile ?? entryFileName) ?? projectModels.get(entryFileName), + padding: { top: 10 }, + scrollBeyondLastLine: false, + "semanticHighlighting.enabled": true, + tabSize: 2, + theme: "typescript-playground", +}) + +renderFileList() +updateActiveFile() +inputEditor.onDidChangeModel(updateActiveFile) +inputEditor.addAction({ + id: "run-project", + label: "Run Project", + keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter], + run: runProject, +}) +inputEditor.onMouseDown(event => { + if (!event.target.position || (!event.event.ctrlKey && !event.event.metaKey)) { + return + } + const position = event.target.position + window.setTimeout(() => { + inputEditor.setPosition(position) + void inputEditor.getAction("editor.action.revealDefinition")?.run() + }) +}) + +const inlayEmitter = new monaco.Emitter() +const typeQueries = new Map() +monaco.languages.registerInlayHintsProvider("typescript", { + onDidChangeInlayHints: inlayEmitter.event, + provideInlayHints(model) { + return { + hints: (typeQueries.get(model.uri.toString()) ?? []).map(query => ({ + kind: monaco.languages.InlayHintKind.Type, + position: new monaco.Position(query.lineNumber, query.column), + label: query.label, + paddingLeft: true, + })), + dispose() {}, + } + }, +}) + +let updateTimer = 0 +for (const model of projectModels.values()) { + registerProjectModel(model) +} +newFileButton.addEventListener("click", createNewFile) +resetProjectButton.addEventListener("click", resetProject) +runButton.addEventListener("click", runProject) +clearRunOutput.addEventListener("click", () => renderRunLogs([])) + +void initializeCompiler() + +async function initializeCompiler() { + try { + setLoadingProgress(0, "Downloading TypeScript...", "Preparing downloads") + const [wasmBytes, libFilesBytes, configSchemaBytes] = await Promise.all([ + downloadAsset("wasm", new URL("./tsc.wasm", import.meta.url)), + downloadAsset("libraries", new URL("./lib-files.json", import.meta.url)), + downloadAsset("schema", new URL("./tsconfig.schema.json", import.meta.url)), + ]) + + setLoadingIndeterminate("Compiling TypeScript...", "") + const module = await WebAssembly.compile(wasmBytes) + setLoadingProgress(78, "Starting compiler API...", "Instantiating WebAssembly") + const libFiles = JSON.parse(new TextDecoder().decode(libFilesBytes)) as Record + const configSchema = JSON.parse(new TextDecoder().decode(configSchemaBytes)) + registerConfigSchema(configSchema) + const instance = await instantiateWasm(module) + const transport = new WasmTransport({ instance, cwd: projectRoot }) + compilerTransport = transport + const api = new API({ transport }) + const libraries = Object.entries(libFiles) + for (const [index, [fileName, content]] of libraries.entries()) { + transport.setFile(fileName, content) + setLoadingProgress( + 82 + ((index + 1) / libraries.length) * 8, + "Mounting TypeScript libraries...", + `${index + 1} of ${libraries.length} files` + ) + } + window.ts = Object.assign(api, { + API, + DiagnosticCategory, + version: __TS_VERSION__, + }) + compilerReady = true + startLanguageServer(module, libFiles) + compileProject(api) + inputEditor.focus() + } catch (error) { + const message = error instanceof Error ? error.message : String(error) + compilerFailure = message + renderStatus() + console.error(error) + } +} + +function startLanguageServer(module: WebAssembly.Module, libraries: Record) { + try { + startTsgoLsp({ + editor: inputEditor, + libraries, + models: [...projectModels.values()], + module, + onError(message) { + lspFailure = message + renderStatus() + }, + onStatus(nextStatus, serverInfo) { + lspStatus = nextStatus + lspReady = nextStatus === "ready" + lspServerInfo = serverInfo ?? lspServerInfo + const progress = { + "mounting files": 92, + "starting tsc.wasm": 95, + "initializing LSP": 98, + ready: 100, + }[nextStatus] + setLoadingProgress( + progress, + nextStatus === "ready" ? "TypeScript is ready" : `Starting language server: ${nextStatus}`, + nextStatus === "ready" ? lspServerInfo ?? __TS_VERSION__ : "" + ) + renderStatus() + }, + }) + } catch (error) { + lspFailure = error instanceof Error ? error.message : String(error) + renderStatus() + } +} + +async function downloadAsset(name: keyof typeof __LOAD_ASSET_SIZES__, url: URL): Promise> { + url.searchParams.set("v", __ASSET_CACHE_VERSION__) + const request = new Request(url) + const cache = await getAssetCache() + let response = await cache?.match(request) + cachedAssets.set(name, response !== undefined) + let cacheWrite: Promise | undefined + if (!response) { + response = await fetch(request) + if (cache) cacheWrite = cache.put(request, response.clone()) + } + if (!response.ok) { + throw new Error(`Unable to load ${url.pathname}: ${response.status} ${response.statusText}`) + } + if (!response.body) { + const bytes = new Uint8Array(await response.arrayBuffer()) + await cacheWrite + downloadedAssets.set(name, bytes.length) + updateDownloadProgress() + return bytes + } + + const chunks: Uint8Array[] = [] + const reader = response.body.getReader() + let length = 0 + for (;;) { + const { done, value } = await reader.read() + if (done) break + chunks.push(value) + length += value.length + downloadedAssets.set(name, length) + updateDownloadProgress() + } + + const bytes = new Uint8Array(length) + let offset = 0 + for (const chunk of chunks) { + bytes.set(chunk, offset) + offset += chunk.length + } + await cacheWrite + return bytes +} + +function getAssetCache() { + assetCachePromise ??= openAssetCache() + return assetCachePromise +} + +async function openAssetCache() { + if (!("caches" in globalThis)) return undefined + const cacheName = `${assetCachePrefix}${__ASSET_CACHE_VERSION__}` + try { + const cacheNames = await caches.keys() + await Promise.all( + cacheNames + .filter(name => name.startsWith(assetCachePrefix) && name !== cacheName) + .map(name => caches.delete(name)) + ) + return caches.open(cacheName) + } catch (error) { + console.warn("Could not open the TypeScript asset cache", error) + return undefined + } +} + +function updateDownloadProgress() { + const totalBytes = Object.values(__LOAD_ASSET_SIZES__).reduce((total, value) => total + value, 0) + const downloadedBytes = [...downloadedAssets.values()].reduce((total, value) => total + value, 0) + const loadingFromNetwork = [...cachedAssets.values()].some(cached => !cached) + setLoadingProgress( + Math.min(70, (downloadedBytes / totalBytes) * 70), + loadingFromNetwork ? "Downloading TypeScript..." : "Loading cached TypeScript...", + `${formatBytes(downloadedBytes)} of ${formatBytes(totalBytes)}` + ) +} + +function setLoadingProgress(value: number, message: string, detail: string) { + loadingProgress.value = Math.max(loadingProgress.value, value) + loadingMessage.textContent = message + loadingDetail.textContent = detail +} + +function setLoadingIndeterminate(message: string, detail: string) { + loadingProgress.removeAttribute("value") + loadingMessage.textContent = message + loadingDetail.textContent = detail +} + +function formatBytes(bytes: number) { + if (bytes < 1024) return `${bytes} B` + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KiB` + return `${(bytes / (1024 * 1024)).toFixed(1)} MiB` +} + +function compileProject(api: API) { + setStatus("Checking project...", "loading") + runButton.disabled = true + + try { + const transport = compilerTransport + if (!transport) throw new Error("The compiler transport is not initialized") + for (const [fileName, model] of projectModels) { + transport.setFile(fileName, model.getValue()) + } + + const config = api.readConfigFile(configFileName) + const parsed = api.parseJsonConfigFileContent(config.config, { configFileName }) + const program = api.createProgram(parsed.fileNames, { + compilerOptions: parsed.options, + projectReferences: parsed.projectReferences, + configFileParsingDiagnostics: parsed.errors, + }) + + try { + const emit = program.emitToString() + const diagnostics = deduplicateDiagnostics([ + ...(config.error ? [config.error] : []), + ...parsed.errors, + ...program.getSyntacticDiagnostics(), + ...program.getSemanticDiagnostics(), + ...program.getConfigFileParsingDiagnostics(), + ...emit.diagnostics, + ]) + diagnosticCount = diagnostics.length + setDiagnostics(diagnostics) + + emittedFiles = new Map([...emit.outputFiles].map(([fileName, output]) => [fileName, output.text])) + runButton.disabled = ![...emittedFiles.keys()].some(fileName => fileName.endsWith(".js")) + void renderEmittedFiles() + + typeQueries.clear() + for (const fileName of parsed.fileNames) { + const model = projectModels.get(fileName) + const sourceFile = program.getSourceFile(fileName) + if (!model || !sourceFile) continue + typeQueries.set( + model.uri.toString(), + collectTypeQueries(model.getValue(), sourceFile, program.getProject().checker, model) + ) + } + inlayEmitter.fire() + projectFailure = undefined + compilerFailure = undefined + renderStatus() + } finally { + program.dispose() + } + } catch (error) { + const message = error instanceof Error ? error.message : String(error) + diagnosticCount = 0 + emittedFiles = new Map() + runButton.disabled = true + setDiagnostics([]) + typeQueries.clear() + inlayEmitter.fire() + renderEmitError(message) + projectFailure = message + renderStatus() + console.error(error) + } +} + +function deduplicateDiagnostics(diagnostics: readonly Diagnostic[]) { + const seen = new Set() + return diagnostics.filter(diagnostic => { + const key = [diagnostic.fileName, diagnostic.pos, diagnostic.end, diagnostic.code, diagnostic.text].join(":") + if (seen.has(key)) return false + seen.add(key) + return true + }) +} + +function collectTypeQueries( + source: string, + sourceFile: CompilerNode, + checker: ReturnType["getProject"]>["checker"], + model: monaco.editor.ITextModel +) { + const queryPattern = /^\s*\/\/\s*\^\?\s*$/gm + const queries: TypeQuery[] = [] + let match: RegExpExecArray | null + + while ((match = queryPattern.exec(source))) { + const queryEnd = match.index + match[0].lastIndexOf("?") + const queryPosition = model.getPositionAt(queryEnd) + if (queryPosition.lineNumber === 1) continue + + const inspectedPosition = model.getOffsetAt({ + lineNumber: queryPosition.lineNumber - 1, + column: queryPosition.column, + }) + const node = + findNodeAtPosition(sourceFile, inspectedPosition) ?? + findNodeAtPosition(sourceFile, Math.max(0, inspectedPosition - 1)) + if (!node) continue + + const type = checker.getTypeAtLocation(node as never) + const typeText = checker.typeToString(type, node as never).replace(/\r?\n\s*/g, " ") + queries.push({ + lineNumber: queryPosition.lineNumber, + column: queryPosition.column + 1, + label: truncate(`: ${typeText}`, 120), + }) + } + + return queries +} + +function findNodeAtPosition(node: CompilerNode, position: number): CompilerNode | undefined { + if (position < node.getFullStart() || position > node.getEnd()) return undefined + + let match: CompilerNode | undefined + node.forEachChild(child => { + const descendant = findNodeAtPosition(child, position) + if (descendant) { + match = descendant + return true + } + return undefined + }) + return match ?? node +} + +function setDiagnostics(diagnostics: readonly Diagnostic[]) { + for (const [fileName, model] of projectModels) { + const markers = diagnostics + .filter(diagnostic => diagnostic.fileName === fileName) + .map(diagnostic => { + const start = model.getPositionAt(Math.max(0, diagnostic.pos)) + const end = model.getPositionAt(Math.max(diagnostic.pos + 1, diagnostic.end)) + return { + code: `TS${diagnostic.code}`, + endColumn: end.column, + endLineNumber: end.lineNumber, + message: diagnostic.text, + severity: diagnosticSeverity(diagnostic.category), + source: diagnostic.source || "TS", + startColumn: start.column, + startLineNumber: start.lineNumber, + } + }) + monaco.editor.setModelMarkers(model, "typescript-7", markers) + } +} + +function diagnosticSeverity(category: number) { + switch (category) { + case DiagnosticCategory.Error: + return monaco.MarkerSeverity.Error + case DiagnosticCategory.Warning: + return monaco.MarkerSeverity.Warning + case DiagnosticCategory.Suggestion: + return monaco.MarkerSeverity.Hint + default: + return monaco.MarkerSeverity.Info + } +} + +async function renderEmittedFiles() { + const renderVersion = ++emitRenderVersion + emitOutput.replaceChildren() + const files = [...emittedFiles].sort(([left], [right]) => left.localeCompare(right)) + emitSummary.textContent = + files.length === 0 ? "No files emitted" : `${files.length} emitted file${files.length === 1 ? "" : "s"}` + + if (files.length === 0) { + emitOutput.appendChild(createText("p", "No files emitted.", "empty-message")) + return + } + + for (const [fileName, text] of files) { + const displayText = text.replace(/(?:\r?\n)+$/, "") + const section = document.createElement("section") + section.className = "emit-file" + section.appendChild(createText("h3", relativeProjectPath(fileName))) + const pre = document.createElement("pre") + pre.tabIndex = 0 + const code = document.createElement("code") + code.textContent = displayText + pre.appendChild(code) + section.appendChild(pre) + emitOutput.appendChild(section) + + const language = fileName.endsWith(".js") ? "javascript" : fileName.endsWith(".json") ? "json" : "typescript" + const highlighted = await monaco.editor.colorize(displayText, language, { tabSize: 2 }) + if (renderVersion !== emitRenderVersion) return + code.innerHTML = highlighted + } +} + +function renderEmitError(message: string) { + emitRenderVersion++ + emitSummary.textContent = "Compile failed" + emitOutput.replaceChildren(createText("p", message, "empty-message")) +} + +function runProject() { + const logs: RuntimeLog[] = [] + const javascriptFiles = new Map([...emittedFiles].filter(([fileName]) => fileName.endsWith(".js"))) + const entryFile = + [...javascriptFiles.keys()].find(fileName => fileName.endsWith("/index.js")) ?? javascriptFiles.keys().next().value + + if (!entryFile) { + renderRunLogs([{ level: "error", text: "No JavaScript entry file was emitted." }]) + return + } + if ([...javascriptFiles.values()].some(code => /^\s*(?:export|import)\b/m.test(code))) { + renderRunLogs([ + { + level: "error", + text: 'Run supports CommonJS output. Set compilerOptions.module to "CommonJS" in tsconfig.json.', + }, + ]) + return + } + + try { + executeCommonJs(entryFile, javascriptFiles, createRuntimeConsole(logs)) + } catch (error) { + logs.push({ + level: "error", + text: error instanceof Error ? error.stack ?? error.message : String(error), + }) + } + renderRunLogs(logs) + runLog.scrollIntoView({ block: "nearest" }) +} + +function executeCommonJs(entryFile: string, files: ReadonlyMap, runtimeConsole: Console) { + const cache = new Map() + + const load = (fileName: string): any => { + const cached = cache.get(fileName) + if (cached) return cached.exports + const code = files.get(fileName) + if (code === undefined) throw new Error(`Cannot find emitted module ${fileName}`) + + const module = { exports: {} as any } + cache.set(fileName, module) + const require = (specifier: string) => { + if (!specifier.startsWith(".")) { + throw new Error(`Run cannot load package import "${specifier}".`) + } + const resolved = new URL(specifier, `file://${fileName}`).pathname + const candidates = [resolved, `${resolved}.js`, `${resolved}/index.js`] + const target = candidates.find(candidate => files.has(candidate)) + if (!target) throw new Error(`Cannot resolve "${specifier}" from ${fileName}`) + return load(target) + } + const directory = fileName.slice(0, fileName.lastIndexOf("/")) || "/" + const evaluate = new Function("exports", "require", "module", "__filename", "__dirname", "console", code) + evaluate(module.exports, require, module, fileName, directory, runtimeConsole) + return module.exports + } + + load(entryFile) +} + +function createRuntimeConsole(logs: RuntimeLog[]) { + const runtimeConsole = Object.create(console) as Console + for (const level of ["debug", "error", "info", "log", "warn"] as const) { + runtimeConsole[level] = (...values: any[]) => { + logs.push({ level, text: values.map(formatRuntimeValue).join(" ") }) + renderRunLogs(logs) + console[level](...values) + } + } + runtimeConsole.clear = () => { + logs.splice(0) + renderRunLogs(logs) + } + return runtimeConsole +} + +function formatRuntimeValue(value: unknown): string { + if (typeof value === "string") return value + if (typeof value === "bigint") return `${value}n` + if (typeof value === "symbol") return String(value) + if (value instanceof Error) return value.stack ?? value.message + try { + const json = JSON.stringify(value, undefined, 2) + return json ?? String(value) + } catch { + return String(value) + } +} + +function renderRunLogs(logs: readonly RuntimeLog[]) { + runLog.replaceChildren() + if (logs.length === 0) { + runLog.appendChild(createText("p", "Run the project to see console output.", "empty-message")) + return + } + for (const log of logs) { + const row = document.createElement("div") + row.className = `run-log-entry ${log.level}` + row.appendChild(createText("strong", log.level.slice(0, 3).toUpperCase())) + row.appendChild(document.createTextNode(log.text)) + runLog.appendChild(row) + } +} + +function renderFileList() { + type Tree = { + directories: Map + files: string[] + } + + const root: Tree = { directories: new Map(), files: [] } + for (const fileName of [...projectModels.keys()].sort()) { + const parts = relativeProjectPath(fileName).split("/") + const basename = parts.pop()! + let tree = root + for (const part of parts) { + let child = tree.directories.get(part) + if (!child) { + child = { directories: new Map(), files: [] } + tree.directories.set(part, child) + } + tree = child + } + tree.files.push(basename) + } + + fileButtons.clear() + fileList.replaceChildren(renderTree(root, "")) + updateActiveFile() + + function renderTree(tree: Tree, parentPath: string): HTMLUListElement { + const list = document.createElement("ul") + list.className = "file-tree" + for (const [directory, child] of [...tree.directories].sort(([left], [right]) => left.localeCompare(right))) { + const item = document.createElement("li") + const details = document.createElement("details") + details.open = true + details.appendChild(createText("summary", directory, "file-tree-folder")) + details.appendChild(renderTree(child, `${parentPath}${directory}/`)) + item.appendChild(details) + list.appendChild(item) + } + for (const basename of tree.files.sort()) { + const relativePath = `${parentPath}${basename}` + const fileName = `${projectRoot}/${relativePath}` + const button = document.createElement("button") + button.type = "button" + button.dataset.kind = fileKind(fileName) + button.textContent = basename + button.addEventListener("click", () => { + inputEditor.setModel(projectModels.get(fileName)!) + inputEditor.focus() + }) + fileButtons.set(fileName, button) + const item = document.createElement("li") + item.appendChild(button) + list.appendChild(item) + } + return list + } +} + +function updateActiveFile() { + const model = inputEditor.getModel() + if (!model) return + currentFile.textContent = relativeProjectPath(model.uri.path) + const projectModel = projectModels.has(model.uri.path) + inputEditor.updateOptions({ readOnly: !projectModel }) + editorHint.textContent = + model.getLanguageId() === "typescript" + ? "Type query: align ^? below an expression" + : model.getLanguageId() === "json" + ? "Edit compiler options directly" + : "Read-only library file" + for (const [fileName, button] of fileButtons) { + if (fileName === model.uri.path) button.setAttribute("aria-current", "page") + else button.removeAttribute("aria-current") + } + if (projectModel) persistProjectState() +} + +function renderStatus() { + const failure = compilerFailure ?? lspFailure + if (failure) { + loader.hidden = false + loader.dataset.state = "error" + loadingMessage.textContent = failure + setStatus(failure, "error") + return + } + if (!compilerReady) { + loadingMessage.textContent = "Downloading TypeScript..." + setStatus("Loading compiler API...", "loading") + return + } + if (!lspReady) { + loadingMessage.textContent = `Starting language server: ${lspStatus}` + setStatus(`LSP: ${lspStatus}`, "loading") + return + } + + loader.hidden = true + loader.dataset.state = "ready" + if (projectFailure) { + setStatus(projectFailure, "error") + inputEditor.layout() + return + } + const compiler = lspServerInfo ?? __TS_VERSION__ + const diagnostics = `${diagnosticCount} diagnostic${diagnosticCount === 1 ? "" : "s"}` + setStatus(`${compiler} ready · ${diagnostics}`, "ready") + inputEditor.layout() +} + +function setStatus(message: string, state: "loading" | "ready" | "error") { + status.textContent = message + status.dataset.state = state +} + +function registerProjectModel(model: monaco.editor.ITextModel) { + model.onDidChangeContent(() => { + persistProjectState() + window.clearTimeout(updateTimer) + updateTimer = window.setTimeout(() => { + if (window.ts) compileProject(window.ts) + }, 220) + }) +} + +function createNewFile() { + const requested = prompt("New file path", "src/new-file.ts") + if (requested === null) return + const relativePath = requested.trim().replaceAll("\\", "/").replace(/^\/+/, "") + const parts = relativePath.split("/") + if (relativePath === "" || parts.some(part => part === "" || part === "." || part === "..")) { + alert("Enter a file path inside /workspace.") + return + } + + const fileName = `${projectRoot}/${relativePath}` + if (projectModels.has(fileName)) { + alert(`${relativePath} already exists.`) + return + } + + const model = monaco.editor.createModel("", languageForFile(fileName), monaco.Uri.parse(`file://${fileName}`)) + projectModels.set(fileName, model) + registerProjectModel(model) + renderFileList() + inputEditor.setModel(model) + inputEditor.focus() + persistProjectState() + if (window.ts) compileProject(window.ts) +} + +function resetProject() { + if (!confirm("Reset the project to the TypeScript 7 defaults?")) return + localStorage.removeItem(storageKey) + const url = new URL(location.href) + url.hash = "" + location.replace(url) +} + +function loadProjectState(): ProjectState { + if (location.hash.startsWith("#code/")) { + const encoded = location.hash.slice("#code/".length) + const decoded = + LZString.decompressFromEncodedURIComponent(encoded) ?? + LZString.decompressFromEncodedURIComponent(decodeURIComponent(encoded)) + if (decoded) { + try { + return normalizeProjectState(JSON.parse(decoded)) + } catch { + return { + activeFile: entryFileName, + files: { [entryFileName]: decoded }, + } + } + } + } + + const stored = localStorage.getItem(storageKey) + if (!stored) return { files: {} } + try { + return normalizeProjectState(JSON.parse(stored)) + } catch (error) { + console.warn("Could not restore the TypeScript 7 project", error) + return { files: {} } + } +} + +function normalizeProjectState(value: unknown): ProjectState { + if (!value || typeof value !== "object") return { files: {} } + const candidate = value as { activeFile?: unknown; files?: unknown } + const filesValue = candidate.files && typeof candidate.files === "object" ? candidate.files : value + const files = Object.fromEntries( + Object.entries(filesValue).filter( + (entry): entry is [string, string] => entry[0].startsWith(`${projectRoot}/`) && typeof entry[1] === "string" + ) + ) + const migrations = new Map([ + [`${projectRoot}/index.ts`, entryFileName], + [`${projectRoot}/greet.ts`, `${projectRoot}/src/greet.ts`], + ]) + const hasLegacyRootFiles = [...migrations.keys()].some(fileName => files[fileName] !== undefined) + for (const [oldPath, newPath] of migrations) { + if (files[oldPath] !== undefined && files[newPath] === undefined) { + files[newPath] = files[oldPath] + } + delete files[oldPath] + } + if (hasLegacyRootFiles && files[configFileName]) { + try { + const config = JSON.parse(files[configFileName]) + if (Array.isArray(config.include) && config.include.length === 1 && config.include[0] === "./*.ts") { + config.include = ["./src/**/*"] + } + config.compilerOptions ??= {} + config.compilerOptions.declaration ??= true + files[configFileName] = `${JSON.stringify(config, undefined, 2)}\n` + } catch { + files[configFileName] = files[configFileName].replace( + /"include"\s*:\s*\[\s*"\.\/\*\.ts"\s*\]/, + '"include": ["./src/**/*"]' + ) + } + } + const requestedActiveFile = + typeof candidate.activeFile === "string" ? migrations.get(candidate.activeFile) ?? candidate.activeFile : undefined + return { + activeFile: requestedActiveFile?.startsWith(`${projectRoot}/`) ? requestedActiveFile : undefined, + files, + } +} + +function persistProjectState() { + const activeModel = inputEditor.getModel() + const state: ProjectState = { + activeFile: activeModel && projectModels.has(activeModel.uri.path) ? activeModel.uri.path : entryFileName, + files: Object.fromEntries([...projectModels].map(([fileName, model]) => [fileName, model.getValue()])), + } + try { + const serialized = JSON.stringify(state) + localStorage.setItem(storageKey, serialized) + const url = new URL(location.href) + url.hash = `code/${LZString.compressToEncodedURIComponent(serialized)}` + history.replaceState({}, "", url) + } catch (error) { + console.warn("Could not save the TypeScript 7 project", error) + } +} + +function languageForFile(fileName: string): ProjectFile["language"] { + if (fileName.endsWith(".json")) return "json" + if (/\.[cm]?jsx?$/i.test(fileName)) return "javascript" + return "typescript" +} + +function fileKind(fileName: string) { + const language = languageForFile(fileName) + return language === "json" ? "{}" : language === "javascript" ? "JS" : "TS" +} + +function relativeProjectPath(fileName: string) { + return fileName.startsWith(`${projectRoot}/`) ? fileName.slice(projectRoot.length + 1) : fileName +} + +function truncate(value: string, maxLength: number) { + return value.length <= maxLength ? value : `${value.slice(0, maxLength - 1)}…` +} + +function createText(tagName: K, text: string, className?: string) { + const element = document.createElement(tagName) + element.textContent = text + if (className) element.className = className + return element +} + +function getElement(id: string) { + const element = document.getElementById(id) + if (!element) throw new Error(`Missing #${id}`) + return element as T +} diff --git a/packages/ts7-playground/src/styles.css b/packages/ts7-playground/src/styles.css new file mode 100644 index 000000000000..ed158b680251 --- /dev/null +++ b/packages/ts7-playground/src/styles.css @@ -0,0 +1,597 @@ +@import "@fontsource/nunito-sans/400.css"; +@import "@fontsource/nunito-sans/600.css"; +@import "hack-font/build/web/hack.css"; + +:root { + color: #1f1f1f; + background: #fafafa; + font-family: "Nunito Sans", sans-serif; + font-synthesis: none; + --border: #d6d6d6; + --hover: #eeeeee; + --muted: #5f6368; + --output: #f5f5f5; + --panel: #ffffff; + --selected: #e4e6f1; + --sidebar: #f3f3f3; + --toolbar: #3178c6; + --tree-guide: #b7b7b7; +} + +* { + box-sizing: border-box; +} + +html, +body, +.playground { + width: 100%; + height: 100%; + margin: 0; +} + +body { + overflow: hidden; +} + +.playground { + position: relative; + display: grid; + grid-template-rows: auto minmax(0, 1fr); +} + +.toolbar { + min-height: 4.5rem; + padding: 0.75rem 1rem; + color: white; + background: var(--toolbar); + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; +} + +.toolbar h1, +.toolbar p, +.panel-heading h2 { + margin: 0; +} + +.toolbar h1 { + font-size: 1.25rem; + font-weight: 600; +} + +.toolbar p { + margin-top: 0.125rem; + font-size: 0.875rem; + opacity: 0.85; +} + +.toolbar-actions { + display: flex; + align-items: center; + gap: 0.75rem; +} + +.toolbar button, +.run-output button { + border: 1px solid rgb(255 255 255 / 45%); + border-radius: 0.25rem; + color: inherit; + background: rgb(0 0 0 / 12%); + cursor: pointer; + font: inherit; + font-weight: 600; +} + +.toolbar button { + padding: 0.4rem 0.8rem; +} + +.toolbar button:hover:not(:disabled) { + background: rgb(0 0 0 / 22%); +} + +.toolbar button:disabled { + cursor: wait; + opacity: 0.5; +} + +.status { + padding: 0.35rem 0.65rem; + border: 1px solid rgb(255 255 255 / 35%); + border-radius: 0.25rem; + font-family: Hack, monospace; + font-size: 0.75rem; + white-space: nowrap; +} + +.status[data-state="ready"] { + background: rgb(0 0 0 / 12%); +} + +.status[data-state="error"] { + background: #a1260d; +} + +.workspace { + min-height: 0; + display: grid; + grid-template-columns: 13rem minmax(24rem, 1fr) minmax(20rem, 0.9fr); +} + +.file-explorer { + min-width: 0; + overflow: auto; + border-right: 1px solid var(--border); + background: var(--sidebar); +} + +.file-explorer-heading, +.panel-heading { + min-height: 2.75rem; + border-bottom: 1px solid var(--border); +} + +.file-explorer-heading { + padding: 0.5rem 0.75rem; + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.5rem; +} + +.file-explorer-heading h2, +.file-explorer-heading span { + margin: 0; +} + +.file-explorer-heading h2 { + font-size: 0.8rem; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; +} + +.file-explorer-heading span { + display: block; + margin-top: 0.1rem; + color: var(--muted); + font-family: Hack, monospace; + font-size: 0.65rem; +} + +.file-explorer-actions { + display: flex; + gap: 0.25rem; +} + +.file-explorer-actions button { + height: 1.7rem; + padding: 0 0.4rem; + border: 1px solid var(--border); + border-radius: 0.2rem; + color: inherit; + background: var(--panel); + cursor: pointer; + font: inherit; + font-size: 0.68rem; + line-height: 1; +} + +.file-explorer-actions button:hover { + background: var(--hover); +} + +.file-list { + padding: 0.4rem 0; +} + +.file-tree, +.file-tree ul { + margin: 0; + padding: 0; + list-style: none; +} + +.file-tree .file-tree { + margin-left: 1rem; + border-left: 1px solid var(--tree-guide); +} + +.file-tree .file-tree > li { + position: relative; +} + +.file-tree .file-tree > li::before { + position: absolute; + top: 1rem; + left: 0; + width: 0.65rem; + border-top: 1px solid var(--tree-guide); + content: ""; +} + +.file-tree-folder { + padding: 0.35rem 0.65rem 0.35rem 0.85rem; + color: inherit; + cursor: pointer; + font-size: 0.76rem; + font-weight: 600; + list-style: none; + user-select: none; +} + +.file-tree-folder::before { + display: inline-block; + width: 0.8rem; + margin-right: 0.35rem; + color: var(--muted); + content: "▾"; +} + +.file-tree details:not([open]) > .file-tree-folder::before { + content: "▸"; +} + +.file-list button { + width: 100%; + padding: 0.42rem 0.65rem 0.42rem 0.85rem; + overflow: hidden; + border: 0; + color: inherit; + background: transparent; + cursor: pointer; + font: inherit; + font-size: 0.8rem; + text-align: left; + text-overflow: ellipsis; + white-space: nowrap; +} + +.file-list button::before { + display: inline-block; + width: 1.65rem; + color: var(--muted); + font-family: Hack, monospace; + font-size: 0.65rem; + content: attr(data-kind); +} + +.file-list button:hover { + background: var(--hover); +} + +.file-list button[aria-current="page"] { + background: var(--selected); + font-weight: 600; +} + +.editor-panel { + min-width: 0; + min-height: 0; + display: grid; + grid-template-rows: auto minmax(0, 1fr); + background: var(--panel); +} + +.output-panel { + min-width: 0; + min-height: 0; + display: grid; + grid-template-rows: auto minmax(0, 1fr) auto; + border-left: 1px solid var(--border); + background: var(--panel); +} + +.panel-heading { + padding: 0.55rem 0.75rem; + display: flex; + align-items: baseline; + justify-content: space-between; + gap: 1rem; +} + +.panel-heading h2 { + font-size: 0.95rem; + font-weight: 600; +} + +.panel-heading span { + color: var(--muted); + font-size: 0.75rem; + text-align: right; +} + +code { + font-family: Hack, monospace; +} + +.editor { + min-width: 0; + min-height: 0; +} + +.emit-output { + min-height: 0; + padding: 0.75rem; + overflow: auto; + background: var(--output); +} + +.emit-file { + margin-bottom: 0.75rem; + overflow: hidden; + border: 1px solid var(--border); + border-radius: 0.25rem; + background: var(--panel); +} + +.emit-file:last-child { + margin-bottom: 0; +} + +.emit-file h3 { + position: sticky; + top: 0; + z-index: 1; + margin: 0; + padding: 0.45rem 0.65rem; + border-bottom: 1px solid var(--border); + background: var(--sidebar); + font-family: Hack, monospace; + font-size: 0.72rem; + font-weight: 600; +} + +.emit-file pre { + margin: 0; + padding: 0.75rem; + overflow: auto; + font-family: Hack, monospace; + font-size: 0.75rem; + line-height: 1.5; + tab-size: 2; +} + +.empty-message { + margin: 0; + color: var(--muted); + font-size: 0.8rem; +} + +.run-output { + max-height: 12rem; + overflow: auto; + border-top: 1px solid var(--border); + background: var(--panel); +} + +.run-output header { + position: sticky; + top: 0; + z-index: 1; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.4rem 0.65rem; + border-bottom: 1px solid var(--border); + background: var(--sidebar); +} + +.run-output h2 { + margin: 0; + font-size: 0.78rem; +} + +.run-output button { + padding: 0.2rem 0.45rem; + border-color: var(--border); + color: inherit; + background: var(--panel); + font-size: 0.7rem; +} + +.run-log { + padding: 0.65rem; + font-family: Hack, monospace; + font-size: 0.72rem; + line-height: 1.45; + white-space: pre-wrap; +} + +.run-log-entry + .run-log-entry { + margin-top: 0.35rem; + padding-top: 0.35rem; + border-top: 1px dashed var(--border); +} + +.run-log-entry strong { + display: inline-block; + width: 2.5rem; + color: var(--muted); +} + +.run-log-entry.warn strong { + color: #b26a00; +} + +.run-log-entry.error strong { + color: #c63131; +} + +.loading-overlay { + position: absolute; + top: 5.25rem; + right: 1rem; + z-index: 20; + pointer-events: none; +} + +.loading-card { + display: flex; + align-items: center; + gap: 0.85rem; + width: min(25rem, calc(100vw - 2rem)); + padding: 0.8rem 0.9rem; + border: 1px solid var(--border); + border-radius: 0.4rem; + background: color-mix(in srgb, var(--panel) 92%, transparent); + box-shadow: 0 0.45rem 1.5rem rgb(0 0 0 / 22%); + backdrop-filter: blur(8px); +} + +.loading-overlay[hidden] { + display: none; +} + +.loading-overlay p { + margin: 0 0 0.45rem; + font-size: 0.82rem; + font-weight: 600; +} + +.loading-copy { + min-width: 0; + flex: 1; +} + +.loading-overlay progress { + display: block; + width: 100%; + height: 0.65rem; + accent-color: #3178c6; +} + +.loading-overlay[data-state="error"] progress { + accent-color: #c63131; +} + +.loading-overlay small { + display: block; + min-height: 1rem; + margin-top: 0.35rem; + overflow: hidden; + color: var(--muted); + font-family: Hack, monospace; + font-size: 0.68rem; + text-overflow: ellipsis; + white-space: nowrap; +} + +.lds-grid { + position: relative; + width: 48px; + height: 48px; + flex: 0 0 48px; +} + +.lds-grid div { + position: absolute; + width: 10px; + height: 10px; + border-radius: 50%; + background: #3178c6; + animation: lds-grid 1.2s linear infinite; +} + +.loading-overlay[data-state="error"] .lds-grid div { + background: #c63131; + animation: none; +} + +.lds-grid div:nth-child(1) { + top: 3px; + left: 3px; + animation-delay: 0s; +} +.lds-grid div:nth-child(2) { + top: 3px; + left: 19px; + animation-delay: -0.4s; +} +.lds-grid div:nth-child(3) { + top: 3px; + left: 35px; + animation-delay: -0.8s; +} +.lds-grid div:nth-child(4) { + top: 19px; + left: 3px; + animation-delay: -0.4s; +} +.lds-grid div:nth-child(5) { + top: 19px; + left: 19px; + animation-delay: -0.8s; +} +.lds-grid div:nth-child(6) { + top: 19px; + left: 35px; + animation-delay: -1.2s; +} +.lds-grid div:nth-child(7) { + top: 35px; + left: 3px; + animation-delay: -0.8s; +} +.lds-grid div:nth-child(8) { + top: 35px; + left: 19px; + animation-delay: -1.2s; +} +.lds-grid div:nth-child(9) { + top: 35px; + left: 35px; + animation-delay: -1.6s; +} + +@keyframes lds-grid { + 0%, + 100% { + opacity: 1; + } + + 50% { + opacity: 0.5; + } +} + +@media (max-width: 1000px) { + .workspace { + grid-template-columns: 11rem minmax(20rem, 1fr); + grid-template-rows: minmax(20rem, 1fr) minmax(16rem, 0.8fr); + } + + .file-explorer { + grid-row: 1; + } + + .editor-panel { + grid-row: 1; + } + + .output-panel { + grid-column: 1 / -1; + grid-row: 2; + border-top: 1px solid var(--border); + border-left: 0; + } +} + +@media (prefers-color-scheme: dark) { + :root { + color: #f3f3f3; + background: #1e1e1e; + --border: #414141; + --hover: #292929; + --muted: #b7b7b7; + --output: #171717; + --panel: #1e1e1e; + --selected: #37373d; + --sidebar: #252526; + --toolbar: #235a97; + --tree-guide: #5c5c5c; + } +} diff --git a/packages/ts7-playground/src/tsgo-lsp.ts b/packages/ts7-playground/src/tsgo-lsp.ts new file mode 100644 index 000000000000..7319a5f4acde --- /dev/null +++ b/packages/ts7-playground/src/tsgo-lsp.ts @@ -0,0 +1,298 @@ +import { createTransportToWorker, MonacoLspClient } from "@vscode/monaco-lsp-client" +import * as monaco from "monaco-editor-core" +import { + conf as javascriptConfiguration, + language as javascriptLanguage, +} from "monaco-editor/languages/definitions/javascript/javascript.js" +import { + conf as typescriptConfiguration, + language as typescriptLanguage, +} from "monaco-editor/languages/definitions/typescript/typescript.js" + +const headerWords = 4 +const readPosition = 0 +const writePosition = 1 +const closed = 2 +const signal = 3 +const bufferSize = 4 * 1024 * 1024 + +export type TsgoStatus = "mounting files" | "starting tsc.wasm" | "initializing LSP" | "ready" + +type WorkerMessage = { + type: "lsp" | "drain" | "status" | "stderr" | "error" + message?: any + status?: TsgoStatus +} + +type LspRange = { + start: { line: number; character: number } + end: { line: number; character: number } +} + +type StartTsgoLspOptions = { + editor: monaco.editor.IStandaloneCodeEditor + libraries: Record + models: readonly monaco.editor.ITextModel[] + module: WebAssembly.Module + onError(message: string): void + onStatus(status: TsgoStatus, serverInfo?: string): void +} + +class RingBufferWorker { + readonly #worker = new Worker(new URL("./tsgo-lsp.worker.js", import.meta.url), { + type: "module", + }) + readonly #state: Int32Array + readonly #data: Uint8Array + readonly #queue: Uint8Array[] = [] + readonly #listeners = new Map() + readonly #pendingRequests = new Map() + #queueOffset = 0 + + onStatus?: (status: TsgoStatus) => void + onError?: (message: string) => void + onServerInfo?: (serverInfo: string) => void + + constructor(stdin: SharedArrayBuffer) { + this.#state = new Int32Array(stdin, 0, headerWords) + this.#data = new Uint8Array(stdin, headerWords * Int32Array.BYTES_PER_ELEMENT) + this.#worker.addEventListener("message", (event: MessageEvent) => { + if (event.data.type === "drain") { + this.#flush() + } else if (event.data.type === "status" && event.data.status) { + this.onStatus?.(event.data.status) + } else if (event.data.type === "error") { + this.onError?.(event.data.message) + } else if (event.data.type === "stderr") { + console.warn("[tsgo]", event.data.message) + } else if (event.data.type === "lsp") { + void this.#handleLspMessage(event.data.message) + } + }) + } + + start( + stdin: SharedArrayBuffer, + module: WebAssembly.Module, + libraries: Record, + files: Record + ) { + this.#worker.postMessage({ + type: "init", + stdin, + libraries, + module, + files, + }) + } + + postMessage(message: unknown) { + const lspMessage = message as { id?: string | number; method?: string } + if (lspMessage.id !== undefined && lspMessage.method) { + this.#pendingRequests.set(lspMessage.id, lspMessage.method) + } + + const body = new TextEncoder().encode(JSON.stringify(message)) + const header = new TextEncoder().encode(`Content-Length: ${body.length}\r\n\r\n`) + const framed = new Uint8Array(header.length + body.length) + framed.set(header) + framed.set(body, header.length) + if (framed.length > this.#data.length) { + throw new Error(`LSP message exceeds the ${this.#data.length}-byte stdin buffer`) + } + this.#queue.push(framed) + this.#flush() + } + + addEventListener(type: string, listener: EventListenerOrEventListenerObject) { + if (type !== "message") return + const callback: EventListener = typeof listener === "function" ? listener : event => listener.handleEvent(event) + this.#listeners.set(listener, callback) + } + + removeEventListener(type: string, listener: EventListenerOrEventListenerObject) { + if (type === "message") this.#listeners.delete(listener) + } + + #flush() { + while (this.#queue.length > 0) { + const readPos = Atomics.load(this.#state, readPosition) + const writePos = Atomics.load(this.#state, writePosition) + const free = this.#data.length - (writePos - readPos) + if (free <= 0) return + + const current = this.#queue[0] + const length = Math.min(free, current.length - this.#queueOffset) + const start = writePos % this.#data.length + const first = Math.min(length, this.#data.length - start) + this.#data.set(current.subarray(this.#queueOffset, this.#queueOffset + first), start) + if (first < length) { + this.#data.set(current.subarray(this.#queueOffset + first, this.#queueOffset + length), 0) + } + this.#queueOffset += length + Atomics.store(this.#state, writePosition, writePos + length) + Atomics.add(this.#state, signal, 1) + Atomics.notify(this.#state, signal) + + if (this.#queueOffset === current.length) { + this.#queue.shift() + this.#queueOffset = 0 + } + } + } + + async #handleLspMessage(message: any) { + const method = message?.method + const label = method ?? this.#pendingRequests.get(message?.id) + if (method === "textDocument/publishDiagnostics") return + if (label === "textDocument/definition") { + await ensureLibraryModels(message?.result) + navigateToDefinition(message?.result) + } + if (message?.id !== undefined && !method) { + this.#pendingRequests.delete(message.id) + } + if (message?.result?.capabilities) { + const info = message.result.serverInfo + if (info?.name) { + const name = info.name === "typescript-go" ? "TypeScript" : info.name + this.onServerInfo?.(`${name}${info.version ? ` ${info.version}` : ""}`) + } + this.onStatus?.("ready") + } + + const forwarded = new MessageEvent("message", { data: message }) + for (const listener of this.#listeners.values()) listener(forwarded) + } +} + +let languageRegistered = false +let activeEditor: monaco.editor.IStandaloneCodeEditor | undefined +let libraryFilesPromise: Promise> | undefined + +export function registerPlaygroundLanguages() { + if (languageRegistered) return + languageRegistered = true + monaco.languages.register({ id: "typescript", extensions: [".ts", ".tsx", ".mts", ".cts"] }) + monaco.languages.setLanguageConfiguration("typescript", typescriptConfiguration) + monaco.languages.setMonarchTokensProvider("typescript", typescriptLanguage) + monaco.languages.register({ id: "javascript", extensions: [".js", ".jsx", ".mjs", ".cjs"] }) + monaco.languages.setLanguageConfiguration("javascript", javascriptConfiguration) + monaco.languages.setMonarchTokensProvider("javascript", javascriptLanguage) + monaco.languages.register({ id: "json", extensions: [".json"] }) + monaco.languages.setLanguageConfiguration("json", { + brackets: [ + ["{", "}"], + ["[", "]"], + ], + comments: { lineComment: "//", blockComment: ["/*", "*/"] }, + }) + monaco.languages.setMonarchTokensProvider("json", { + tokenizer: { + root: [ + [/"(?:\\.|[^"\\])*"(?=\s*:)/, "string.key.json"], + [/"(?:\\.|[^"\\])*"/, "string.value.json"], + [/\b(?:true|false|null)\b/, "keyword.json"], + [/-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/, "number"], + [/[{}\[\],:]/, "delimiter"], + [/\/\/.*$/, "comment"], + ], + }, + }) +} + +export function startTsgoLsp(options: StartTsgoLspOptions) { + if (!crossOriginIsolated) { + throw new Error("The TypeScript language server requires cross-origin isolation; reload once to activate it.") + } + + activeEditor = options.editor + libraryFilesPromise = Promise.resolve(options.libraries) + const stdin = new SharedArrayBuffer(headerWords * Int32Array.BYTES_PER_ELEMENT + bufferSize) + const worker = new RingBufferWorker(stdin) + let serverInfo: string | undefined + worker.onStatus = status => options.onStatus(status, serverInfo) + worker.onServerInfo = info => { + serverInfo = info + } + worker.onError = options.onError + worker.start( + stdin, + options.module, + options.libraries, + Object.fromEntries(options.models.map(model => [model.uri.path, model.getValue()])) + ) + + const transport = createTransportToWorker(worker as unknown as Worker) + new MonacoLspClient(transport) +} + +async function ensureLibraryModels(result: unknown) { + const locations = Array.isArray(result) ? result : [result] + const uris = new Set() + for (const location of locations) { + if (!location || typeof location !== "object") continue + const candidate = location as { uri?: string; targetUri?: string } + const uri = candidate.targetUri ?? candidate.uri + if (uri && isLibraryUri(uri)) uris.add(uri) + } + + const libraryFiles = uris.size > 0 ? await getLibraryFiles() : {} + for (const uri of uris) { + const monacoUri = monaco.Uri.parse(uri) + if (monaco.editor.getModel(monacoUri)) continue + const filename = uri.slice(uri.lastIndexOf("/") + 1) + const contents = libraryFiles[`/${filename}`] ?? libraryFiles[filename] + if (contents === undefined) throw new Error(`Could not load ${filename}`) + monaco.editor.createModel(contents, "typescript", monacoUri) + } +} + +function isLibraryUri(uri: string) { + const parsed = monaco.Uri.parse(uri) + return ( + (parsed.scheme === "file" && /^\/typescript\/lib\/lib(?:\..*)?\.d\.ts$/i.test(parsed.path)) || + (parsed.scheme === "bundled" && /^\/libs\/lib(?:\..*)?\.d\.ts$/i.test(parsed.path)) + ) +} + +function getLibraryFiles() { + libraryFilesPromise ??= fetch(new URL("./lib-files.json", import.meta.url)).then(response => { + if (!response.ok) { + throw new Error(`Could not load TypeScript libraries: ${response.status} ${response.statusText}`) + } + return response.json() as Promise> + }) + return libraryFilesPromise +} + +function navigateToDefinition(result: unknown) { + const location = Array.isArray(result) ? result[0] : result + if (!activeEditor || !location || typeof location !== "object") return + + const target = location as { + uri?: string + targetUri?: string + range?: LspRange + targetRange?: LspRange + targetSelectionRange?: LspRange + } + const uri = target.targetUri ?? target.uri + const range = target.targetSelectionRange ?? target.targetRange ?? target.range + if (!uri || !range) return + const model = monaco.editor.getModel(monaco.Uri.parse(uri)) + if (!model || activeEditor.getModel() === model) return + + const monacoRange = new monaco.Range( + range.start.line + 1, + range.start.character + 1, + range.end.line + 1, + range.end.character + 1 + ) + activeEditor.setModel(model) + activeEditor.setSelection(monacoRange) + activeEditor.revealRangeInCenter(monacoRange, monaco.editor.ScrollType.Immediate) + activeEditor.focus() +} + +export { monaco } diff --git a/packages/ts7-playground/src/tsgo-lsp.worker.ts b/packages/ts7-playground/src/tsgo-lsp.worker.ts new file mode 100644 index 000000000000..572e56eb19fb --- /dev/null +++ b/packages/ts7-playground/src/tsgo-lsp.worker.ts @@ -0,0 +1,306 @@ +import { Directory, Fd, File, Inode, PreopenDirectory, WASI, wasi, WASIProcExit } from "@bjorn3/browser_wasi_shim" + +const headerWords = 4 +const readPosition = 0 +const writePosition = 1 +const closed = 2 +const signal = 3 +const subscriptionSize = 48 +const eventSize = 32 + +type InitMessage = { + type: "init" + stdin: SharedArrayBuffer + libraries: Record + module: WebAssembly.Module + files: Record +} + +class BlockingStdin extends Fd { + readonly #state: Int32Array + readonly #data: Uint8Array + + constructor(buffer: SharedArrayBuffer) { + super() + this.#state = new Int32Array(buffer, 0, headerWords) + this.#data = new Uint8Array(buffer, headerWords * Int32Array.BYTES_PER_ELEMENT) + } + + override fd_fdstat_get() { + const fdstat = new wasi.Fdstat(wasi.FILETYPE_CHARACTER_DEVICE, wasi.FDFLAGS_NONBLOCK) + fdstat.fs_rights_base = BigInt(wasi.RIGHTS_FD_READ | wasi.RIGHTS_POLL_FD_READWRITE) + return { ret: wasi.ERRNO_SUCCESS, fdstat } + } + + override fd_filestat_get() { + return { + ret: wasi.ERRNO_SUCCESS, + filestat: new wasi.Filestat(Inode.issue_ino(), wasi.FILETYPE_CHARACTER_DEVICE, 0n), + } + } + + override fd_fdstat_set_flags() { + return wasi.ERRNO_SUCCESS + } + + override fd_read(size: number) { + let readPos = Atomics.load(this.#state, readPosition) + let writePos = Atomics.load(this.#state, writePosition) + if (readPos === writePos && !Atomics.load(this.#state, closed)) { + const currentSignal = Atomics.load(this.#state, signal) + Atomics.wait(this.#state, signal, currentSignal, 50) + readPos = Atomics.load(this.#state, readPosition) + writePos = Atomics.load(this.#state, writePosition) + } + if (readPos !== writePos) { + const available = writePos - readPos + const length = Math.min(size, available) + const result = new Uint8Array(length) + const start = readPos % this.#data.length + const first = Math.min(length, this.#data.length - start) + result.set(this.#data.subarray(start, start + first)) + if (first < length) { + result.set(this.#data.subarray(0, length - first), first) + } + Atomics.store(this.#state, readPosition, readPos + length) + self.postMessage({ type: "drain" }) + return { ret: wasi.ERRNO_SUCCESS, data: result } + } + if (Atomics.load(this.#state, closed)) { + return { ret: wasi.ERRNO_SUCCESS, data: new Uint8Array() } + } + return { ret: wasi.ERRNO_AGAIN, data: new Uint8Array() } + } +} + +class LspStdout extends Fd { + #buffer = new Uint8Array() + + override fd_fdstat_get() { + const fdstat = new wasi.Fdstat(wasi.FILETYPE_CHARACTER_DEVICE, 0) + fdstat.fs_rights_base = BigInt(wasi.RIGHTS_FD_WRITE) + return { ret: wasi.ERRNO_SUCCESS, fdstat } + } + + override fd_filestat_get() { + return { + ret: wasi.ERRNO_SUCCESS, + filestat: new wasi.Filestat(Inode.issue_ino(), wasi.FILETYPE_CHARACTER_DEVICE, 0n), + } + } + + override fd_write(data: Uint8Array) { + const combined = new Uint8Array(this.#buffer.length + data.length) + combined.set(this.#buffer) + combined.set(data, this.#buffer.length) + this.#buffer = combined + this.#flushMessages() + return { ret: wasi.ERRNO_SUCCESS, nwritten: data.length } + } + + #flushMessages() { + for (;;) { + const headerEnd = findHeaderEnd(this.#buffer) + if (headerEnd < 0) return + const header = new TextDecoder().decode(this.#buffer.subarray(0, headerEnd)) + const match = /(?:^|\r\n)Content-Length:\s*(\d+)/i.exec(header) + if (!match) { + self.postMessage({ type: "error", message: `Invalid LSP header: ${header}` }) + this.#buffer = new Uint8Array() + return + } + const contentLength = Number(match[1]) + const bodyStart = headerEnd + 4 + const bodyEnd = bodyStart + contentLength + if (this.#buffer.length < bodyEnd) return + const body = new TextDecoder().decode(this.#buffer.subarray(bodyStart, bodyEnd)) + try { + self.postMessage({ type: "lsp", message: JSON.parse(body) }) + } catch (error) { + self.postMessage({ + type: "error", + message: `Invalid LSP JSON: ${String(error)}`, + }) + } + this.#buffer = this.#buffer.slice(bodyEnd) + } + } +} + +class Stderr extends Fd { + readonly #decoder = new TextDecoder() + + override fd_write(data: Uint8Array) { + const message = this.#decoder.decode(data, { stream: true }).trim() + if (message) self.postMessage({ type: "stderr", message }) + return { ret: wasi.ERRNO_SUCCESS, nwritten: data.length } + } +} + +function findHeaderEnd(data: Uint8Array) { + for (let i = 0; i <= data.length - 4; i++) { + if (data[i] === 13 && data[i + 1] === 10 && data[i + 2] === 13 && data[i + 3] === 10) { + return i + } + } + return -1 +} + +function installPollOneoff(wasiRuntime: WASI, state: Int32Array) { + wasiRuntime.wasiImport.poll_oneoff = ( + inputPointer: number, + outputPointer: number, + subscriptionCount: number, + eventCountPointer: number + ) => { + const memory = new DataView(wasiRuntime.inst.exports.memory.buffer) + const subscriptions = Array.from({ length: subscriptionCount }, (_, index) => + wasi.Subscription.read_bytes(memory, inputPointer + index * subscriptionSize) + ) + const clockDeadlines = new Map() + for (const subscription of subscriptions) { + if (subscription.eventtype !== wasi.EVENTTYPE_CLOCK) continue + const clockNow = + subscription.clockid === wasi.CLOCKID_REALTIME + ? BigInt(Date.now()) * 1_000_000n + : BigInt(Math.round(performance.now() * 1e6)) + clockDeadlines.set( + subscription, + (subscription.flags & wasi.SUBCLOCKFLAGS_SUBSCRIPTION_CLOCK_ABSTIME) !== 0 + ? subscription.timeout + : clockNow + subscription.timeout + ) + } + + for (;;) { + const now = { + [wasi.CLOCKID_MONOTONIC]: BigInt(Math.round(performance.now() * 1e6)), + [wasi.CLOCKID_REALTIME]: BigInt(Date.now()) * 1_000_000n, + } + const ready = subscriptions.filter(subscription => { + if (subscription.eventtype === wasi.EVENTTYPE_FD_READ) { + return ( + Atomics.load(state, readPosition) !== Atomics.load(state, writePosition) || + Atomics.load(state, closed) !== 0 + ) + } + if (subscription.eventtype === wasi.EVENTTYPE_FD_WRITE) return true + if (subscription.eventtype !== wasi.EVENTTYPE_CLOCK) return false + const clockNow = now[subscription.clockid as keyof typeof now] ?? 0n + return (clockDeadlines.get(subscription) ?? 0n) <= clockNow + }) + + if (ready.length > 0) { + ready.forEach((subscription, index) => { + const eventPointer = outputPointer + index * eventSize + new wasi.Event(subscription.userdata, wasi.ERRNO_SUCCESS, subscription.eventtype).write_bytes( + memory, + eventPointer + ) + memory.setBigUint64(eventPointer + 16, 0n, true) + memory.setUint16(eventPointer + 24, 0, true) + }) + memory.setUint32(eventCountPointer, ready.length, true) + return wasi.ERRNO_SUCCESS + } + + const currentSignal = Atomics.load(state, signal) + const nextDeadline = Math.min( + 50, + ...subscriptions + .filter(subscription => subscription.eventtype === wasi.EVENTTYPE_CLOCK) + .map(subscription => { + const clockNow = now[subscription.clockid as keyof typeof now] ?? 0n + const remaining = (clockDeadlines.get(subscription) ?? clockNow) - clockNow + return Math.max(1, Math.ceil(Number(remaining) / 1e6)) + }) + ) + Atomics.wait(state, signal, currentSignal, nextDeadline) + } + } +} + +type Tree = Map + +function createFileSystem(files: Record) { + const root: Tree = new Map([ + ["workspace", new Map()], + ["tmp", new Map()], + ["typescript", new Map([["lib", new Map()]])], + ]) + for (const [filename, contents] of Object.entries(files)) { + const parts = filename.replace(/^\/+/, "").split("/") + const basename = parts.pop()! + let current = root + for (const part of parts) { + let child = current.get(part) + if (!(child instanceof Map)) { + child = new Map() + current.set(part, child) + } + current = child + } + current.set(basename, contents) + } + + function build(tree: Tree): Directory { + const contents = new Map() + for (const [name, value] of tree) { + contents.set(name, typeof value === "string" ? new File(new TextEncoder().encode(value)) : build(value)) + } + return new Directory(contents) + } + + return new PreopenDirectory("/", build(root).contents) +} + +async function start(message: InitMessage) { + self.postMessage({ type: "status", status: "mounting files" }) + const files = { ...message.files } + for (const [name, contents] of Object.entries(message.libraries)) { + const filename = name.slice(name.lastIndexOf("/") + 1) + files[`/typescript/lib/${filename}`] = contents + } + + const fds = [new BlockingStdin(message.stdin), new LspStdout(), new Stderr(), createFileSystem(files)] + const wasiRuntime = new WASI(["tsc", "--lsp", "--stdio"], ["HOME=/workspace", "TMPDIR=/tmp"], fds, { debug: false }) + installPollOneoff(wasiRuntime, new Int32Array(message.stdin, 0, headerWords)) + const instance = await WebAssembly.instantiate(message.module, { + wasi_snapshot_preview1: wasiRuntime.wasiImport, + }) + self.postMessage({ type: "status", status: "starting tsc.wasm" }) + self.postMessage({ type: "status", status: "initializing LSP" }) + try { + wasiRuntime.start( + instance as unknown as { + exports: { + memory: WebAssembly.Memory + _start(): unknown + } + } + ) + } catch (error) { + if (error instanceof WASIProcExit) { + self.postMessage({ + type: "error", + message: `tsc exited with status ${error.code}`, + }) + } else { + throw error + } + } +} + +self.addEventListener( + "message", + (event: MessageEvent) => { + if (event.data.type !== "init") return + start(event.data).catch(error => { + self.postMessage({ + type: "error", + message: error instanceof Error ? error.stack ?? error.message : String(error), + }) + }) + }, + { once: true } +) diff --git a/packages/ts7-playground/tsconfig.json b/packages/ts7-playground/tsconfig.json new file mode 100644 index 000000000000..cfd3ec83e336 --- /dev/null +++ b/packages/ts7-playground/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["DOM", "ES2022"], + "strict": true, + "noEmit": true, + "skipLibCheck": true + }, + "include": ["src"] +} diff --git a/packages/ts7-playground/vendor/lib/lib.d.ts b/packages/ts7-playground/vendor/lib/lib.d.ts new file mode 100644 index 000000000000..a80f0070486d --- /dev/null +++ b/packages/ts7-playground/vendor/lib/lib.d.ts @@ -0,0 +1,20 @@ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABILITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ + + +/// +/// +/// +/// diff --git a/packages/ts7-playground/vendor/lib/lib.decorators.d.ts b/packages/ts7-playground/vendor/lib/lib.decorators.d.ts new file mode 100644 index 000000000000..83d82bef2e55 --- /dev/null +++ b/packages/ts7-playground/vendor/lib/lib.decorators.d.ts @@ -0,0 +1,382 @@ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABILITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ + + +/** + * The decorator context types provided to class element decorators. + */ +type ClassMemberDecoratorContext = + | ClassMethodDecoratorContext + | ClassGetterDecoratorContext + | ClassSetterDecoratorContext + | ClassFieldDecoratorContext + | ClassAccessorDecoratorContext; + +/** + * The decorator context types provided to any decorator. + */ +type DecoratorContext = + | ClassDecoratorContext + | ClassMemberDecoratorContext; + +type DecoratorMetadataObject = Record & object; + +type DecoratorMetadata = typeof globalThis extends { Symbol: { readonly metadata: symbol; }; } ? DecoratorMetadataObject : DecoratorMetadataObject | undefined; + +/** + * Context provided to a class decorator. + * @template Class The type of the decorated class associated with this context. + */ +interface ClassDecoratorContext< + Class extends abstract new (...args: any) => any = abstract new (...args: any) => any, +> { + /** The kind of element that was decorated. */ + readonly kind: "class"; + + /** The name of the decorated class. */ + readonly name: string | undefined; + + /** + * Adds a callback to be invoked after the class definition has been finalized. + * + * @example + * ```ts + * function customElement(name: string): ClassDecoratorFunction { + * return (target, context) => { + * context.addInitializer(function () { + * customElements.define(name, this); + * }); + * } + * } + * + * @customElement("my-element") + * class MyElement {} + * ``` + */ + addInitializer(initializer: (this: Class) => void): void; + + readonly metadata: DecoratorMetadata; +} + +/** + * Context provided to a class method decorator. + * @template This The type on which the class element will be defined. For a static class element, this will be + * the type of the constructor. For a non-static class element, this will be the type of the instance. + * @template Value The type of the decorated class method. + */ +interface ClassMethodDecoratorContext< + This = unknown, + Value extends (this: This, ...args: any) => any = (this: This, ...args: any) => any, +> { + /** The kind of class element that was decorated. */ + readonly kind: "method"; + + /** The name of the decorated class element. */ + readonly name: string | symbol; + + /** A value indicating whether the class element is a static (`true`) or instance (`false`) element. */ + readonly static: boolean; + + /** A value indicating whether the class element has a private name. */ + readonly private: boolean; + + /** An object that can be used to access the current value of the class element at runtime. */ + readonly access: { + /** + * Determines whether an object has a property with the same name as the decorated element. + */ + has(object: This): boolean; + /** + * Gets the current value of the method from the provided object. + * + * @example + * let fn = context.access.get(instance); + */ + get(object: This): Value; + }; + + /** + * Adds a callback to be invoked either after static methods are defined but before + * static initializers are run (when decorating a `static` element), or before instance + * initializers are run (when decorating a non-`static` element). + * + * @example + * ```ts + * const bound: ClassMethodDecoratorFunction = (value, context) { + * if (context.private) throw new TypeError("Not supported on private methods."); + * context.addInitializer(function () { + * this[context.name] = this[context.name].bind(this); + * }); + * } + * + * class C { + * message = "Hello"; + * + * @bound + * m() { + * console.log(this.message); + * } + * } + * ``` + */ + addInitializer(initializer: (this: This) => void): void; + + readonly metadata: DecoratorMetadata; +} + +/** + * Context provided to a class getter decorator. + * @template This The type on which the class element will be defined. For a static class element, this will be + * the type of the constructor. For a non-static class element, this will be the type of the instance. + * @template Value The property type of the decorated class getter. + */ +interface ClassGetterDecoratorContext< + This = unknown, + Value = unknown, +> { + /** The kind of class element that was decorated. */ + readonly kind: "getter"; + + /** The name of the decorated class element. */ + readonly name: string | symbol; + + /** A value indicating whether the class element is a static (`true`) or instance (`false`) element. */ + readonly static: boolean; + + /** A value indicating whether the class element has a private name. */ + readonly private: boolean; + + /** An object that can be used to access the current value of the class element at runtime. */ + readonly access: { + /** + * Determines whether an object has a property with the same name as the decorated element. + */ + has(object: This): boolean; + /** + * Invokes the getter on the provided object. + * + * @example + * let value = context.access.get(instance); + */ + get(object: This): Value; + }; + + /** + * Adds a callback to be invoked either after static methods are defined but before + * static initializers are run (when decorating a `static` element), or before instance + * initializers are run (when decorating a non-`static` element). + */ + addInitializer(initializer: (this: This) => void): void; + + readonly metadata: DecoratorMetadata; +} + +/** + * Context provided to a class setter decorator. + * @template This The type on which the class element will be defined. For a static class element, this will be + * the type of the constructor. For a non-static class element, this will be the type of the instance. + * @template Value The type of the decorated class setter. + */ +interface ClassSetterDecoratorContext< + This = unknown, + Value = unknown, +> { + /** The kind of class element that was decorated. */ + readonly kind: "setter"; + + /** The name of the decorated class element. */ + readonly name: string | symbol; + + /** A value indicating whether the class element is a static (`true`) or instance (`false`) element. */ + readonly static: boolean; + + /** A value indicating whether the class element has a private name. */ + readonly private: boolean; + + /** An object that can be used to access the current value of the class element at runtime. */ + readonly access: { + /** + * Determines whether an object has a property with the same name as the decorated element. + */ + has(object: This): boolean; + /** + * Invokes the setter on the provided object. + * + * @example + * context.access.set(instance, value); + */ + set(object: This, value: Value): void; + }; + + /** + * Adds a callback to be invoked either after static methods are defined but before + * static initializers are run (when decorating a `static` element), or before instance + * initializers are run (when decorating a non-`static` element). + */ + addInitializer(initializer: (this: This) => void): void; + + readonly metadata: DecoratorMetadata; +} + +/** + * Context provided to a class `accessor` field decorator. + * @template This The type on which the class element will be defined. For a static class element, this will be + * the type of the constructor. For a non-static class element, this will be the type of the instance. + * @template Value The type of decorated class field. + */ +interface ClassAccessorDecoratorContext< + This = unknown, + Value = unknown, +> { + /** The kind of class element that was decorated. */ + readonly kind: "accessor"; + + /** The name of the decorated class element. */ + readonly name: string | symbol; + + /** A value indicating whether the class element is a static (`true`) or instance (`false`) element. */ + readonly static: boolean; + + /** A value indicating whether the class element has a private name. */ + readonly private: boolean; + + /** An object that can be used to access the current value of the class element at runtime. */ + readonly access: { + /** + * Determines whether an object has a property with the same name as the decorated element. + */ + has(object: This): boolean; + + /** + * Invokes the getter on the provided object. + * + * @example + * let value = context.access.get(instance); + */ + get(object: This): Value; + + /** + * Invokes the setter on the provided object. + * + * @example + * context.access.set(instance, value); + */ + set(object: This, value: Value): void; + }; + + /** + * Adds a callback to be invoked immediately after the auto `accessor` being + * decorated is initialized (regardless if the `accessor` is `static` or not). + */ + addInitializer(initializer: (this: This) => void): void; + + readonly metadata: DecoratorMetadata; +} + +/** + * Describes the target provided to class `accessor` field decorators. + * @template This The `this` type to which the target applies. + * @template Value The property type for the class `accessor` field. + */ +interface ClassAccessorDecoratorTarget { + /** + * Invokes the getter that was defined prior to decorator application. + * + * @example + * let value = target.get.call(instance); + */ + get(this: This): Value; + + /** + * Invokes the setter that was defined prior to decorator application. + * + * @example + * target.set.call(instance, value); + */ + set(this: This, value: Value): void; +} + +/** + * Describes the allowed return value from a class `accessor` field decorator. + * @template This The `this` type to which the target applies. + * @template Value The property type for the class `accessor` field. + */ +interface ClassAccessorDecoratorResult { + /** + * An optional replacement getter function. If not provided, the existing getter function is used instead. + */ + get?(this: This): Value; + + /** + * An optional replacement setter function. If not provided, the existing setter function is used instead. + */ + set?(this: This, value: Value): void; + + /** + * An optional initializer mutator that is invoked when the underlying field initializer is evaluated. + * @param value The incoming initializer value. + * @returns The replacement initializer value. + */ + init?(this: This, value: Value): Value; +} + +/** + * Context provided to a class field decorator. + * @template This The type on which the class element will be defined. For a static class element, this will be + * the type of the constructor. For a non-static class element, this will be the type of the instance. + * @template Value The type of the decorated class field. + */ +interface ClassFieldDecoratorContext< + This = unknown, + Value = unknown, +> { + /** The kind of class element that was decorated. */ + readonly kind: "field"; + + /** The name of the decorated class element. */ + readonly name: string | symbol; + + /** A value indicating whether the class element is a static (`true`) or instance (`false`) element. */ + readonly static: boolean; + + /** A value indicating whether the class element has a private name. */ + readonly private: boolean; + + /** An object that can be used to access the current value of the class element at runtime. */ + readonly access: { + /** + * Determines whether an object has a property with the same name as the decorated element. + */ + has(object: This): boolean; + + /** + * Gets the value of the field on the provided object. + */ + get(object: This): Value; + + /** + * Sets the value of the field on the provided object. + */ + set(object: This, value: Value): void; + }; + + /** + * Adds a callback to be invoked immediately after the field being decorated + * is initialized (regardless if the field is `static` or not). + */ + addInitializer(initializer: (this: This) => void): void; + + readonly metadata: DecoratorMetadata; +} diff --git a/packages/ts7-playground/vendor/lib/lib.decorators.legacy.d.ts b/packages/ts7-playground/vendor/lib/lib.decorators.legacy.d.ts new file mode 100644 index 000000000000..89775167cf7e --- /dev/null +++ b/packages/ts7-playground/vendor/lib/lib.decorators.legacy.d.ts @@ -0,0 +1,20 @@ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABILITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ + + +declare type ClassDecorator = (target: TFunction) => TFunction | void; +declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void; +declare type MethodDecorator = (target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor) => TypedPropertyDescriptor | void; +declare type ParameterDecorator = (target: Object, propertyKey: string | symbol | undefined, parameterIndex: number) => void; diff --git a/packages/ts7-playground/vendor/lib/lib.dom.asynciterable.d.ts b/packages/ts7-playground/vendor/lib/lib.dom.asynciterable.d.ts new file mode 100644 index 000000000000..075563ce0340 --- /dev/null +++ b/packages/ts7-playground/vendor/lib/lib.dom.asynciterable.d.ts @@ -0,0 +1,18 @@ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABILITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ + + +// This file's contents are now included in the main types file. +// The file has been left for backward compatibility. diff --git a/packages/ts7-playground/vendor/lib/lib.dom.d.ts b/packages/ts7-playground/vendor/lib/lib.dom.d.ts new file mode 100644 index 000000000000..9e127c3f5a52 --- /dev/null +++ b/packages/ts7-playground/vendor/lib/lib.dom.d.ts @@ -0,0 +1,45125 @@ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABILITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ + + +/// +/// + +///////////////////////////// +/// Window APIs +///////////////////////////// + +interface AacEncoderConfig { + format?: AacBitstreamFormat; +} + +interface AddEventListenerOptions extends EventListenerOptions { + once?: boolean; + passive?: boolean; + signal?: AbortSignal; +} + +interface AddressErrors { + addressLine?: string; + city?: string; + country?: string; + dependentLocality?: string; + organization?: string; + phone?: string; + postalCode?: string; + recipient?: string; + region?: string; + sortingCode?: string; +} + +interface AesCbcParams extends Algorithm { + iv: BufferSource; +} + +interface AesCtrParams extends Algorithm { + counter: BufferSource; + length: number; +} + +interface AesDerivedKeyParams extends Algorithm { + length: number; +} + +interface AesGcmParams extends Algorithm { + additionalData?: BufferSource; + iv: BufferSource; + tagLength?: number; +} + +interface AesKeyAlgorithm extends KeyAlgorithm { + length: number; +} + +interface AesKeyGenParams extends Algorithm { + length: number; +} + +interface Algorithm { + name: string; +} + +interface AllAcceptedCredentialsOptions { + allAcceptedCredentialIds: Base64URLString[]; + rpId: string; + userId: Base64URLString; +} + +interface AnalyserOptions extends AudioNodeOptions { + fftSize?: number; + maxDecibels?: number; + minDecibels?: number; + smoothingTimeConstant?: number; +} + +interface AnimationEventInit extends EventInit { + animationName?: string; + elapsedTime?: number; + pseudoElement?: string; +} + +interface AnimationPlaybackEventInit extends EventInit { + currentTime?: CSSNumberish | null; + timelineTime?: CSSNumberish | null; +} + +interface AssignedNodesOptions { + flatten?: boolean; +} + +interface AudioBufferOptions { + length: number; + numberOfChannels?: number; + sampleRate: number; +} + +interface AudioBufferSourceOptions { + buffer?: AudioBuffer | null; + detune?: number; + loop?: boolean; + loopEnd?: number; + loopStart?: number; + playbackRate?: number; +} + +interface AudioConfiguration { + bitrate?: number; + channels?: string; + contentType: string; + samplerate?: number; + spatialRendering?: boolean; +} + +interface AudioContextOptions { + latencyHint?: AudioContextLatencyCategory | number; + sampleRate?: number; +} + +interface AudioDataCopyToOptions { + format?: AudioSampleFormat; + frameCount?: number; + frameOffset?: number; + planeIndex: number; +} + +interface AudioDataInit { + data: BufferSource; + format: AudioSampleFormat; + numberOfChannels: number; + numberOfFrames: number; + sampleRate: number; + timestamp: number; + transfer?: ArrayBuffer[]; +} + +interface AudioDecoderConfig { + codec: string; + description?: AllowSharedBufferSource; + numberOfChannels: number; + sampleRate: number; +} + +interface AudioDecoderInit { + error: WebCodecsErrorCallback; + output: AudioDataOutputCallback; +} + +interface AudioDecoderSupport { + config?: AudioDecoderConfig; + supported?: boolean; +} + +interface AudioEncoderConfig { + aac?: AacEncoderConfig; + bitrate?: number; + bitrateMode?: BitrateMode; + codec: string; + numberOfChannels: number; + opus?: OpusEncoderConfig; + sampleRate: number; +} + +interface AudioEncoderInit { + error: WebCodecsErrorCallback; + output: EncodedAudioChunkOutputCallback; +} + +interface AudioEncoderSupport { + config?: AudioEncoderConfig; + supported?: boolean; +} + +interface AudioNodeOptions { + channelCount?: number; + channelCountMode?: ChannelCountMode; + channelInterpretation?: ChannelInterpretation; +} + +interface AudioProcessingEventInit extends EventInit { + inputBuffer: AudioBuffer; + outputBuffer: AudioBuffer; + playbackTime: number; +} + +interface AudioTimestamp { + contextTime?: number; + performanceTime?: DOMHighResTimeStamp; +} + +interface AudioWorkletNodeOptions extends AudioNodeOptions { + numberOfInputs?: number; + numberOfOutputs?: number; + outputChannelCount?: number[]; + parameterData?: Record; + processorOptions?: any; +} + +interface AuthenticationExtensionsClientInputs { + appid?: string; + credProps?: boolean; + credentialProtectionPolicy?: string; + enforceCredentialProtectionPolicy?: boolean; + hmacCreateSecret?: boolean; + largeBlob?: AuthenticationExtensionsLargeBlobInputs; + minPinLength?: boolean; + prf?: AuthenticationExtensionsPRFInputs; +} + +interface AuthenticationExtensionsClientInputsJSON { + appid?: string; + credProps?: boolean; + largeBlob?: AuthenticationExtensionsLargeBlobInputsJSON; + prf?: AuthenticationExtensionsPRFInputsJSON; +} + +interface AuthenticationExtensionsClientOutputs { + appid?: boolean; + credProps?: CredentialPropertiesOutput; + hmacCreateSecret?: boolean; + largeBlob?: AuthenticationExtensionsLargeBlobOutputs; + prf?: AuthenticationExtensionsPRFOutputs; +} + +interface AuthenticationExtensionsClientOutputsJSON { + appid?: boolean; + credProps?: CredentialPropertiesOutput; + largeBlob?: AuthenticationExtensionsLargeBlobOutputsJSON; + prf?: AuthenticationExtensionsPRFOutputsJSON; +} + +interface AuthenticationExtensionsLargeBlobInputs { + read?: boolean; + support?: string; + write?: BufferSource; +} + +interface AuthenticationExtensionsLargeBlobInputsJSON { + read?: boolean; + support?: string; + write?: Base64URLString; +} + +interface AuthenticationExtensionsLargeBlobOutputs { + blob?: ArrayBuffer; + supported?: boolean; + written?: boolean; +} + +interface AuthenticationExtensionsLargeBlobOutputsJSON { + blob?: Base64URLString; + supported?: boolean; + written?: boolean; +} + +interface AuthenticationExtensionsPRFInputs { + eval?: AuthenticationExtensionsPRFValues; + evalByCredential?: Record; +} + +interface AuthenticationExtensionsPRFInputsJSON { + eval?: AuthenticationExtensionsPRFValuesJSON; + evalByCredential?: Record; +} + +interface AuthenticationExtensionsPRFOutputs { + enabled?: boolean; + results?: AuthenticationExtensionsPRFValues; +} + +interface AuthenticationExtensionsPRFOutputsJSON { + enabled?: boolean; + results?: AuthenticationExtensionsPRFValuesJSON; +} + +interface AuthenticationExtensionsPRFValues { + first: BufferSource; + second?: BufferSource; +} + +interface AuthenticationExtensionsPRFValuesJSON { + first: Base64URLString; + second?: Base64URLString; +} + +interface AuthenticationResponseJSON { + authenticatorAttachment?: string; + clientExtensionResults: AuthenticationExtensionsClientOutputsJSON; + id: string; + rawId: Base64URLString; + response: AuthenticatorAssertionResponseJSON; + type: string; +} + +interface AuthenticatorAssertionResponseJSON { + authenticatorData: Base64URLString; + clientDataJSON: Base64URLString; + signature: Base64URLString; + userHandle?: Base64URLString; +} + +interface AuthenticatorAttestationResponseJSON { + attestationObject: Base64URLString; + authenticatorData: Base64URLString; + clientDataJSON: Base64URLString; + publicKey?: Base64URLString; + publicKeyAlgorithm: COSEAlgorithmIdentifier; + transports: string[]; +} + +interface AuthenticatorSelectionCriteria { + authenticatorAttachment?: AuthenticatorAttachment; + requireResidentKey?: boolean; + residentKey?: ResidentKeyRequirement; + userVerification?: UserVerificationRequirement; +} + +interface AvcEncoderConfig { + format?: AvcBitstreamFormat; +} + +interface BiquadFilterOptions extends AudioNodeOptions { + Q?: number; + detune?: number; + frequency?: number; + gain?: number; + type?: BiquadFilterType; +} + +interface BlobEventInit extends EventInit { + data: Blob; + timecode?: DOMHighResTimeStamp; +} + +interface BlobPropertyBag { + endings?: EndingType; + type?: string; +} + +interface CSSMatrixComponentOptions { + is2D?: boolean; +} + +interface CSSNumericType { + angle?: number; + flex?: number; + frequency?: number; + length?: number; + percent?: number; + percentHint?: CSSNumericBaseType; + resolution?: number; + time?: number; +} + +interface CSSStyleSheetInit { + baseURL?: string; + disabled?: boolean; + media?: MediaList | string; +} + +interface CacheQueryOptions { + ignoreMethod?: boolean; + ignoreSearch?: boolean; + ignoreVary?: boolean; +} + +interface CanvasRenderingContext2DSettings { + alpha?: boolean; + colorSpace?: PredefinedColorSpace; + desynchronized?: boolean; + willReadFrequently?: boolean; +} + +interface CaretPositionFromPointOptions { + shadowRoots?: ShadowRoot[]; +} + +interface ChannelMergerOptions extends AudioNodeOptions { + numberOfInputs?: number; +} + +interface ChannelSplitterOptions extends AudioNodeOptions { + numberOfOutputs?: number; +} + +interface CheckVisibilityOptions { + checkOpacity?: boolean; + checkVisibilityCSS?: boolean; + contentVisibilityAuto?: boolean; + opacityProperty?: boolean; + visibilityProperty?: boolean; +} + +interface ClientQueryOptions { + includeUncontrolled?: boolean; + type?: ClientTypes; +} + +interface ClipboardEventInit extends EventInit { + clipboardData?: DataTransfer | null; +} + +interface ClipboardItemOptions { + presentationStyle?: PresentationStyle; +} + +interface CloseEventInit extends EventInit { + code?: number; + reason?: string; + wasClean?: boolean; +} + +interface CommandEventInit extends EventInit { + command?: string; + source?: Element | null; +} + +interface CompositionEventInit extends UIEventInit { + data?: string; +} + +interface ComputedEffectTiming extends EffectTiming { + activeDuration?: CSSNumberish; + currentIteration?: number | null; + endTime?: CSSNumberish; + localTime?: CSSNumberish | null; + progress?: number | null; + startTime?: CSSNumberish; +} + +interface ComputedKeyframe { + composite: CompositeOperationOrAuto; + computedOffset: number; + easing: string; + offset: number | null; + [property: string]: string | number | null | undefined; +} + +interface ConstantSourceOptions { + offset?: number; +} + +interface ConstrainBooleanOrDOMStringParameters { + exact?: boolean | string; + ideal?: boolean | string; +} + +interface ConstrainBooleanParameters { + exact?: boolean; + ideal?: boolean; +} + +interface ConstrainDOMStringParameters { + exact?: string | string[]; + ideal?: string | string[]; +} + +interface ConstrainDoubleRange extends DoubleRange { + exact?: number; + ideal?: number; +} + +interface ConstrainULongRange extends ULongRange { + exact?: number; + ideal?: number; +} + +interface ContentVisibilityAutoStateChangeEventInit extends EventInit { + skipped?: boolean; +} + +interface ConvolverOptions extends AudioNodeOptions { + buffer?: AudioBuffer | null; + disableNormalization?: boolean; +} + +interface CookieChangeEventInit extends EventInit { + changed?: CookieList; + deleted?: CookieList; +} + +interface CookieInit { + domain?: string | null; + expires?: DOMHighResTimeStamp | null; + name: string; + partitioned?: boolean; + path?: string; + sameSite?: CookieSameSite; + value: string; +} + +interface CookieListItem { + name?: string; + value?: string; +} + +interface CookieStoreDeleteOptions { + domain?: string | null; + name: string; + partitioned?: boolean; + path?: string; +} + +interface CookieStoreGetOptions { + name?: string; + url?: string; +} + +interface CredentialCreationOptions { + publicKey?: PublicKeyCredentialCreationOptions; + signal?: AbortSignal; +} + +interface CredentialPropertiesOutput { + rk?: boolean; +} + +interface CredentialRequestOptions { + mediation?: CredentialMediationRequirement; + publicKey?: PublicKeyCredentialRequestOptions; + signal?: AbortSignal; +} + +interface CryptoKeyPair { + privateKey: CryptoKey; + publicKey: CryptoKey; +} + +interface CurrentUserDetailsOptions { + displayName: string; + name: string; + rpId: string; + userId: Base64URLString; +} + +interface CustomEventInit extends EventInit { + detail?: T; +} + +interface DOMMatrix2DInit { + a?: number; + b?: number; + c?: number; + d?: number; + e?: number; + f?: number; + m11?: number; + m12?: number; + m21?: number; + m22?: number; + m41?: number; + m42?: number; +} + +interface DOMMatrixInit extends DOMMatrix2DInit { + is2D?: boolean; + m13?: number; + m14?: number; + m23?: number; + m24?: number; + m31?: number; + m32?: number; + m33?: number; + m34?: number; + m43?: number; + m44?: number; +} + +interface DOMPointInit { + w?: number; + x?: number; + y?: number; + z?: number; +} + +interface DOMQuadInit { + p1?: DOMPointInit; + p2?: DOMPointInit; + p3?: DOMPointInit; + p4?: DOMPointInit; +} + +interface DOMRectInit { + height?: number; + width?: number; + x?: number; + y?: number; +} + +interface DelayOptions extends AudioNodeOptions { + delayTime?: number; + maxDelayTime?: number; +} + +interface DeviceMotionEventAccelerationInit { + x?: number | null; + y?: number | null; + z?: number | null; +} + +interface DeviceMotionEventInit extends EventInit { + acceleration?: DeviceMotionEventAccelerationInit; + accelerationIncludingGravity?: DeviceMotionEventAccelerationInit; + interval?: number; + rotationRate?: DeviceMotionEventRotationRateInit; +} + +interface DeviceMotionEventRotationRateInit { + alpha?: number | null; + beta?: number | null; + gamma?: number | null; +} + +interface DeviceOrientationEventInit extends EventInit { + absolute?: boolean; + alpha?: number | null; + beta?: number | null; + gamma?: number | null; +} + +interface DisplayMediaStreamOptions { + audio?: boolean | MediaTrackConstraints; + video?: boolean | MediaTrackConstraints; +} + +interface DocumentTimelineOptions { + originTime?: DOMHighResTimeStamp; +} + +interface DoubleRange { + max?: number; + min?: number; +} + +interface DragEventInit extends MouseEventInit { + dataTransfer?: DataTransfer | null; +} + +interface DynamicsCompressorOptions extends AudioNodeOptions { + attack?: number; + knee?: number; + ratio?: number; + release?: number; + threshold?: number; +} + +interface EcKeyAlgorithm extends KeyAlgorithm { + namedCurve: NamedCurve; +} + +interface EcKeyGenParams extends Algorithm { + namedCurve: NamedCurve; +} + +interface EcKeyImportParams extends Algorithm { + namedCurve: NamedCurve; +} + +interface EcdhKeyDeriveParams extends Algorithm { + public: CryptoKey; +} + +interface EcdsaParams extends Algorithm { + hash: HashAlgorithmIdentifier; +} + +interface EffectTiming { + delay?: number; + direction?: PlaybackDirection; + duration?: number | CSSNumericValue | string; + easing?: string; + endDelay?: number; + fill?: FillMode; + iterationStart?: number; + iterations?: number; + playbackRate?: number; +} + +interface ElementCreationOptions { + customElementRegistry?: CustomElementRegistry | null; + is?: string; +} + +interface ElementDefinitionOptions { + extends?: string; +} + +interface EncodedAudioChunkInit { + data: AllowSharedBufferSource; + duration?: number; + timestamp: number; + transfer?: ArrayBuffer[]; + type: EncodedAudioChunkType; +} + +interface EncodedAudioChunkMetadata { + decoderConfig?: AudioDecoderConfig; +} + +interface EncodedVideoChunkInit { + data: AllowSharedBufferSource; + duration?: number; + timestamp: number; + type: EncodedVideoChunkType; +} + +interface EncodedVideoChunkMetadata { + decoderConfig?: VideoDecoderConfig; + svc?: SvcOutputMetadata; +} + +interface ErrorEventInit extends EventInit { + colno?: number; + error?: any; + filename?: string; + lineno?: number; + message?: string; +} + +interface EventInit { + bubbles?: boolean; + cancelable?: boolean; + composed?: boolean; +} + +interface EventListenerOptions { + capture?: boolean; +} + +interface EventModifierInit extends UIEventInit { + altKey?: boolean; + ctrlKey?: boolean; + metaKey?: boolean; + modifierAltGraph?: boolean; + modifierCapsLock?: boolean; + modifierFn?: boolean; + modifierFnLock?: boolean; + modifierHyper?: boolean; + modifierNumLock?: boolean; + modifierScrollLock?: boolean; + modifierSuper?: boolean; + modifierSymbol?: boolean; + modifierSymbolLock?: boolean; + shiftKey?: boolean; +} + +interface EventSourceInit { + withCredentials?: boolean; +} + +interface FilePropertyBag extends BlobPropertyBag { + lastModified?: number; +} + +interface FileSystemCreateWritableOptions { + keepExistingData?: boolean; +} + +interface FileSystemFlags { + create?: boolean; + exclusive?: boolean; +} + +interface FileSystemGetDirectoryOptions { + create?: boolean; +} + +interface FileSystemGetFileOptions { + create?: boolean; +} + +interface FileSystemRemoveOptions { + recursive?: boolean; +} + +interface FocusEventInit extends UIEventInit { + relatedTarget?: EventTarget | null; +} + +interface FocusOptions { + focusVisible?: boolean; + preventScroll?: boolean; +} + +interface FontFaceDescriptors { + ascentOverride?: string; + descentOverride?: string; + display?: FontDisplay; + featureSettings?: string; + lineGapOverride?: string; + stretch?: string; + style?: string; + unicodeRange?: string; + variationSettings?: string; + weight?: string; +} + +interface FontFaceSetLoadEventInit extends EventInit { + fontfaces?: FontFace[]; +} + +interface FormDataEventInit extends EventInit { + formData: FormData; +} + +interface FullscreenOptions { + navigationUI?: FullscreenNavigationUI; +} + +interface GPUBindGroupDescriptor extends GPUObjectDescriptorBase { + entries: GPUBindGroupEntry[]; + layout: GPUBindGroupLayout; +} + +interface GPUBindGroupEntry { + binding: GPUIndex32; + resource: GPUBindingResource; +} + +interface GPUBindGroupLayoutDescriptor extends GPUObjectDescriptorBase { + entries: GPUBindGroupLayoutEntry[]; +} + +interface GPUBindGroupLayoutEntry { + binding: GPUIndex32; + buffer?: GPUBufferBindingLayout; + externalTexture?: GPUExternalTextureBindingLayout; + sampler?: GPUSamplerBindingLayout; + storageTexture?: GPUStorageTextureBindingLayout; + texture?: GPUTextureBindingLayout; + visibility: GPUShaderStageFlags; +} + +interface GPUBlendComponent { + dstFactor?: GPUBlendFactor; + operation?: GPUBlendOperation; + srcFactor?: GPUBlendFactor; +} + +interface GPUBlendState { + alpha: GPUBlendComponent; + color: GPUBlendComponent; +} + +interface GPUBufferBinding { + buffer: GPUBuffer; + offset?: GPUSize64; + size?: GPUSize64; +} + +interface GPUBufferBindingLayout { + hasDynamicOffset?: boolean; + minBindingSize?: GPUSize64; + type?: GPUBufferBindingType; +} + +interface GPUBufferDescriptor extends GPUObjectDescriptorBase { + mappedAtCreation?: boolean; + size: GPUSize64; + usage: GPUBufferUsageFlags; +} + +interface GPUCanvasConfiguration { + alphaMode?: GPUCanvasAlphaMode; + colorSpace?: PredefinedColorSpace; + device: GPUDevice; + format: GPUTextureFormat; + toneMapping?: GPUCanvasToneMapping; + usage?: GPUTextureUsageFlags; + viewFormats?: GPUTextureFormat[]; +} + +interface GPUCanvasToneMapping { + mode?: GPUCanvasToneMappingMode; +} + +interface GPUColorDict { + a: number; + b: number; + g: number; + r: number; +} + +interface GPUColorTargetState { + blend?: GPUBlendState; + format: GPUTextureFormat; + writeMask?: GPUColorWriteFlags; +} + +interface GPUCommandBufferDescriptor extends GPUObjectDescriptorBase { +} + +interface GPUCommandEncoderDescriptor extends GPUObjectDescriptorBase { +} + +interface GPUComputePassDescriptor extends GPUObjectDescriptorBase { + timestampWrites?: GPUComputePassTimestampWrites; +} + +interface GPUComputePassTimestampWrites { + beginningOfPassWriteIndex?: GPUSize32; + endOfPassWriteIndex?: GPUSize32; + querySet: GPUQuerySet; +} + +interface GPUComputePipelineDescriptor extends GPUPipelineDescriptorBase { + compute: GPUProgrammableStage; +} + +interface GPUCopyExternalImageDestInfo extends GPUTexelCopyTextureInfo { + colorSpace?: PredefinedColorSpace; + premultipliedAlpha?: boolean; +} + +interface GPUCopyExternalImageSourceInfo { + flipY?: boolean; + origin?: GPUOrigin2D; + source: GPUCopyExternalImageSource; +} + +interface GPUDepthStencilState { + depthBias?: GPUDepthBias; + depthBiasClamp?: number; + depthBiasSlopeScale?: number; + depthCompare?: GPUCompareFunction; + depthWriteEnabled?: boolean; + format: GPUTextureFormat; + stencilBack?: GPUStencilFaceState; + stencilFront?: GPUStencilFaceState; + stencilReadMask?: GPUStencilValue; + stencilWriteMask?: GPUStencilValue; +} + +interface GPUDeviceDescriptor extends GPUObjectDescriptorBase { + defaultQueue?: GPUQueueDescriptor; + requiredFeatures?: GPUFeatureName[]; + requiredLimits?: Record; +} + +interface GPUExtent3DDict { + depthOrArrayLayers?: GPUIntegerCoordinate; + height?: GPUIntegerCoordinate; + width: GPUIntegerCoordinate; +} + +interface GPUExternalTextureBindingLayout { +} + +interface GPUExternalTextureDescriptor extends GPUObjectDescriptorBase { + colorSpace?: PredefinedColorSpace; + source: HTMLVideoElement | VideoFrame; +} + +interface GPUFragmentState extends GPUProgrammableStage { + targets: (GPUColorTargetState | null)[]; +} + +interface GPUMultisampleState { + alphaToCoverageEnabled?: boolean; + count?: GPUSize32; + mask?: GPUSampleMask; +} + +interface GPUObjectDescriptorBase { + label?: string; +} + +interface GPUOrigin2DDict { + x?: GPUIntegerCoordinate; + y?: GPUIntegerCoordinate; +} + +interface GPUOrigin3DDict { + x?: GPUIntegerCoordinate; + y?: GPUIntegerCoordinate; + z?: GPUIntegerCoordinate; +} + +interface GPUPipelineDescriptorBase extends GPUObjectDescriptorBase { + layout: GPUPipelineLayout | GPUAutoLayoutMode; +} + +interface GPUPipelineErrorInit { + reason: GPUPipelineErrorReason; +} + +interface GPUPipelineLayoutDescriptor extends GPUObjectDescriptorBase { + bindGroupLayouts: (GPUBindGroupLayout | null)[]; +} + +interface GPUPrimitiveState { + cullMode?: GPUCullMode; + frontFace?: GPUFrontFace; + stripIndexFormat?: GPUIndexFormat; + topology?: GPUPrimitiveTopology; + unclippedDepth?: boolean; +} + +interface GPUProgrammableStage { + constants?: Record; + entryPoint?: string; + module: GPUShaderModule; +} + +interface GPUQuerySetDescriptor extends GPUObjectDescriptorBase { + count: GPUSize32; + type: GPUQueryType; +} + +interface GPUQueueDescriptor extends GPUObjectDescriptorBase { +} + +interface GPURenderBundleDescriptor extends GPUObjectDescriptorBase { +} + +interface GPURenderBundleEncoderDescriptor extends GPURenderPassLayout { + depthReadOnly?: boolean; + stencilReadOnly?: boolean; +} + +interface GPURenderPassColorAttachment { + clearValue?: GPUColor; + depthSlice?: GPUIntegerCoordinate; + loadOp: GPULoadOp; + resolveTarget?: GPUTexture | GPUTextureView; + storeOp: GPUStoreOp; + view: GPUTexture | GPUTextureView; +} + +interface GPURenderPassDepthStencilAttachment { + depthClearValue?: number; + depthLoadOp?: GPULoadOp; + depthReadOnly?: boolean; + depthStoreOp?: GPUStoreOp; + stencilClearValue?: GPUStencilValue; + stencilLoadOp?: GPULoadOp; + stencilReadOnly?: boolean; + stencilStoreOp?: GPUStoreOp; + view: GPUTexture | GPUTextureView; +} + +interface GPURenderPassDescriptor extends GPUObjectDescriptorBase { + colorAttachments: (GPURenderPassColorAttachment | null)[]; + depthStencilAttachment?: GPURenderPassDepthStencilAttachment; + maxDrawCount?: GPUSize64; + occlusionQuerySet?: GPUQuerySet; + timestampWrites?: GPURenderPassTimestampWrites; +} + +interface GPURenderPassLayout extends GPUObjectDescriptorBase { + colorFormats: (GPUTextureFormat | null)[]; + depthStencilFormat?: GPUTextureFormat; + sampleCount?: GPUSize32; +} + +interface GPURenderPassTimestampWrites { + beginningOfPassWriteIndex?: GPUSize32; + endOfPassWriteIndex?: GPUSize32; + querySet: GPUQuerySet; +} + +interface GPURenderPipelineDescriptor extends GPUPipelineDescriptorBase { + depthStencil?: GPUDepthStencilState; + fragment?: GPUFragmentState; + multisample?: GPUMultisampleState; + primitive?: GPUPrimitiveState; + vertex: GPUVertexState; +} + +interface GPURequestAdapterOptions { + forceFallbackAdapter?: boolean; + powerPreference?: GPUPowerPreference; +} + +interface GPUSamplerBindingLayout { + type?: GPUSamplerBindingType; +} + +interface GPUSamplerDescriptor extends GPUObjectDescriptorBase { + addressModeU?: GPUAddressMode; + addressModeV?: GPUAddressMode; + addressModeW?: GPUAddressMode; + compare?: GPUCompareFunction; + lodMaxClamp?: number; + lodMinClamp?: number; + magFilter?: GPUFilterMode; + maxAnisotropy?: number; + minFilter?: GPUFilterMode; + mipmapFilter?: GPUMipmapFilterMode; +} + +interface GPUShaderModuleDescriptor extends GPUObjectDescriptorBase { + code: string; +} + +interface GPUStencilFaceState { + compare?: GPUCompareFunction; + depthFailOp?: GPUStencilOperation; + failOp?: GPUStencilOperation; + passOp?: GPUStencilOperation; +} + +interface GPUStorageTextureBindingLayout { + access?: GPUStorageTextureAccess; + format: GPUTextureFormat; + viewDimension?: GPUTextureViewDimension; +} + +interface GPUTexelCopyBufferInfo extends GPUTexelCopyBufferLayout { + buffer: GPUBuffer; +} + +interface GPUTexelCopyBufferLayout { + bytesPerRow?: GPUSize32; + offset?: GPUSize64; + rowsPerImage?: GPUSize32; +} + +interface GPUTexelCopyTextureInfo { + aspect?: GPUTextureAspect; + mipLevel?: GPUIntegerCoordinate; + origin?: GPUOrigin3D; + texture: GPUTexture; +} + +interface GPUTextureBindingLayout { + multisampled?: boolean; + sampleType?: GPUTextureSampleType; + viewDimension?: GPUTextureViewDimension; +} + +interface GPUTextureDescriptor extends GPUObjectDescriptorBase { + dimension?: GPUTextureDimension; + format: GPUTextureFormat; + mipLevelCount?: GPUIntegerCoordinate; + sampleCount?: GPUSize32; + size: GPUExtent3D; + usage: GPUTextureUsageFlags; + viewFormats?: GPUTextureFormat[]; +} + +interface GPUTextureViewDescriptor extends GPUObjectDescriptorBase { + arrayLayerCount?: GPUIntegerCoordinate; + aspect?: GPUTextureAspect; + baseArrayLayer?: GPUIntegerCoordinate; + baseMipLevel?: GPUIntegerCoordinate; + dimension?: GPUTextureViewDimension; + format?: GPUTextureFormat; + mipLevelCount?: GPUIntegerCoordinate; + usage?: GPUTextureUsageFlags; +} + +interface GPUUncapturedErrorEventInit extends EventInit { + error: GPUError; +} + +interface GPUVertexAttribute { + format: GPUVertexFormat; + offset: GPUSize64; + shaderLocation: GPUIndex32; +} + +interface GPUVertexBufferLayout { + arrayStride: GPUSize64; + attributes: GPUVertexAttribute[]; + stepMode?: GPUVertexStepMode; +} + +interface GPUVertexState extends GPUProgrammableStage { + buffers?: (GPUVertexBufferLayout | null)[]; +} + +interface GainOptions extends AudioNodeOptions { + gain?: number; +} + +interface GamepadEffectParameters { + duration?: number; + leftTrigger?: number; + rightTrigger?: number; + startDelay?: number; + strongMagnitude?: number; + weakMagnitude?: number; +} + +interface GamepadEventInit extends EventInit { + gamepad?: Gamepad | null; +} + +interface GetAnimationsOptions { + subtree?: boolean; +} + +interface GetComposedRangesOptions { + shadowRoots?: ShadowRoot[]; +} + +interface GetHTMLOptions { + serializableShadowRoots?: boolean; + shadowRoots?: ShadowRoot[]; +} + +interface GetNotificationOptions { + tag?: string; +} + +interface GetRootNodeOptions { + composed?: boolean; +} + +interface HashChangeEventInit extends EventInit { + newURL?: string; + oldURL?: string; +} + +interface HkdfParams extends Algorithm { + hash: HashAlgorithmIdentifier; + info: BufferSource; + salt: BufferSource; +} + +interface HmacImportParams extends Algorithm { + hash: HashAlgorithmIdentifier; + length?: number; +} + +interface HmacKeyAlgorithm extends KeyAlgorithm { + hash: KeyAlgorithm; + length: number; +} + +interface HmacKeyGenParams extends Algorithm { + hash: HashAlgorithmIdentifier; + length?: number; +} + +interface IDBDatabaseInfo { + name?: string; + version?: number; +} + +interface IDBIndexParameters { + multiEntry?: boolean; + unique?: boolean; +} + +interface IDBObjectStoreParameters { + autoIncrement?: boolean; + keyPath?: string | string[] | null; +} + +interface IDBTransactionOptions { + durability?: IDBTransactionDurability; +} + +interface IDBVersionChangeEventInit extends EventInit { + newVersion?: number | null; + oldVersion?: number; +} + +interface IIRFilterOptions extends AudioNodeOptions { + feedback: number[]; + feedforward: number[]; +} + +interface IdleRequestOptions { + timeout?: number; +} + +interface ImageBitmapOptions { + colorSpaceConversion?: ColorSpaceConversion; + imageOrientation?: ImageOrientation; + premultiplyAlpha?: PremultiplyAlpha; + resizeHeight?: number; + resizeQuality?: ResizeQuality; + resizeWidth?: number; +} + +interface ImageBitmapRenderingContextSettings { + alpha?: boolean; +} + +interface ImageDataSettings { + colorSpace?: PredefinedColorSpace; + pixelFormat?: ImageDataPixelFormat; +} + +interface ImageDecodeOptions { + completeFramesOnly?: boolean; + frameIndex?: number; +} + +interface ImageDecodeResult { + complete: boolean; + image: VideoFrame; +} + +interface ImageDecoderInit { + colorSpaceConversion?: ColorSpaceConversion; + data: ImageBufferSource; + desiredHeight?: number; + desiredWidth?: number; + preferAnimation?: boolean; + transfer?: ArrayBuffer[]; + type: string; +} + +interface ImageEncodeOptions { + quality?: number; + type?: string; +} + +interface ImportNodeOptions { + customElementRegistry?: CustomElementRegistry; + selfOnly?: boolean; +} + +interface InputEventInit extends UIEventInit { + data?: string | null; + dataTransfer?: DataTransfer | null; + inputType?: string; + isComposing?: boolean; + targetRanges?: StaticRange[]; +} + +interface IntersectionObserverInit { + root?: Element | Document | null; + rootMargin?: string; + scrollMargin?: string; + threshold?: number | number[]; +} + +interface JsonWebKey { + alg?: string; + crv?: string; + d?: string; + dp?: string; + dq?: string; + e?: string; + ext?: boolean; + k?: string; + key_ops?: string[]; + kty?: string; + n?: string; + oth?: RsaOtherPrimesInfo[]; + p?: string; + q?: string; + qi?: string; + use?: string; + x?: string; + y?: string; +} + +interface KeyAlgorithm { + name: string; +} + +interface KeySystemTrackConfiguration { + robustness?: string; +} + +interface KeyboardEventInit extends EventModifierInit { + /** @deprecated `charCode` is inconsistent across environments, consider using `key` instead. */ + charCode?: number; + code?: string; + isComposing?: boolean; + key?: string; + /** @deprecated `keyCode` is inconsistent across environments, consider using `key` instead. */ + keyCode?: number; + location?: number; + repeat?: boolean; +} + +interface Keyframe { + composite?: CompositeOperationOrAuto; + easing?: string; + offset?: number | null; + [property: string]: string | number | null | undefined; +} + +interface KeyframeAnimationOptions extends KeyframeEffectOptions { + id?: string; + rangeEnd?: TimelineRangeOffset | CSSNumericValue | CSSKeywordValue | string; + rangeStart?: TimelineRangeOffset | CSSNumericValue | CSSKeywordValue | string; + timeline?: AnimationTimeline | null; +} + +interface KeyframeEffectOptions extends EffectTiming { + composite?: CompositeOperation; + iterationComposite?: IterationCompositeOperation; + pseudoElement?: string | null; +} + +interface LockInfo { + clientId?: string; + mode?: LockMode; + name?: string; +} + +interface LockManagerSnapshot { + held?: LockInfo[]; + pending?: LockInfo[]; +} + +interface LockOptions { + ifAvailable?: boolean; + mode?: LockMode; + signal?: AbortSignal; + steal?: boolean; +} + +interface MIDIConnectionEventInit extends EventInit { + port?: MIDIPort; +} + +interface MIDIMessageEventInit extends EventInit { + data?: Uint8Array; +} + +interface MIDIOptions { + software?: boolean; + sysex?: boolean; +} + +interface MediaCapabilitiesDecodingInfo extends MediaCapabilitiesInfo { + keySystemAccess: MediaKeySystemAccess | null; +} + +interface MediaCapabilitiesEncodingInfo extends MediaCapabilitiesInfo { +} + +interface MediaCapabilitiesInfo { + powerEfficient: boolean; + smooth: boolean; + supported: boolean; +} + +interface MediaCapabilitiesKeySystemConfiguration { + audio?: KeySystemTrackConfiguration; + distinctiveIdentifier?: MediaKeysRequirement; + initDataType?: string; + keySystem: string; + persistentState?: MediaKeysRequirement; + sessionTypes?: string[]; + video?: KeySystemTrackConfiguration; +} + +interface MediaConfiguration { + audio?: AudioConfiguration; + video?: VideoConfiguration; +} + +interface MediaDecodingConfiguration extends MediaConfiguration { + keySystemConfiguration?: MediaCapabilitiesKeySystemConfiguration; + type: MediaDecodingType; +} + +interface MediaElementAudioSourceOptions { + mediaElement: HTMLMediaElement; +} + +interface MediaEncodingConfiguration extends MediaConfiguration { + type: MediaEncodingType; +} + +interface MediaEncryptedEventInit extends EventInit { + initData?: ArrayBuffer | null; + initDataType?: string; +} + +interface MediaImage { + sizes?: string; + src: string; + type?: string; +} + +interface MediaKeyMessageEventInit extends EventInit { + message: ArrayBuffer; + messageType: MediaKeyMessageType; +} + +interface MediaKeySystemConfiguration { + audioCapabilities?: MediaKeySystemMediaCapability[]; + distinctiveIdentifier?: MediaKeysRequirement; + initDataTypes?: string[]; + label?: string; + persistentState?: MediaKeysRequirement; + sessionTypes?: string[]; + videoCapabilities?: MediaKeySystemMediaCapability[]; +} + +interface MediaKeySystemMediaCapability { + contentType?: string; + encryptionScheme?: string | null; + robustness?: string; +} + +interface MediaKeysPolicy { + minHdcpVersion?: string; +} + +interface MediaMetadataInit { + album?: string; + artist?: string; + artwork?: MediaImage[]; + title?: string; +} + +interface MediaPositionState { + duration?: number; + playbackRate?: number; + position?: number; +} + +interface MediaQueryListEventInit extends EventInit { + matches?: boolean; + media?: string; +} + +interface MediaRecorderOptions { + audioBitsPerSecond?: number; + bitsPerSecond?: number; + mimeType?: string; + videoBitsPerSecond?: number; +} + +interface MediaSessionActionDetails { + action: MediaSessionAction; + fastSeek?: boolean; + seekOffset?: number; + seekTime?: number; +} + +interface MediaSettingsRange { + max?: number; + min?: number; + step?: number; +} + +interface MediaStreamAudioSourceOptions { + mediaStream: MediaStream; +} + +interface MediaStreamConstraints { + audio?: boolean | MediaTrackConstraints; + peerIdentity?: string; + preferCurrentTab?: boolean; + video?: boolean | MediaTrackConstraints; +} + +interface MediaStreamTrackEventInit extends EventInit { + track: MediaStreamTrack; +} + +interface MediaTrackCapabilities { + aspectRatio?: DoubleRange; + autoGainControl?: boolean[]; + backgroundBlur?: boolean[]; + channelCount?: ULongRange; + deviceId?: string; + displaySurface?: string; + echoCancellation?: (boolean | string)[]; + facingMode?: string[]; + frameRate?: DoubleRange; + groupId?: string; + height?: ULongRange; + noiseSuppression?: boolean[]; + sampleRate?: ULongRange; + sampleSize?: ULongRange; + width?: ULongRange; +} + +interface MediaTrackConstraintSet { + aspectRatio?: ConstrainDouble; + autoGainControl?: ConstrainBoolean; + backgroundBlur?: ConstrainBoolean; + channelCount?: ConstrainULong; + deviceId?: ConstrainDOMString; + displaySurface?: ConstrainDOMString; + echoCancellation?: ConstrainBooleanOrDOMString; + facingMode?: ConstrainDOMString; + frameRate?: ConstrainDouble; + groupId?: ConstrainDOMString; + height?: ConstrainULong; + noiseSuppression?: ConstrainBoolean; + sampleRate?: ConstrainULong; + sampleSize?: ConstrainULong; + width?: ConstrainULong; +} + +interface MediaTrackConstraints extends MediaTrackConstraintSet { + advanced?: MediaTrackConstraintSet[]; +} + +interface MediaTrackSettings { + aspectRatio?: number; + autoGainControl?: boolean; + backgroundBlur?: boolean; + channelCount?: number; + deviceId?: string; + displaySurface?: string; + echoCancellation?: boolean | string; + facingMode?: string; + frameRate?: number; + groupId?: string; + height?: number; + noiseSuppression?: boolean; + sampleRate?: number; + sampleSize?: number; + torch?: boolean; + whiteBalanceMode?: string; + width?: number; + zoom?: number; +} + +interface MediaTrackSupportedConstraints { + aspectRatio?: boolean; + autoGainControl?: boolean; + backgroundBlur?: boolean; + channelCount?: boolean; + deviceId?: boolean; + displaySurface?: boolean; + echoCancellation?: boolean; + facingMode?: boolean; + frameRate?: boolean; + groupId?: boolean; + height?: boolean; + noiseSuppression?: boolean; + sampleRate?: boolean; + sampleSize?: boolean; + width?: boolean; +} + +interface MessageEventInit extends EventInit { + data?: T; + lastEventId?: string; + origin?: string; + ports?: MessagePort[]; + source?: MessageEventSource | null; +} + +interface MouseEventInit extends EventModifierInit { + button?: number; + buttons?: number; + clientX?: number; + clientY?: number; + movementX?: number; + movementY?: number; + relatedTarget?: EventTarget | null; + screenX?: number; + screenY?: number; +} + +interface MultiCacheQueryOptions extends CacheQueryOptions { + cacheName?: string; +} + +interface MutationObserverInit { + /** Set to a list of attribute local names (without namespace) if not all attribute mutations need to be observed and attributes is true or omitted. */ + attributeFilter?: string[]; + /** Set to true if attributes is true or omitted and target's attribute value before the mutation needs to be recorded. */ + attributeOldValue?: boolean; + /** Set to true if mutations to target's attributes are to be observed. Can be omitted if attributeOldValue or attributeFilter is specified. */ + attributes?: boolean; + /** Set to true if mutations to target's data are to be observed. Can be omitted if characterDataOldValue is specified. */ + characterData?: boolean; + /** Set to true if characterData is set to true or omitted and target's data before the mutation needs to be recorded. */ + characterDataOldValue?: boolean; + /** Set to true if mutations to target's children are to be observed. */ + childList?: boolean; + /** Set to true if mutations to not just target, but also target's descendants are to be observed. */ + subtree?: boolean; +} + +interface NavigateEventInit extends EventInit { + canIntercept?: boolean; + destination: NavigationDestination; + downloadRequest?: string | null; + formData?: FormData | null; + hasUAVisualTransition?: boolean; + hashChange?: boolean; + info?: any; + navigationType?: NavigationType; + signal: AbortSignal; + sourceElement?: Element | null; + userInitiated?: boolean; +} + +interface NavigationCurrentEntryChangeEventInit extends EventInit { + from: NavigationHistoryEntry; + navigationType?: NavigationType | null; +} + +interface NavigationInterceptOptions { + focusReset?: NavigationFocusReset; + handler?: NavigationInterceptHandler; + precommitHandler?: NavigationPrecommitHandler; + scroll?: NavigationScrollBehavior; +} + +interface NavigationNavigateOptions extends NavigationOptions { + history?: NavigationHistoryBehavior; + state?: any; +} + +interface NavigationOptions { + info?: any; +} + +interface NavigationPreloadState { + enabled?: boolean; + headerValue?: string; +} + +interface NavigationReloadOptions extends NavigationOptions { + state?: any; +} + +interface NavigationResult { + committed?: Promise; + finished?: Promise; +} + +interface NavigationUpdateCurrentEntryOptions { + state: any; +} + +interface NotificationOptions { + badge?: string; + body?: string; + data?: any; + dir?: NotificationDirection; + icon?: string; + lang?: string; + requireInteraction?: boolean; + silent?: boolean | null; + tag?: string; +} + +interface OfflineAudioCompletionEventInit extends EventInit { + renderedBuffer: AudioBuffer; +} + +interface OfflineAudioContextOptions { + length: number; + numberOfChannels?: number; + sampleRate: number; +} + +interface OptionalEffectTiming { + delay?: number; + direction?: PlaybackDirection; + duration?: number | string; + easing?: string; + endDelay?: number; + fill?: FillMode; + iterationStart?: number; + iterations?: number; + playbackRate?: number; +} + +interface OpusEncoderConfig { + complexity?: number; + format?: OpusBitstreamFormat; + frameDuration?: number; + packetlossperc?: number; + usedtx?: boolean; + useinbandfec?: boolean; +} + +interface OscillatorOptions extends AudioNodeOptions { + detune?: number; + frequency?: number; + periodicWave?: PeriodicWave; + type?: OscillatorType; +} + +interface PageRevealEventInit extends EventInit { + viewTransition?: ViewTransition | null; +} + +interface PageSwapEventInit extends EventInit { + activation?: NavigationActivation | null; + viewTransition?: ViewTransition | null; +} + +interface PageTransitionEventInit extends EventInit { + persisted?: boolean; +} + +interface PannerOptions extends AudioNodeOptions { + coneInnerAngle?: number; + coneOuterAngle?: number; + coneOuterGain?: number; + distanceModel?: DistanceModelType; + maxDistance?: number; + orientationX?: number; + orientationY?: number; + orientationZ?: number; + panningModel?: PanningModelType; + positionX?: number; + positionY?: number; + positionZ?: number; + refDistance?: number; + rolloffFactor?: number; +} + +interface PayerErrors { + email?: string; + name?: string; + phone?: string; +} + +interface PaymentCurrencyAmount { + currency: string; + value: string; +} + +interface PaymentDetailsBase { + displayItems?: PaymentItem[]; + modifiers?: PaymentDetailsModifier[]; + shippingOptions?: PaymentShippingOption[]; +} + +interface PaymentDetailsInit extends PaymentDetailsBase { + id?: string; + total: PaymentItem; +} + +interface PaymentDetailsModifier { + additionalDisplayItems?: PaymentItem[]; + data?: any; + supportedMethods: string; + total?: PaymentItem; +} + +interface PaymentDetailsUpdate extends PaymentDetailsBase { + error?: string; + paymentMethodErrors?: any; + shippingAddressErrors?: AddressErrors; + total?: PaymentItem; +} + +interface PaymentItem { + amount: PaymentCurrencyAmount; + label: string; + pending?: boolean; +} + +interface PaymentMethodChangeEventInit extends PaymentRequestUpdateEventInit { + methodDetails?: any; + methodName?: string; +} + +interface PaymentMethodData { + data?: any; + supportedMethods: string; +} + +interface PaymentOptions { + requestPayerEmail?: boolean; + requestPayerName?: boolean; + requestPayerPhone?: boolean; + requestShipping?: boolean; + shippingType?: PaymentShippingType; +} + +interface PaymentRequestUpdateEventInit extends EventInit { +} + +interface PaymentShippingOption { + amount: PaymentCurrencyAmount; + id: string; + label: string; + selected?: boolean; +} + +interface PaymentValidationErrors { + error?: string; + payer?: PayerErrors; + shippingAddress?: AddressErrors; +} + +interface Pbkdf2Params extends Algorithm { + hash: HashAlgorithmIdentifier; + iterations: number; + salt: BufferSource; +} + +interface PerformanceMarkOptions { + detail?: any; + startTime?: DOMHighResTimeStamp; +} + +interface PerformanceMeasureOptions { + detail?: any; + duration?: DOMHighResTimeStamp; + end?: string | DOMHighResTimeStamp; + start?: string | DOMHighResTimeStamp; +} + +interface PerformanceObserverInit { + buffered?: boolean; + entryTypes?: string[]; + type?: string; +} + +interface PeriodicWaveConstraints { + disableNormalization?: boolean; +} + +interface PeriodicWaveOptions extends PeriodicWaveConstraints { + imag?: number[] | Float32Array; + real?: number[] | Float32Array; +} + +interface PermissionDescriptor { + name: PermissionName; +} + +interface PhotoCapabilities { + fillLightMode?: FillLightMode[]; + imageHeight?: MediaSettingsRange; + imageWidth?: MediaSettingsRange; + redEyeReduction?: RedEyeReduction; +} + +interface PhotoSettings { + fillLightMode?: FillLightMode; + imageHeight?: number; + imageWidth?: number; + redEyeReduction?: boolean; +} + +interface PictureInPictureEventInit extends EventInit { + pictureInPictureWindow: PictureInPictureWindow; +} + +interface PlaneLayout { + offset: number; + stride: number; +} + +interface PointerEventInit extends MouseEventInit { + altitudeAngle?: number; + azimuthAngle?: number; + coalescedEvents?: PointerEvent[]; + height?: number; + isPrimary?: boolean; + pointerId?: number; + pointerType?: string; + predictedEvents?: PointerEvent[]; + pressure?: number; + tangentialPressure?: number; + tiltX?: number; + tiltY?: number; + twist?: number; + width?: number; +} + +interface PointerLockOptions { + unadjustedMovement?: boolean; +} + +interface PopStateEventInit extends EventInit { + hasUAVisualTransition?: boolean; + state?: any; +} + +interface PositionOptions { + enableHighAccuracy?: boolean; + maximumAge?: number; + timeout?: number; +} + +interface ProgressEventInit extends EventInit { + lengthComputable?: boolean; + loaded?: number; + total?: number; +} + +interface PromiseRejectionEventInit extends EventInit { + promise: Promise; + reason?: any; +} + +interface PropertyDefinition { + inherits: boolean; + initialValue?: string; + name: string; + syntax?: string; +} + +interface PropertyIndexedKeyframes { + composite?: CompositeOperationOrAuto | CompositeOperationOrAuto[]; + easing?: string | string[]; + offset?: number | (number | null)[]; + [property: string]: string | string[] | number | null | (number | null)[] | undefined; +} + +interface PublicKeyCredentialCreationOptions { + attestation?: AttestationConveyancePreference; + authenticatorSelection?: AuthenticatorSelectionCriteria; + challenge: BufferSource; + excludeCredentials?: PublicKeyCredentialDescriptor[]; + extensions?: AuthenticationExtensionsClientInputs; + pubKeyCredParams: PublicKeyCredentialParameters[]; + rp: PublicKeyCredentialRpEntity; + timeout?: number; + user: PublicKeyCredentialUserEntity; +} + +interface PublicKeyCredentialCreationOptionsJSON { + attestation?: string; + authenticatorSelection?: AuthenticatorSelectionCriteria; + challenge: Base64URLString; + excludeCredentials?: PublicKeyCredentialDescriptorJSON[]; + extensions?: AuthenticationExtensionsClientInputsJSON; + hints?: string[]; + pubKeyCredParams: PublicKeyCredentialParameters[]; + rp: PublicKeyCredentialRpEntity; + timeout?: number; + user: PublicKeyCredentialUserEntityJSON; +} + +interface PublicKeyCredentialDescriptor { + id: BufferSource; + transports?: AuthenticatorTransport[]; + type: PublicKeyCredentialType; +} + +interface PublicKeyCredentialDescriptorJSON { + id: Base64URLString; + transports?: string[]; + type: string; +} + +interface PublicKeyCredentialEntity { + name: string; +} + +interface PublicKeyCredentialParameters { + alg: COSEAlgorithmIdentifier; + type: PublicKeyCredentialType; +} + +interface PublicKeyCredentialRequestOptions { + allowCredentials?: PublicKeyCredentialDescriptor[]; + challenge: BufferSource; + extensions?: AuthenticationExtensionsClientInputs; + rpId?: string; + timeout?: number; + userVerification?: UserVerificationRequirement; +} + +interface PublicKeyCredentialRequestOptionsJSON { + allowCredentials?: PublicKeyCredentialDescriptorJSON[]; + challenge: Base64URLString; + extensions?: AuthenticationExtensionsClientInputsJSON; + hints?: string[]; + rpId?: string; + timeout?: number; + userVerification?: string; +} + +interface PublicKeyCredentialRpEntity extends PublicKeyCredentialEntity { + id?: string; +} + +interface PublicKeyCredentialUserEntity extends PublicKeyCredentialEntity { + displayName: string; + id: BufferSource; +} + +interface PublicKeyCredentialUserEntityJSON { + displayName: string; + id: Base64URLString; + name: string; +} + +interface PushSubscriptionJSON { + endpoint?: string; + expirationTime?: EpochTimeStamp | null; + keys?: Record; +} + +interface PushSubscriptionOptionsInit { + applicationServerKey?: BufferSource | string | null; + userVisibleOnly?: boolean; +} + +interface QueuingStrategy { + highWaterMark?: number; + size?: QueuingStrategySize; +} + +interface QueuingStrategyInit { + /** + * Creates a new ByteLengthQueuingStrategy with the provided high water mark. + * + * Note that the provided high water mark will not be validated ahead of time. Instead, if it is negative, NaN, or not a number, the resulting ByteLengthQueuingStrategy will cause the corresponding stream constructor to throw. + */ + highWaterMark: number; +} + +interface RTCAnswerOptions extends RTCOfferAnswerOptions { +} + +interface RTCCertificateExpiration { + expires?: number; +} + +interface RTCConfiguration { + bundlePolicy?: RTCBundlePolicy; + certificates?: RTCCertificate[]; + iceCandidatePoolSize?: number; + iceServers?: RTCIceServer[]; + iceTransportPolicy?: RTCIceTransportPolicy; + rtcpMuxPolicy?: RTCRtcpMuxPolicy; +} + +interface RTCDTMFToneChangeEventInit extends EventInit { + tone?: string; +} + +interface RTCDataChannelEventInit extends EventInit { + channel: RTCDataChannel; +} + +interface RTCDataChannelInit { + id?: number; + maxPacketLifeTime?: number; + maxRetransmits?: number; + negotiated?: boolean; + ordered?: boolean; + protocol?: string; +} + +interface RTCDtlsFingerprint { + algorithm?: string; + value?: string; +} + +interface RTCEncodedAudioFrameMetadata extends RTCEncodedFrameMetadata { + sequenceNumber?: number; +} + +interface RTCEncodedFrameMetadata { + contributingSources?: number[]; + mimeType?: string; + payloadType?: number; + rtpTimestamp?: number; + synchronizationSource?: number; +} + +interface RTCEncodedVideoFrameMetadata extends RTCEncodedFrameMetadata { + dependencies?: number[]; + frameId?: number; + height?: number; + spatialIndex?: number; + temporalIndex?: number; + timestamp?: number; + width?: number; +} + +interface RTCErrorEventInit extends EventInit { + error: RTCError; +} + +interface RTCErrorInit { + errorDetail: RTCErrorDetailType; + httpRequestStatusCode?: number; + receivedAlert?: number; + sctpCauseCode?: number; + sdpLineNumber?: number; + sentAlert?: number; +} + +interface RTCIceCandidateInit { + candidate?: string; + sdpMLineIndex?: number | null; + sdpMid?: string | null; + usernameFragment?: string | null; +} + +interface RTCIceCandidatePairStats extends RTCStats { + availableIncomingBitrate?: number; + availableOutgoingBitrate?: number; + bytesDiscardedOnSend?: number; + bytesReceived?: number; + bytesSent?: number; + consentRequestsSent?: number; + currentRoundTripTime?: number; + lastPacketReceivedTimestamp?: DOMHighResTimeStamp; + lastPacketSentTimestamp?: DOMHighResTimeStamp; + localCandidateId: string; + nominated?: boolean; + packetsDiscardedOnSend?: number; + packetsReceived?: number; + packetsSent?: number; + remoteCandidateId: string; + requestsReceived?: number; + requestsSent?: number; + responsesReceived?: number; + responsesSent?: number; + state: RTCStatsIceCandidatePairState; + totalRoundTripTime?: number; + transportId: string; +} + +interface RTCIceServer { + credential?: string; + urls: string | string[]; + username?: string; +} + +interface RTCInboundRtpStreamStats extends RTCReceivedRtpStreamStats { + audioLevel?: number; + bytesReceived?: number; + concealedSamples?: number; + concealmentEvents?: number; + decoderImplementation?: string; + estimatedPlayoutTimestamp?: DOMHighResTimeStamp; + fecBytesReceived?: number; + fecPacketsDiscarded?: number; + fecPacketsReceived?: number; + fecSsrc?: number; + firCount?: number; + frameHeight?: number; + frameWidth?: number; + framesAssembledFromMultiplePackets?: number; + framesDecoded?: number; + framesDropped?: number; + framesPerSecond?: number; + framesReceived?: number; + framesRendered?: number; + freezeCount?: number; + headerBytesReceived?: number; + insertedSamplesForDeceleration?: number; + jitterBufferDelay?: number; + jitterBufferEmittedCount?: number; + jitterBufferMinimumDelay?: number; + jitterBufferTargetDelay?: number; + keyFramesDecoded?: number; + lastPacketReceivedTimestamp?: DOMHighResTimeStamp; + mid?: string; + nackCount?: number; + packetsDiscarded?: number; + pauseCount?: number; + playoutId?: string; + pliCount?: number; + qpSum?: number; + remoteId?: string; + removedSamplesForAcceleration?: number; + retransmittedBytesReceived?: number; + retransmittedPacketsReceived?: number; + rtxSsrc?: number; + silentConcealedSamples?: number; + totalAssemblyTime?: number; + totalAudioEnergy?: number; + totalDecodeTime?: number; + totalFreezesDuration?: number; + totalInterFrameDelay?: number; + totalPausesDuration?: number; + totalProcessingDelay?: number; + totalSamplesDuration?: number; + totalSamplesReceived?: number; + totalSquaredInterFrameDelay?: number; + trackIdentifier: string; +} + +interface RTCLocalIceCandidateInit extends RTCIceCandidateInit { +} + +interface RTCLocalSessionDescriptionInit { + sdp?: string; + type?: RTCSdpType; +} + +interface RTCOfferAnswerOptions { +} + +interface RTCOfferOptions extends RTCOfferAnswerOptions { + iceRestart?: boolean; + offerToReceiveAudio?: boolean; + offerToReceiveVideo?: boolean; +} + +interface RTCOutboundRtpStreamStats extends RTCSentRtpStreamStats { + active?: boolean; + firCount?: number; + frameHeight?: number; + frameWidth?: number; + framesEncoded?: number; + framesPerSecond?: number; + framesSent?: number; + headerBytesSent?: number; + hugeFramesSent?: number; + keyFramesEncoded?: number; + mediaSourceId?: string; + mid?: string; + nackCount?: number; + pliCount?: number; + qpSum?: number; + qualityLimitationDurations?: Record; + qualityLimitationReason?: RTCQualityLimitationReason; + qualityLimitationResolutionChanges?: number; + remoteId?: string; + retransmittedBytesSent?: number; + retransmittedPacketsSent?: number; + rid?: string; + rtxSsrc?: number; + scalabilityMode?: string; + targetBitrate?: number; + totalEncodeTime?: number; + totalEncodedBytesTarget?: number; + totalPacketSendDelay?: number; +} + +interface RTCPeerConnectionIceErrorEventInit extends EventInit { + address?: string | null; + errorCode: number; + errorText?: string; + port?: number | null; + url?: string; +} + +interface RTCPeerConnectionIceEventInit extends EventInit { + candidate?: RTCIceCandidate | null; +} + +interface RTCReceivedRtpStreamStats extends RTCRtpStreamStats { + jitter?: number; + packetsLost?: number; + packetsReceived?: number; +} + +interface RTCRtcpParameters { + cname?: string; + reducedSize?: boolean; +} + +interface RTCRtpCapabilities { + codecs: RTCRtpCodec[]; + headerExtensions: RTCRtpHeaderExtensionCapability[]; +} + +interface RTCRtpCodec { + channels?: number; + clockRate: number; + mimeType: string; + sdpFmtpLine?: string; +} + +interface RTCRtpCodecParameters extends RTCRtpCodec { + payloadType: number; +} + +interface RTCRtpCodingParameters { + rid?: string; +} + +interface RTCRtpContributingSource { + audioLevel?: number; + rtpTimestamp: number; + source: number; + timestamp: DOMHighResTimeStamp; +} + +interface RTCRtpEncodingParameters extends RTCRtpCodingParameters { + active?: boolean; + maxBitrate?: number; + maxFramerate?: number; + networkPriority?: RTCPriorityType; + priority?: RTCPriorityType; + scaleResolutionDownBy?: number; +} + +interface RTCRtpHeaderExtensionCapability { + uri: string; +} + +interface RTCRtpHeaderExtensionParameters { + encrypted?: boolean; + id: number; + uri: string; +} + +interface RTCRtpParameters { + codecs: RTCRtpCodecParameters[]; + headerExtensions: RTCRtpHeaderExtensionParameters[]; + rtcp: RTCRtcpParameters; +} + +interface RTCRtpReceiveParameters extends RTCRtpParameters { +} + +interface RTCRtpSendParameters extends RTCRtpParameters { + degradationPreference?: RTCDegradationPreference; + encodings: RTCRtpEncodingParameters[]; + transactionId: string; +} + +interface RTCRtpStreamStats extends RTCStats { + codecId?: string; + kind: string; + ssrc: number; + transportId?: string; +} + +interface RTCRtpSynchronizationSource extends RTCRtpContributingSource { +} + +interface RTCRtpTransceiverInit { + direction?: RTCRtpTransceiverDirection; + sendEncodings?: RTCRtpEncodingParameters[]; + streams?: MediaStream[]; +} + +interface RTCSentRtpStreamStats extends RTCRtpStreamStats { + bytesSent?: number; + packetsSent?: number; +} + +interface RTCSessionDescriptionInit { + sdp?: string; + type: RTCSdpType; +} + +interface RTCSetParameterOptions { +} + +interface RTCStats { + id: string; + timestamp: DOMHighResTimeStamp; + type: RTCStatsType; +} + +interface RTCTrackEventInit extends EventInit { + receiver: RTCRtpReceiver; + streams?: MediaStream[]; + track: MediaStreamTrack; + transceiver: RTCRtpTransceiver; +} + +interface RTCTransportStats extends RTCStats { + bytesReceived?: number; + bytesSent?: number; + dtlsCipher?: string; + dtlsRole?: RTCDtlsRole; + dtlsState: RTCDtlsTransportState; + iceLocalUsernameFragment?: string; + iceRole?: RTCIceRole; + iceState?: RTCIceTransportState; + localCertificateId?: string; + packetsReceived?: number; + packetsSent?: number; + remoteCertificateId?: string; + selectedCandidatePairChanges?: number; + selectedCandidatePairId?: string; + srtpCipher?: string; + tlsVersion?: string; +} + +interface ReadableStreamBYOBReaderReadOptions { + min?: number; +} + +interface ReadableStreamGetReaderOptions { + /** + * Creates a ReadableStreamBYOBReader and locks the stream to the new reader. + * + * This call behaves the same way as the no-argument variant, except that it only works on readable byte streams, i.e. streams which were constructed specifically with the ability to handle "bring your own buffer" reading. The returned BYOB reader provides the ability to directly read individual chunks from the stream via its read() method, into developer-supplied buffers, allowing more precise control over allocation. + */ + mode?: ReadableStreamReaderMode; +} + +interface ReadableStreamIteratorOptions { + /** + * Asynchronously iterates over the chunks in the stream's internal queue. + * + * Asynchronously iterating over the stream will lock it, preventing any other consumer from acquiring a reader. The lock will be released if the async iterator's return() method is called, e.g. by breaking out of the loop. + * + * By default, calling the async iterator's return() method will also cancel the stream. To prevent this, use the stream's values() method, passing true for the preventCancel option. + */ + preventCancel?: boolean; +} + +interface ReadableStreamReadDoneResult { + done: true; + value: T | undefined; +} + +interface ReadableStreamReadValueResult { + done: false; + value: T; +} + +interface ReadableWritablePair { + readable: ReadableStream; + /** + * Provides a convenient, chainable way of piping this readable stream through a transform stream (or any other { writable, readable } pair). It simply pipes the stream into the writable side of the supplied pair, and returns the readable side for further use. + * + * Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader. + */ + writable: WritableStream; +} + +interface RegistrationOptions { + scope?: string; + type?: WorkerType; + updateViaCache?: ServiceWorkerUpdateViaCache; +} + +interface RegistrationResponseJSON { + authenticatorAttachment?: string; + clientExtensionResults: AuthenticationExtensionsClientOutputsJSON; + id: string; + rawId: Base64URLString; + response: AuthenticatorAttestationResponseJSON; + type: string; +} + +interface Report { + body?: ReportBody | null; + type?: string; + url?: string; +} + +interface ReportBody { +} + +interface ReportingObserverOptions { + buffered?: boolean; + types?: string[]; +} + +interface RequestInit { + /** A BodyInit object or null to set request's body. */ + body?: BodyInit | null; + /** A string indicating how the request will interact with the browser's cache to set request's cache. */ + cache?: RequestCache; + /** A string indicating whether credentials will be sent with the request always, never, or only when sent to a same-origin URL. Sets request's credentials. */ + credentials?: RequestCredentials; + /** A Headers object, an object literal, or an array of two-item arrays to set request's headers. */ + headers?: HeadersInit; + /** A cryptographic hash of the resource to be fetched by request. Sets request's integrity. */ + integrity?: string; + /** A boolean to set request's keepalive. */ + keepalive?: boolean; + /** A string to set request's method. */ + method?: string; + /** A string to indicate whether the request will use CORS, or will be restricted to same-origin URLs. Sets request's mode. */ + mode?: RequestMode; + priority?: RequestPriority; + /** A string indicating whether request follows redirects, results in an error upon encountering a redirect, or returns the redirect (in an opaque fashion). Sets request's redirect. */ + redirect?: RequestRedirect; + /** A string whose value is a same-origin URL, "about:client", or the empty string, to set request's referrer. */ + referrer?: string; + /** A referrer policy to set request's referrerPolicy. */ + referrerPolicy?: ReferrerPolicy; + /** An AbortSignal to set request's signal. */ + signal?: AbortSignal | null; + /** Can only be null. Used to disassociate request from any Window. */ + window?: null; +} + +interface ResizeObserverOptions { + box?: ResizeObserverBoxOptions; +} + +interface ResponseInit { + headers?: HeadersInit; + status?: number; + statusText?: string; +} + +interface RsaHashedImportParams extends Algorithm { + hash: HashAlgorithmIdentifier; +} + +interface RsaHashedKeyAlgorithm extends RsaKeyAlgorithm { + hash: KeyAlgorithm; +} + +interface RsaHashedKeyGenParams extends RsaKeyGenParams { + hash: HashAlgorithmIdentifier; +} + +interface RsaKeyAlgorithm extends KeyAlgorithm { + modulusLength: number; + publicExponent: BigInteger; +} + +interface RsaKeyGenParams extends Algorithm { + modulusLength: number; + publicExponent: BigInteger; +} + +interface RsaOaepParams extends Algorithm { + label?: BufferSource; +} + +interface RsaOtherPrimesInfo { + d?: string; + r?: string; + t?: string; +} + +interface RsaPssParams extends Algorithm { + saltLength: number; +} + +interface SVGBoundingBoxOptions { + clipped?: boolean; + fill?: boolean; + markers?: boolean; + stroke?: boolean; +} + +interface SanitizerAttributeNamespace { + name: string; + namespace?: string | null; +} + +interface SanitizerConfig { + attributes?: SanitizerAttribute[]; + comments?: boolean; + dataAttributes?: boolean; + elements?: SanitizerElementWithAttributes[]; + removeAttributes?: SanitizerAttribute[]; + removeElements?: SanitizerElement[]; + replaceWithChildrenElements?: SanitizerElement[]; +} + +interface SanitizerElementNamespace { + name: string; + namespace?: string | null; +} + +interface SanitizerElementNamespaceWithAttributes extends SanitizerElementNamespace { + attributes?: SanitizerAttribute[]; + removeAttributes?: SanitizerAttribute[]; +} + +interface SchedulerPostTaskOptions { + delay?: number; + priority?: TaskPriority; + signal?: AbortSignal; +} + +interface ScrollIntoViewOptions extends ScrollOptions { + block?: ScrollLogicalPosition; + inline?: ScrollLogicalPosition; +} + +interface ScrollOptions { + behavior?: ScrollBehavior; +} + +interface ScrollTimelineOptions { + axis?: ScrollAxis; + source?: Element | null; +} + +interface ScrollToOptions extends ScrollOptions { + left?: number; + top?: number; +} + +interface SecurityPolicyViolationEventInit extends EventInit { + blockedURI?: string; + columnNumber?: number; + disposition?: SecurityPolicyViolationEventDisposition; + documentURI?: string; + effectiveDirective?: string; + lineNumber?: number; + originalPolicy?: string; + referrer?: string; + sample?: string; + sourceFile?: string; + statusCode?: number; + violatedDirective?: string; +} + +interface ShadowRootInit { + clonable?: boolean; + customElementRegistry?: CustomElementRegistry | null; + delegatesFocus?: boolean; + mode: ShadowRootMode; + serializable?: boolean; + slotAssignment?: SlotAssignmentMode; +} + +interface ShareData { + files?: File[]; + text?: string; + title?: string; + url?: string; +} + +interface ShowPopoverOptions { + source?: HTMLElement; +} + +interface SpeechRecognitionErrorEventInit extends EventInit { + error: SpeechRecognitionErrorCode; + message?: string; +} + +interface SpeechRecognitionEventInit extends EventInit { + resultIndex?: number; + results: SpeechRecognitionResultList; +} + +interface SpeechSynthesisErrorEventInit extends SpeechSynthesisEventInit { + error: SpeechSynthesisErrorCode; +} + +interface SpeechSynthesisEventInit extends EventInit { + charIndex?: number; + charLength?: number; + elapsedTime?: number; + name?: string; + utterance: SpeechSynthesisUtterance; +} + +interface StartViewTransitionOptions { + types?: string[] | null; + update?: ViewTransitionUpdateCallback | null; +} + +interface StaticRangeInit { + endContainer: Node; + endOffset: number; + startContainer: Node; + startOffset: number; +} + +interface StereoPannerOptions extends AudioNodeOptions { + pan?: number; +} + +interface StorageEstimate { + quota?: number; + usage?: number; +} + +interface StorageEventInit extends EventInit { + key?: string | null; + newValue?: string | null; + oldValue?: string | null; + storageArea?: Storage | null; + url?: string; +} + +interface StreamPipeOptions { + preventAbort?: boolean; + preventCancel?: boolean; + /** + * Pipes this readable stream to a given writable stream destination. The way in which the piping process behaves under various error conditions can be customized with a number of passed options. It returns a promise that fulfills when the piping process completes successfully, or rejects if any errors were encountered. + * + * Piping a stream will lock it for the duration of the pipe, preventing any other consumer from acquiring a reader. + * + * Errors and closures of the source and destination streams propagate as follows: + * + * An error in this source readable stream will abort destination, unless preventAbort is truthy. The returned promise will be rejected with the source's error, or with any error that occurs during aborting the destination. + * + * An error in destination will cancel this source readable stream, unless preventCancel is truthy. The returned promise will be rejected with the destination's error, or with any error that occurs during canceling the source. + * + * When this source readable stream closes, destination will be closed, unless preventClose is truthy. The returned promise will be fulfilled once this process completes, unless an error is encountered while closing the destination, in which case it will be rejected with that error. + * + * If destination starts out closed or closing, this source readable stream will be canceled, unless preventCancel is true. The returned promise will be rejected with an error indicating piping to a closed stream failed, or with any error that occurs during canceling the source. + * + * The signal option can be set to an AbortSignal to allow aborting an ongoing pipe operation via the corresponding AbortController. In this case, this source readable stream will be canceled, and destination aborted, unless the respective options preventCancel or preventAbort are set. + */ + preventClose?: boolean; + signal?: AbortSignal; +} + +interface StructuredSerializeOptions { + transfer?: Transferable[]; +} + +interface SubmitEventInit extends EventInit { + submitter?: HTMLElement | null; +} + +interface SvcOutputMetadata { + temporalLayerId?: number; +} + +interface TaskControllerInit { + priority?: TaskPriority; +} + +interface TaskPriorityChangeEventInit extends EventInit { + previousPriority: TaskPriority; +} + +interface TaskSignalAnyInit { + priority?: TaskPriority | TaskSignal; +} + +interface TextDecodeOptions { + stream?: boolean; +} + +interface TextDecoderOptions { + fatal?: boolean; + ignoreBOM?: boolean; +} + +interface TextEncoderEncodeIntoResult { + read: number; + written: number; +} + +interface TimelineRangeOffset { + offset?: CSSNumericValue; + rangeName?: string | null; +} + +interface ToggleEventInit extends EventInit { + newState?: string; + oldState?: string; + source?: Element | null; +} + +interface TogglePopoverOptions extends ShowPopoverOptions { + force?: boolean; +} + +interface TouchEventInit extends EventModifierInit { + changedTouches?: Touch[]; + targetTouches?: Touch[]; + touches?: Touch[]; +} + +interface TouchInit { + altitudeAngle?: number; + azimuthAngle?: number; + clientX?: number; + clientY?: number; + force?: number; + identifier: number; + pageX?: number; + pageY?: number; + radiusX?: number; + radiusY?: number; + rotationAngle?: number; + screenX?: number; + screenY?: number; + target: EventTarget; + touchType?: TouchType; +} + +interface TrackEventInit extends EventInit { + track?: TextTrack | null; +} + +interface Transformer { + flush?: TransformerFlushCallback; + readableType?: undefined; + start?: TransformerStartCallback; + transform?: TransformerTransformCallback; + writableType?: undefined; +} + +interface TransitionEventInit extends EventInit { + elapsedTime?: number; + propertyName?: string; + pseudoElement?: string; +} + +interface UIEventInit extends EventInit { + detail?: number; + view?: Window | null; + /** @deprecated */ + which?: number; +} + +interface ULongRange { + max?: number; + min?: number; +} + +interface URLPatternComponentResult { + groups: Record; + input: string; +} + +interface URLPatternInit { + baseURL?: string; + hash?: string; + hostname?: string; + password?: string; + pathname?: string; + port?: string; + protocol?: string; + search?: string; + username?: string; +} + +interface URLPatternOptions { + ignoreCase?: boolean; +} + +interface URLPatternResult { + hash: URLPatternComponentResult; + hostname: URLPatternComponentResult; + inputs: URLPatternInput[]; + password: URLPatternComponentResult; + pathname: URLPatternComponentResult; + port: URLPatternComponentResult; + protocol: URLPatternComponentResult; + search: URLPatternComponentResult; + username: URLPatternComponentResult; +} + +interface UnderlyingByteSource { + autoAllocateChunkSize?: number; + cancel?: UnderlyingSourceCancelCallback; + pull?: (controller: ReadableByteStreamController) => void | PromiseLike; + start?: (controller: ReadableByteStreamController) => any; + type: "bytes"; +} + +interface UnderlyingDefaultSource { + cancel?: UnderlyingSourceCancelCallback; + pull?: (controller: ReadableStreamDefaultController) => void | PromiseLike; + start?: (controller: ReadableStreamDefaultController) => any; + type?: undefined; +} + +interface UnderlyingSink { + abort?: UnderlyingSinkAbortCallback; + close?: UnderlyingSinkCloseCallback; + start?: UnderlyingSinkStartCallback; + type?: undefined; + write?: UnderlyingSinkWriteCallback; +} + +interface UnderlyingSource { + autoAllocateChunkSize?: number; + cancel?: UnderlyingSourceCancelCallback; + pull?: UnderlyingSourcePullCallback; + start?: UnderlyingSourceStartCallback; + type?: ReadableStreamType; +} + +interface UnknownCredentialOptions { + credentialId: Base64URLString; + rpId: string; +} + +interface ValidityStateFlags { + badInput?: boolean; + customError?: boolean; + patternMismatch?: boolean; + rangeOverflow?: boolean; + rangeUnderflow?: boolean; + stepMismatch?: boolean; + tooLong?: boolean; + tooShort?: boolean; + typeMismatch?: boolean; + valueMissing?: boolean; +} + +interface VideoColorSpaceInit { + fullRange?: boolean | null; + matrix?: VideoMatrixCoefficients | null; + primaries?: VideoColorPrimaries | null; + transfer?: VideoTransferCharacteristics | null; +} + +interface VideoConfiguration { + bitrate: number; + colorGamut?: ColorGamut; + contentType: string; + framerate: number; + hasAlphaChannel?: boolean; + hdrMetadataType?: HdrMetadataType; + height: number; + scalabilityMode?: string; + transferFunction?: TransferFunction; + width: number; +} + +interface VideoDecoderConfig { + codec: string; + codedHeight?: number; + codedWidth?: number; + colorSpace?: VideoColorSpaceInit; + description?: AllowSharedBufferSource; + displayAspectHeight?: number; + displayAspectWidth?: number; + hardwareAcceleration?: HardwareAcceleration; + optimizeForLatency?: boolean; +} + +interface VideoDecoderInit { + error: WebCodecsErrorCallback; + output: VideoFrameOutputCallback; +} + +interface VideoDecoderSupport { + config?: VideoDecoderConfig; + supported?: boolean; +} + +interface VideoEncoderConfig { + alpha?: AlphaOption; + avc?: AvcEncoderConfig; + bitrate?: number; + bitrateMode?: VideoEncoderBitrateMode; + codec: string; + contentHint?: string; + displayHeight?: number; + displayWidth?: number; + framerate?: number; + hardwareAcceleration?: HardwareAcceleration; + height: number; + latencyMode?: LatencyMode; + scalabilityMode?: string; + width: number; +} + +interface VideoEncoderEncodeOptions { + avc?: VideoEncoderEncodeOptionsForAvc; + keyFrame?: boolean; +} + +interface VideoEncoderEncodeOptionsForAvc { + quantizer?: number | null; +} + +interface VideoEncoderInit { + error: WebCodecsErrorCallback; + output: EncodedVideoChunkOutputCallback; +} + +interface VideoEncoderSupport { + config?: VideoEncoderConfig; + supported?: boolean; +} + +interface VideoFrameBufferInit { + codedHeight: number; + codedWidth: number; + colorSpace?: VideoColorSpaceInit; + displayHeight?: number; + displayWidth?: number; + duration?: number; + format: VideoPixelFormat; + layout?: PlaneLayout[]; + timestamp: number; + visibleRect?: DOMRectInit; +} + +interface VideoFrameCallbackMetadata { + captureTime?: DOMHighResTimeStamp; + expectedDisplayTime: DOMHighResTimeStamp; + height: number; + mediaTime: number; + presentationTime: DOMHighResTimeStamp; + presentedFrames: number; + processingDuration?: number; + receiveTime?: DOMHighResTimeStamp; + rtpTimestamp?: number; + width: number; +} + +interface VideoFrameCopyToOptions { + colorSpace?: PredefinedColorSpace; + format?: VideoPixelFormat; + layout?: PlaneLayout[]; + rect?: DOMRectInit; +} + +interface VideoFrameInit { + alpha?: AlphaOption; + displayHeight?: number; + displayWidth?: number; + duration?: number; + timestamp?: number; + visibleRect?: DOMRectInit; +} + +interface ViewTimelineOptions { + axis?: ScrollAxis; + inset?: string | (CSSNumericValue | CSSKeywordValue)[]; + subject?: Element; +} + +interface WaveShaperOptions extends AudioNodeOptions { + curve?: number[] | Float32Array; + oversample?: OverSampleType; +} + +interface WebGLContextAttributes { + alpha?: boolean; + antialias?: boolean; + depth?: boolean; + desynchronized?: boolean; + failIfMajorPerformanceCaveat?: boolean; + powerPreference?: WebGLPowerPreference; + premultipliedAlpha?: boolean; + preserveDrawingBuffer?: boolean; + stencil?: boolean; + xrCompatible?: boolean; +} + +interface WebGLContextEventInit extends EventInit { + statusMessage?: string; +} + +interface WebTransportCloseInfo { + closeCode?: number; + reason?: string; +} + +interface WebTransportErrorOptions { + source?: WebTransportErrorSource; + streamErrorCode?: number | null; +} + +interface WebTransportHash { + algorithm: string; + value: BufferSource; +} + +interface WebTransportOptions { + allowPooling?: boolean; + congestionControl?: WebTransportCongestionControl; + protocols?: string[]; + requireUnreliable?: boolean; + serverCertificateHashes?: WebTransportHash[]; +} + +interface WebTransportSendOptions { + sendOrder?: number; +} + +interface WebTransportSendStreamOptions extends WebTransportSendOptions { +} + +interface WheelEventInit extends MouseEventInit { + deltaMode?: number; + deltaX?: number; + deltaY?: number; + deltaZ?: number; +} + +interface WindowPostMessageOptions extends StructuredSerializeOptions { + targetOrigin?: string; +} + +interface WorkerOptions { + credentials?: RequestCredentials; + name?: string; + type?: WorkerType; +} + +interface WorkletOptions { + credentials?: RequestCredentials; +} + +interface WriteParams { + data?: BufferSource | Blob | string | null; + position?: number | null; + size?: number | null; + type: WriteCommandType; +} + +type NodeFilter = ((node: Node) => number) | { acceptNode(node: Node): number; }; + +declare var NodeFilter: { + readonly FILTER_ACCEPT: 1; + readonly FILTER_REJECT: 2; + readonly FILTER_SKIP: 3; + readonly SHOW_ALL: 0xFFFFFFFF; + readonly SHOW_ELEMENT: 0x1; + readonly SHOW_ATTRIBUTE: 0x2; + readonly SHOW_TEXT: 0x4; + readonly SHOW_CDATA_SECTION: 0x8; + readonly SHOW_ENTITY_REFERENCE: 0x10; + readonly SHOW_ENTITY: 0x20; + readonly SHOW_PROCESSING_INSTRUCTION: 0x40; + readonly SHOW_COMMENT: 0x80; + readonly SHOW_DOCUMENT: 0x100; + readonly SHOW_DOCUMENT_TYPE: 0x200; + readonly SHOW_DOCUMENT_FRAGMENT: 0x400; + readonly SHOW_NOTATION: 0x800; +}; + +type XPathNSResolver = ((prefix: string | null) => string | null) | { lookupNamespaceURI(prefix: string | null): string | null; }; + +/** + * The **`ANGLE_instanced_arrays`** extension is part of the WebGL API and allows to draw the same object, or groups of similar objects multiple times, if they share the same vertex data, primitive count and type. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ANGLE_instanced_arrays) + */ +interface ANGLE_instanced_arrays { + /** + * The **`ANGLE_instanced_arrays.drawArraysInstancedANGLE()`** method of the WebGL API renders primitives from array data like the gl.drawArrays() method. In addition, it can execute multiple instances of the range of elements. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ANGLE_instanced_arrays/drawArraysInstancedANGLE) + */ + drawArraysInstancedANGLE(mode: GLenum, first: GLint, count: GLsizei, primcount: GLsizei): void; + /** + * The **`ANGLE_instanced_arrays.drawElementsInstancedANGLE()`** method of the WebGL API renders primitives from array data like the gl.drawElements() method. In addition, it can execute multiple instances of a set of elements. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ANGLE_instanced_arrays/drawElementsInstancedANGLE) + */ + drawElementsInstancedANGLE(mode: GLenum, count: GLsizei, type: GLenum, offset: GLintptr, primcount: GLsizei): void; + /** + * The **`ANGLE_instanced_arrays.vertexAttribDivisorANGLE()`** method of the WebGL API modifies the rate at which generic vertex attributes advance when rendering multiple instances of primitives with ext.drawArraysInstancedANGLE() and ext.drawElementsInstancedANGLE(). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/ANGLE_instanced_arrays/vertexAttribDivisorANGLE) + */ + vertexAttribDivisorANGLE(index: GLuint, divisor: GLuint): void; + readonly VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE: 0x88FE; +} + +interface ARIAMixin { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaActiveDescendantElement) */ + ariaActiveDescendantElement: Element | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaAtomic) */ + ariaAtomic: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaAutoComplete) */ + ariaAutoComplete: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaBrailleLabel) */ + ariaBrailleLabel: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaBrailleRoleDescription) */ + ariaBrailleRoleDescription: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaBusy) */ + ariaBusy: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaChecked) */ + ariaChecked: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaColCount) */ + ariaColCount: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaColIndex) */ + ariaColIndex: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaColIndexText) */ + ariaColIndexText: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaColSpan) */ + ariaColSpan: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaControlsElements) */ + ariaControlsElements: ReadonlyArray | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaCurrent) */ + ariaCurrent: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaDescribedByElements) */ + ariaDescribedByElements: ReadonlyArray | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaDescription) */ + ariaDescription: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaDetailsElements) */ + ariaDetailsElements: ReadonlyArray | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaDisabled) */ + ariaDisabled: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaErrorMessageElements) */ + ariaErrorMessageElements: ReadonlyArray | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaExpanded) */ + ariaExpanded: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaFlowToElements) */ + ariaFlowToElements: ReadonlyArray | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaHasPopup) */ + ariaHasPopup: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaHidden) */ + ariaHidden: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaInvalid) */ + ariaInvalid: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaKeyShortcuts) */ + ariaKeyShortcuts: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaLabel) */ + ariaLabel: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaLabelledByElements) */ + ariaLabelledByElements: ReadonlyArray | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaLevel) */ + ariaLevel: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaLive) */ + ariaLive: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaModal) */ + ariaModal: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaMultiLine) */ + ariaMultiLine: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaMultiSelectable) */ + ariaMultiSelectable: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaOrientation) */ + ariaOrientation: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaOwnsElements) */ + ariaOwnsElements: ReadonlyArray | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaPlaceholder) */ + ariaPlaceholder: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaPosInSet) */ + ariaPosInSet: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaPressed) */ + ariaPressed: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaReadOnly) */ + ariaReadOnly: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaRelevant) */ + ariaRelevant: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaRequired) */ + ariaRequired: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaRoleDescription) */ + ariaRoleDescription: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaRowCount) */ + ariaRowCount: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaRowIndex) */ + ariaRowIndex: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaRowIndexText) */ + ariaRowIndexText: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaRowSpan) */ + ariaRowSpan: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaSelected) */ + ariaSelected: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaSetSize) */ + ariaSetSize: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaSort) */ + ariaSort: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaValueMax) */ + ariaValueMax: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaValueMin) */ + ariaValueMin: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaValueNow) */ + ariaValueNow: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/ariaValueText) */ + ariaValueText: string | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/role) */ + role: string | null; +} + +/** + * The **`AbortController`** interface represents a controller object that allows you to abort one or more Web requests as and when desired. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController) + */ +interface AbortController { + /** + * The **`signal`** read-only property of the AbortController interface returns an AbortSignal object instance, which can be used to communicate with/abort an asynchronous operation as desired. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController/signal) + */ + readonly signal: AbortSignal; + /** + * The **`abort()`** method of the AbortController interface aborts an asynchronous operation before it has completed. This is able to abort fetch requests, the consumption of any response bodies, or streams. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortController/abort) + */ + abort(reason?: any): void; +} + +declare var AbortController: { + prototype: AbortController; + new(): AbortController; +}; + +interface AbortSignalEventMap { + "abort": Event; +} + +/** + * The **`AbortSignal`** interface represents a signal object that allows you to communicate with an asynchronous operation (such as a fetch request) and abort it if required via an AbortController object. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal) + */ +interface AbortSignal extends EventTarget { + /** + * The **`aborted`** read-only property returns a value that indicates whether the asynchronous operations the signal is communicating with are aborted (true) or not (false). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/aborted) + */ + readonly aborted: boolean; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_event) */ + onabort: ((this: AbortSignal, ev: Event) => any) | null; + /** + * The **`reason`** read-only property returns a JavaScript value that indicates the abort reason. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/reason) + */ + readonly reason: any; + /** + * The **`throwIfAborted()`** method throws the signal's abort reason if the signal has been aborted; otherwise it does nothing. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/throwIfAborted) + */ + throwIfAborted(): void; + addEventListener(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; +} + +declare var AbortSignal: { + prototype: AbortSignal; + new(): AbortSignal; + /** + * The **`AbortSignal.abort()`** static method returns an AbortSignal that is already set as aborted (and which does not trigger an abort event). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_static) + */ + abort(reason?: any): AbortSignal; + /** + * The **`AbortSignal.any()`** static method takes an iterable of abort signals and returns an AbortSignal. The returned abort signal is aborted when any of the input iterable abort signals are aborted. The abort reason will be set to the reason of the first signal that is aborted. If any of the given abort signals are already aborted then so will be the returned AbortSignal. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/any_static) + */ + any(signals: AbortSignal[]): AbortSignal; + /** + * The **`AbortSignal.timeout()`** static method returns an AbortSignal that will automatically abort after a specified time. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/timeout_static) + */ + timeout(milliseconds: number): AbortSignal; +}; + +/** + * The **`AbstractRange`** abstract interface is the base class upon which all DOM range types are defined. A range is an object that indicates the start and end points of a section of content within the document. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbstractRange) + */ +interface AbstractRange { + /** + * The read-only **`collapsed`** property of the AbstractRange interface returns true if the range's start position and end position are the same. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbstractRange/collapsed) + */ + readonly collapsed: boolean; + /** + * The read-only **`endContainer`** property of the AbstractRange interface returns the Node in which the end of the range is located. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbstractRange/endContainer) + */ + readonly endContainer: Node; + /** + * The **`endOffset`** property of the AbstractRange interface returns the offset into the end node of the range's end position. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbstractRange/endOffset) + */ + readonly endOffset: number; + /** + * The read-only **`startContainer`** property of the AbstractRange interface returns the Node in which the start of the range is located. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbstractRange/startContainer) + */ + readonly startContainer: Node; + /** + * The read-only **`startOffset`** property of the AbstractRange interface returns the offset into the start node of the range's start position. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbstractRange/startOffset) + */ + readonly startOffset: number; +} + +declare var AbstractRange: { + prototype: AbstractRange; + new(): AbstractRange; +}; + +interface AbstractWorkerEventMap { + "error": ErrorEvent; +} + +interface AbstractWorker { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/ServiceWorker/error_event) */ + onerror: ((this: AbstractWorker, ev: ErrorEvent) => any) | null; + addEventListener(type: K, listener: (this: AbstractWorker, ev: AbstractWorkerEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: (this: AbstractWorker, ev: AbstractWorkerEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; +} + +/** + * The **`AnalyserNode`** interface represents a node able to provide real-time frequency and time-domain analysis information. It is an AudioNode that passes the audio stream unchanged from the input to the output, but allows you to take the generated data, process it, and create audio visualizations. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnalyserNode) + */ +interface AnalyserNode extends AudioNode { + /** + * The **`fftSize`** property of the AnalyserNode interface is an unsigned long value and represents the window size in samples that is used when performing a Fast Fourier Transform (FFT) to get frequency domain data. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnalyserNode/fftSize) + */ + fftSize: number; + /** + * The **`frequencyBinCount`** read-only property of the AnalyserNode interface contains the total number of data points available to AudioContext sampleRate. This is half of the value of the AnalyserNode.fftSize. The two methods' indices have a linear relationship with the frequencies they represent, between 0 and the Nyquist frequency. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnalyserNode/frequencyBinCount) + */ + readonly frequencyBinCount: number; + /** + * The **`maxDecibels`** property of the AnalyserNode interface is a double value representing the maximum power value in the scaling range for the FFT analysis data, for conversion to unsigned byte values — basically, this specifies the maximum value for the range of results when using getByteFrequencyData(). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnalyserNode/maxDecibels) + */ + maxDecibels: number; + /** + * The **`minDecibels`** property of the AnalyserNode interface is a double value representing the minimum power value in the scaling range for the FFT analysis data, for conversion to unsigned byte values — basically, this specifies the minimum value for the range of results when using getByteFrequencyData(). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnalyserNode/minDecibels) + */ + minDecibels: number; + /** + * The **`smoothingTimeConstant`** property of the AnalyserNode interface is a double value representing the averaging constant with the last analysis frame. It's basically an average between the current buffer and the last buffer the AnalyserNode processed, and results in a much smoother set of value changes over time. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnalyserNode/smoothingTimeConstant) + */ + smoothingTimeConstant: number; + /** + * The **`getByteFrequencyData()`** method of the AnalyserNode interface copies the current frequency data into a Uint8Array (unsigned byte array) passed into it. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnalyserNode/getByteFrequencyData) + */ + getByteFrequencyData(array: Uint8Array): void; + /** + * The **`getByteTimeDomainData()`** method of the AnalyserNode Interface copies the current waveform, or time-domain, data into a Uint8Array (unsigned byte array) passed into it. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnalyserNode/getByteTimeDomainData) + */ + getByteTimeDomainData(array: Uint8Array): void; + /** + * The **`getFloatFrequencyData()`** method of the AnalyserNode Interface copies the current frequency data into a Float32Array array passed into it. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnalyserNode/getFloatFrequencyData) + */ + getFloatFrequencyData(array: Float32Array): void; + /** + * The **`getFloatTimeDomainData()`** method of the AnalyserNode Interface copies the current waveform, or time-domain, data into a Float32Array array passed into it. Each array value is a sample, the magnitude of the signal at a particular time. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnalyserNode/getFloatTimeDomainData) + */ + getFloatTimeDomainData(array: Float32Array): void; +} + +declare var AnalyserNode: { + prototype: AnalyserNode; + new(context: BaseAudioContext, options?: AnalyserOptions): AnalyserNode; +}; + +interface Animatable { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/animate) */ + animate(keyframes: Keyframe[] | PropertyIndexedKeyframes | null, options?: number | KeyframeAnimationOptions): Animation; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Element/getAnimations) */ + getAnimations(options?: GetAnimationsOptions): Animation[]; +} + +interface AnimationEventMap { + "cancel": AnimationPlaybackEvent; + "finish": AnimationPlaybackEvent; + "remove": AnimationPlaybackEvent; +} + +/** + * The **`Animation`** interface of the Web Animations API represents a single animation player and provides playback controls and a timeline for an animation node or source. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation) + */ +interface Animation extends EventTarget { + /** + * The **`Animation.currentTime`** property of the Web Animations API returns and sets the current time value of the animation in milliseconds, whether running or paused. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/currentTime) + */ + currentTime: CSSNumberish | null; + /** + * The **`Animation.effect`** property of the Web Animations API gets and sets the target effect of an animation. The target effect may be either an effect object of a type based on AnimationEffect, such as KeyframeEffect, or null. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/effect) + */ + effect: AnimationEffect | null; + /** + * The **`Animation.finished`** read-only property of the Web Animations API returns a Promise which resolves once the animation has finished playing. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/finished) + */ + readonly finished: Promise; + /** + * The **`Animation.id`** property of the Web Animations API returns or sets a string used to identify the animation. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/id) + */ + id: string; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/cancel_event) */ + oncancel: ((this: Animation, ev: AnimationPlaybackEvent) => any) | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/finish_event) */ + onfinish: ((this: Animation, ev: AnimationPlaybackEvent) => any) | null; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/remove_event) */ + onremove: ((this: Animation, ev: AnimationPlaybackEvent) => any) | null; + /** + * The **`overallProgress`** read-only property of the Animation interface returns a number between 0 and 1 indicating the animation's overall progress towards its finished state. This is the overall progress across all of the animation's iterations, not each individual iteration. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/overallProgress) + */ + readonly overallProgress: number | null; + /** + * The read-only **`Animation.pending`** property of the Web Animations API indicates whether the animation is currently waiting for an asynchronous operation such as initiating playback or pausing a running animation. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/pending) + */ + readonly pending: boolean; + /** + * The read-only **`Animation.playState`** property of the Web Animations API returns an enumerated value describing the playback state of an animation. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/playState) + */ + readonly playState: AnimationPlayState; + /** + * The **`Animation.playbackRate`** property of the Web Animations API returns or sets the playback rate of the animation. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/playbackRate) + */ + playbackRate: number; + /** + * The read-only **`Animation.ready`** property of the Web Animations API returns a Promise which resolves when the animation is ready to play. A new promise is created every time the animation enters the "pending" play state as well as when the animation is canceled, since in both of those scenarios, the animation is ready to be started again. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/ready) + */ + readonly ready: Promise; + /** + * The read-only **`Animation.replaceState`** property of the Web Animations API indicates whether the animation has been removed by the browser automatically after being replaced by another animation. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/replaceState) + */ + readonly replaceState: AnimationReplaceState; + /** + * The **`Animation.startTime`** property of the Animation interface is a double-precision floating-point value which indicates the scheduled time when an animation's playback should begin. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/startTime) + */ + startTime: CSSNumberish | null; + /** + * The **`Animation.timeline`** property of the Animation interface returns or sets the timeline associated with this animation. A timeline is a source of time values for synchronization purposes, and is an AnimationTimeline-based object. By default, the animation's timeline and the Document's timeline are the same. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/timeline) + */ + timeline: AnimationTimeline | null; + /** + * The Web Animations API's **`cancel()`** method of the Animation interface clears all KeyframeEffects caused by this animation and aborts its playback. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/cancel) + */ + cancel(): void; + /** + * The **`commitStyles()`** method of the Web Animations API's Animation interface writes the computed values of the animation's current styles into its target element's style attribute. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/commitStyles) + */ + commitStyles(): void; + /** + * The **`finish()`** method of the Web Animations API's Animation Interface sets the current playback time to the end of the animation corresponding to the current playback direction. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/finish) + */ + finish(): void; + /** + * The **`pause()`** method of the Web Animations API's Animation interface suspends playback of the animation. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/pause) + */ + pause(): void; + /** + * The **`persist()`** method of the Web Animations API's Animation interface explicitly persists an animation, preventing it from being automatically removed when it is replaced by another animation. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/persist) + */ + persist(): void; + /** + * The **`play()`** method of the Web Animations API's Animation Interface starts or resumes playing of an animation. If the animation is finished, calling play() restarts the animation, playing it from the beginning. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/play) + */ + play(): void; + /** + * The **`Animation.reverse()`** method of the Animation Interface reverses the playback direction, meaning the animation ends at its beginning. If called on an unplayed animation, the whole animation is played backwards. If called on a paused animation, the animation will continue in reverse. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/reverse) + */ + reverse(): void; + /** + * The **`updatePlaybackRate()`** method of the Web Animations API's Animation Interface sets the speed of an animation after first synchronizing its playback position. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Animation/updatePlaybackRate) + */ + updatePlaybackRate(playbackRate: number): void; + addEventListener(type: K, listener: (this: Animation, ev: AnimationEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: (this: Animation, ev: AnimationEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; +} + +declare var Animation: { + prototype: Animation; + new(effect?: AnimationEffect | null, timeline?: AnimationTimeline | null): Animation; +}; + +/** + * The **`AnimationEffect`** interface of the Web Animations API is an interface representing animation effects. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationEffect) + */ +interface AnimationEffect { + /** + * The **`getComputedTiming()`** method of the AnimationEffect interface returns the calculated timing properties for this animation effect. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationEffect/getComputedTiming) + */ + getComputedTiming(): ComputedEffectTiming; + /** + * The **`AnimationEffect.getTiming()`** method of the AnimationEffect interface returns an object containing the timing properties for the Animation Effect. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationEffect/getTiming) + */ + getTiming(): EffectTiming; + /** + * The **`updateTiming()`** method of the AnimationEffect interface updates the specified timing properties for an animation effect. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationEffect/updateTiming) + */ + updateTiming(timing?: OptionalEffectTiming): void; +} + +declare var AnimationEffect: { + prototype: AnimationEffect; + new(): AnimationEffect; +}; + +/** + * The **`AnimationEvent`** interface represents events providing information related to animations. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationEvent) + */ +interface AnimationEvent extends Event { + /** + * The **`AnimationEvent.animationName`** read-only property is a string containing the value of the animation-name CSS property associated with the transition. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationEvent/animationName) + */ + readonly animationName: string; + /** + * The **`AnimationEvent.elapsedTime`** read-only property is a float giving the amount of time the animation has been running, in seconds, when this event fired, excluding any time the animation was paused. For an animationstart event, elapsedTime is 0.0 unless there was a negative value for animation-delay, in which case the event will be fired with elapsedTime containing (-1 * delay). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationEvent/elapsedTime) + */ + readonly elapsedTime: number; + /** + * The **`AnimationEvent.pseudoElement`** read-only property is a string, starting with '::', containing the name of the pseudo-element the animation runs on. If the animation doesn't run on a pseudo-element but on the element, an empty string: ''. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationEvent/pseudoElement) + */ + readonly pseudoElement: string; +} + +declare var AnimationEvent: { + prototype: AnimationEvent; + new(type: string, animationEventInitDict?: AnimationEventInit): AnimationEvent; +}; + +interface AnimationFrameProvider { + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/DedicatedWorkerGlobalScope/cancelAnimationFrame) */ + cancelAnimationFrame(handle: number): void; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/DedicatedWorkerGlobalScope/requestAnimationFrame) */ + requestAnimationFrame(callback: FrameRequestCallback): number; +} + +/** + * The **`AnimationPlaybackEvent`** interface of the Web Animations API represents animation events. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationPlaybackEvent) + */ +interface AnimationPlaybackEvent extends Event { + /** + * The **`currentTime`** read-only property of the AnimationPlaybackEvent interface represents the current time of the animation that generated the event at the moment the event is queued. This will be unresolved if the animation was idle at the time the event was generated. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationPlaybackEvent/currentTime) + */ + readonly currentTime: CSSNumberish | null; + /** + * The **`timelineTime`** read-only property of the AnimationPlaybackEvent interface represents the time value of the animation's timeline at the moment the event is queued. This will be unresolved if the animation was not associated with a timeline at the time the event was generated or if the associated timeline was inactive. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationPlaybackEvent/timelineTime) + */ + readonly timelineTime: CSSNumberish | null; +} + +declare var AnimationPlaybackEvent: { + prototype: AnimationPlaybackEvent; + new(type: string, eventInitDict?: AnimationPlaybackEventInit): AnimationPlaybackEvent; +}; + +/** + * The **`AnimationTimeline`** interface of the Web Animations API represents the timeline of an animation. This interface exists to define timeline features, inherited by other timeline types: + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationTimeline) + */ +interface AnimationTimeline { + /** + * The **`currentTime`** read-only property of the Web Animations API's AnimationTimeline interface returns the timeline's current time in milliseconds, or null if the timeline is inactive. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationTimeline/currentTime) + */ + readonly currentTime: CSSNumberish | null; + /** + * The **`duration`** read-only property of the Web Animations API's AnimationTimeline interface returns the maximum value for this timeline or null. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AnimationTimeline/duration) + */ + readonly duration: CSSNumberish | null; +} + +declare var AnimationTimeline: { + prototype: AnimationTimeline; + new(): AnimationTimeline; +}; + +/** + * The **`Attr`** interface represents one of an element's attributes as an object. In most situations, you will directly retrieve the attribute value as a string (e.g., Element.getAttribute()), but some cases may require interacting with Attr instances (e.g., Element.getAttributeNode()). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Attr) + */ +interface Attr extends Node { + /** + * The read-only **`localName`** property of the Attr interface returns the local part of the qualified name of an attribute, that is the name of the attribute, stripped from any namespace in front of it. For example, if the qualified name is xml:lang, the returned local name is lang, if the element supports that namespace. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Attr/localName) + */ + readonly localName: string; + /** + * The read-only **`name`** property of the Attr interface returns the qualified name of an attribute, that is the name of the attribute, with the namespace prefix, if any, in front of it. For example, if the local name is lang and the namespace prefix is xml, the returned qualified name is xml:lang. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Attr/name) + */ + readonly name: string; + /** + * The read-only **`namespaceURI`** property of the Attr interface returns the namespace URI of the attribute, or null if the element is not in a namespace. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Attr/namespaceURI) + */ + readonly namespaceURI: string | null; + readonly ownerDocument: Document; + /** + * The read-only **`ownerElement`** property of the Attr interface returns the Element the attribute belongs to. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Attr/ownerElement) + */ + readonly ownerElement: Element | null; + /** + * The read-only **`prefix`** property of the Attr returns the namespace prefix of the attribute, or null if no prefix is specified. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Attr/prefix) + */ + readonly prefix: string | null; + /** + * The read-only **`specified`** property of the Attr interface always returns true. + * @deprecated + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Attr/specified) + */ + readonly specified: boolean; + /** + * The **`value`** property of the Attr interface contains the value of the attribute. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/Attr/value) + */ + value: string; + /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Node/textContent) */ + get textContent(): string; + set textContent(value: string | null); +} + +declare var Attr: { + prototype: Attr; + new(): Attr; +}; + +/** + * The **`AudioBuffer`** interface represents a short audio asset residing in memory, created from an audio file using the AudioContext.decodeAudioData() method, or from raw data using AudioContext.createBuffer(). Once put into an AudioBuffer, the audio can then be played by being passed into an AudioBufferSourceNode. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBuffer) + */ +interface AudioBuffer { + /** + * The **`duration`** property of the AudioBuffer interface returns a double representing the duration, in seconds, of the PCM data stored in the buffer. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBuffer/duration) + */ + readonly duration: number; + /** + * The **`length`** property of the AudioBuffer interface returns an integer representing the length, in sample-frames, of the PCM data stored in the buffer. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBuffer/length) + */ + readonly length: number; + /** + * The **`numberOfChannels`** property of the AudioBuffer interface returns an integer representing the number of discrete audio channels described by the PCM data stored in the buffer. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBuffer/numberOfChannels) + */ + readonly numberOfChannels: number; + /** + * The **`sampleRate`** property of the AudioBuffer interface returns a float representing the sample rate, in samples per second, of the PCM data stored in the buffer. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBuffer/sampleRate) + */ + readonly sampleRate: number; + /** + * The **`copyFromChannel()`** method of the AudioBuffer interface copies the audio sample data from the specified channel of the AudioBuffer to a specified Float32Array. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBuffer/copyFromChannel) + */ + copyFromChannel(destination: Float32Array, channelNumber: number, bufferOffset?: number): void; + /** + * The **`copyToChannel()`** method of the AudioBuffer interface copies the samples to the specified channel of the AudioBuffer, from the source array. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBuffer/copyToChannel) + */ + copyToChannel(source: Float32Array, channelNumber: number, bufferOffset?: number): void; + /** + * The **`getChannelData()`** method of the AudioBuffer Interface returns a Float32Array containing the PCM data associated with the channel, defined by the channel parameter (with 0 representing the first channel). + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBuffer/getChannelData) + */ + getChannelData(channel: number): Float32Array; +} + +declare var AudioBuffer: { + prototype: AudioBuffer; + new(options: AudioBufferOptions): AudioBuffer; +}; + +/** + * The **`AudioBufferSourceNode`** interface is an AudioScheduledSourceNode which represents an audio source consisting of in-memory audio data, stored in an AudioBuffer. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBufferSourceNode) + */ +interface AudioBufferSourceNode extends AudioScheduledSourceNode { + /** + * The **`buffer`** property of the AudioBufferSourceNode interface provides the ability to play back audio using an AudioBuffer as the source of the sound data. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBufferSourceNode/buffer) + */ + buffer: AudioBuffer | null; + /** + * The **`detune`** property of the AudioBufferSourceNode interface is a k-rate AudioParam representing detuning of oscillation in cents. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBufferSourceNode/detune) + */ + readonly detune: AudioParam; + /** + * The **`loop`** property of the AudioBufferSourceNode interface is a Boolean indicating if the audio asset must be replayed when the end of the AudioBuffer is reached. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBufferSourceNode/loop) + */ + loop: boolean; + /** + * The **`loopEnd`** property of the AudioBufferSourceNode interface specifies is a floating point number specifying, in seconds, at what offset into playing the AudioBuffer playback should loop back to the time indicated by the loopStart property. This is only used if the loop property is true. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBufferSourceNode/loopEnd) + */ + loopEnd: number; + /** + * The **`loopStart`** property of the AudioBufferSourceNode interface is a floating-point value indicating, in seconds, where in the AudioBuffer the restart of the play must happen. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBufferSourceNode/loopStart) + */ + loopStart: number; + /** + * The **`playbackRate`** property of the AudioBufferSourceNode interface Is a k-rate AudioParam that defines the speed at which the audio asset will be played. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBufferSourceNode/playbackRate) + */ + readonly playbackRate: AudioParam; + /** + * The **`start()`** method of the AudioBufferSourceNode Interface is used to schedule playback of the audio data contained in the buffer, or to begin playback immediately. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioBufferSourceNode/start) + */ + start(when?: number, offset?: number, duration?: number): void; + addEventListener(type: K, listener: (this: AudioBufferSourceNode, ev: AudioScheduledSourceNodeEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void; + removeEventListener(type: K, listener: (this: AudioBufferSourceNode, ev: AudioScheduledSourceNodeEventMap[K]) => any, options?: boolean | EventListenerOptions): void; + removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void; +} + +declare var AudioBufferSourceNode: { + prototype: AudioBufferSourceNode; + new(context: BaseAudioContext, options?: AudioBufferSourceOptions): AudioBufferSourceNode; +}; + +/** + * The **`AudioContext`** interface represents an audio-processing graph built from audio modules linked together, each represented by an AudioNode. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioContext) + */ +interface AudioContext extends BaseAudioContext { + /** + * The **`baseLatency`** read-only property of the AudioContext interface returns a double that represents the number of seconds of processing latency incurred by the AudioContext passing an audio buffer from the AudioDestinationNode — i.e., the end of the audio graph — into the host system's audio subsystem ready for playing. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioContext/baseLatency) + */ + readonly baseLatency: number; + /** + * The **`outputLatency`** read-only property of the AudioContext Interface provides an estimation of the output latency of the current audio context. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioContext/outputLatency) + */ + readonly outputLatency: number; + /** + * The **`close()`** method of the AudioContext Interface closes the audio context, releasing any system audio resources that it uses. + * + * [MDN Reference](https://developer.mozilla.org/docs/Web/API/AudioContext/close) + */ + close(): Promise; + /** + * The **`createMediaElementSource()`** method of the AudioContext Interface is used to create a new MediaElementAudioSourceNode object, given an existing HTML