diff --git a/CHANGELOG.md b/CHANGELOG.md index 0835093c..70264098 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,6 +56,12 @@ versioning and publication. browser's successful result is `undefined`, not a database. - `WebAPI.Notification.t` no longer contains a static permission field. Read that value through `WebAPI.Notification.permission`. +- Native browser-instance record construction and record-update copies are being restricted with + private record views. Reads remain direct; writes to protected mutable properties migrate to + typed setters such as `canvas->WebAPI.HTMLCanvasElement.setWidth(640)`. Options, initializers, + callback dictionaries, and plain data records remain constructible. This compile-time restriction + does not prevent a deliberate explicit structural coercion (`:>`) from a recreated public record + shape, unsafe casts, or JavaScript fabrication. For the intended installation, compiler range, runtime/browser checks, and known limitations, see the [project status](https://rescript-lang.github.io/experimental-rescript-webapi/project-status/) diff --git a/docs/content/docs/contributing/api-modelling.mdx b/docs/content/docs/contributing/api-modelling.mdx index f5119614..dcf321e7 100644 --- a/docs/content/docs/contributing/api-modelling.mdx +++ b/docs/content/docs/contributing/api-modelling.mdx @@ -58,6 +58,46 @@ Keep null and empty states explicit when they are part of the browser contract. for nullable results and variants for meaningful empty values; do not replace native results with invented defaults or unchecked casts. +## Native instance types + +Model browser-owned objects as private records at both the backing declaration and every public +record view. Keep direct field reads when useful, but expose typed native setters for writable +properties instead of asking callers to mutate a private field: + +```ReScript +let canvas = WebAPI.Document.createElement(document, "canvas")->WebAPI.HTMLCanvasElement.classify +switch canvas { +| Some(canvas) => + let width = canvas.width + canvas->WebAPI.HTMLCanvasElement.setWidth(width + 1) +| None => () +} +``` + +Use names such as `setWidth`, preserve the JavaScript property name and existing field type, and +keep throwing behavior on setters whose native operation can throw. Put shared properties on the +existing `Impl` chain so derived interfaces reuse their base setters. Do not make initializer +dictionaries, option records, callback data, or ordinary result records private merely because they +are exported beside a native interface. + +For `CSSStyleDeclaration`, keep direct reads but use its existing `setProperty` operation for +individual declarations, using CSS property names such as `"background-color"` rather than the +camel-cased IDL field name. Both `float` and `cssFloat` address the `"float"` CSS property. +Replacing the complete declaration block uses `CSSStyleDeclaration.setCssText`; do not route that +operation through `setProperty`. + +```ReScript +style->WebAPI.CSSStyleDeclaration.setProperty(~property="background-color", ~value="red") +style->WebAPI.CSSStyleDeclaration.setProperty(~property="float", ~value="left") +style->WebAPI.CSSStyleDeclaration.setCssText("background-color: blue") +``` + +Private records prevent ordinary literals and record-update copies, but ReScript currently permits +a deliberate explicit structural coercion such as `ordinaryRecord :> Native.t` when a caller +recreates the full public shape. This representation is not an unforgeable native brand; do not +describe it as preventing deliberate coercions, unsafe casts, or JavaScript fabrication. A stronger +guarantee requires an opaque representation and corresponding getter APIs. + ## Verification Add compile-only, Node runtime, or browser runtime fixtures according to the API being modeled. diff --git a/docs/content/docs/project-status.mdx b/docs/content/docs/project-status.mdx index d7528876..76a45026 100644 --- a/docs/content/docs/project-status.mdx +++ b/docs/content/docs/project-status.mdx @@ -35,9 +35,20 @@ implementation modules and internal type-holder modules are not part of that pub Bindings are maintained and corrected by hand as well as generated; the generator is not a reproducible source of truth for every checked-in binding. -Approximately 80 bindings still carry private-record TODOs; a blanket conversion is deferred -because the current compiler reports mutation warning 111. Generator reproducibility is also -follow-up work, not an alpha release gate. +Native-instance privacy is being implemented in the private-record alternative for +[#403](https://github.com/rescript-lang/experimental-rescript-webapi/issues/403). Protected +instances retain direct reads and use typed setters for supported writes; caller-owned option and +initializer records remain constructible. This is not yet complete across the package while the +implementation is under review. The private-record representation also permits a deliberate +explicit structural coercion (`:>`) from a recreated public shape, so it does not provide an +unforgeable native-instance brand. Generator reproducibility remains separate follow-up work. + +Some signatures still use `unknown` placeholders instead of a modeled native identity. In +particular, stream reader/writer types, DOM/Canvas rendering-context unions, and the performance +entry-list handle are not protected by this record policy. Input and mixed placeholders such as +stream source/sink/strategy types, request information, IDB keys, media providers, and algorithm +identifiers are also not native-instance guarantees. See the checked native-instance inventory for +the exact current disposition; these placeholders must not be counted as protected records. CI uses Node 26 for ReScript compilation and Node runtime fixtures. Its Playwright suite runs compiled Chromium regressions for the confirmed alpha contracts, including typed byte/audio diff --git a/package.json b/package.json index 8c5e3984..c28127c9 100644 --- a/package.json +++ b/package.json @@ -40,12 +40,12 @@ "test:browser": "rescript clean && npm run build && playwright test", "test:negative": "node scripts/check-consumer.mjs --negative-only", "build": "rescript", - "check:features": "node --test scripts/check-features.test.mjs && node scripts/check-features.mjs", + "check:features": "node --test scripts/check-features.test.mjs && node scripts/check-features.mjs && node scripts/check-native-instance-inventory.mjs", "check:consumer": "node scripts/check-consumer.mjs", "check:release": "node --test scripts/validate-release-artifact.test.mjs scripts/check-release.test.mjs && node scripts/check-release.mjs", "check:docs-links": "node --test scripts/check-doc-links.test.mjs scripts/check-doc-type-links.test.mjs && node scripts/check-doc-links.mjs", - "format": "rescript format && oxfmt ./tests/index.js ./tests/fixtures.json ./tests/browser/*.mjs ./scripts/negative-fixtures.json ./scripts/validate-release-artifact.mjs ./scripts/validate-release-artifact.test.mjs ./playwright.config.mjs ./astro.config.mjs ./scripts/check-consumer.mjs ./scripts/check-release.mjs ./scripts/check-release.test.mjs ./scripts/check-features.mjs ./scripts/check-features.test.mjs ./scripts/check-doc-links.mjs ./scripts/check-doc-links.test.mjs ./scripts/check-doc-type-links.test.mjs ./package.json ./README.md ./CHANGELOG.md ./docs && prettier --write .github/workflows/*.yml ./docs/pages", - "format:check": "rescript format --check && oxfmt ./tests/index.js ./tests/fixtures.json ./tests/browser/*.mjs ./scripts/negative-fixtures.json ./scripts/validate-release-artifact.mjs ./scripts/validate-release-artifact.test.mjs ./playwright.config.mjs ./astro.config.mjs ./scripts/check-consumer.mjs ./scripts/check-release.mjs ./scripts/check-release.test.mjs ./scripts/check-features.mjs ./scripts/check-features.test.mjs ./scripts/check-doc-links.mjs ./scripts/check-doc-links.test.mjs ./scripts/check-doc-type-links.test.mjs ./package.json ./README.md ./CHANGELOG.md ./docs --check && prettier --check .github/workflows/*.yml ./docs/pages", + "format": "rescript format && oxfmt ./tests/index.js ./tests/fixtures.json ./tests/browser/*.mjs ./scripts/negative-fixtures.json ./scripts/native-instance-inventory.json ./scripts/check-native-instance-inventory.mjs ./scripts/validate-release-artifact.mjs ./scripts/validate-release-artifact.test.mjs ./playwright.config.mjs ./astro.config.mjs ./scripts/check-consumer.mjs ./scripts/check-release.mjs ./scripts/check-release.test.mjs ./scripts/check-features.mjs ./scripts/check-features.test.mjs ./scripts/check-doc-links.mjs ./scripts/check-doc-links.test.mjs ./scripts/check-doc-type-links.test.mjs ./package.json ./README.md ./CHANGELOG.md ./docs && prettier --write .github/workflows/*.yml ./docs/pages", + "format:check": "rescript format --check && oxfmt ./tests/index.js ./tests/fixtures.json ./tests/browser/*.mjs ./scripts/negative-fixtures.json ./scripts/native-instance-inventory.json ./scripts/check-native-instance-inventory.mjs ./scripts/validate-release-artifact.mjs ./scripts/validate-release-artifact.test.mjs ./playwright.config.mjs ./astro.config.mjs ./scripts/check-consumer.mjs ./scripts/check-release.mjs ./scripts/check-release.test.mjs ./scripts/check-features.mjs ./scripts/check-features.test.mjs ./scripts/check-doc-links.mjs ./scripts/check-doc-links.test.mjs ./scripts/check-doc-type-links.test.mjs ./package.json ./README.md ./CHANGELOG.md ./docs --check && prettier --check .github/workflows/*.yml ./docs/pages", "docs": "astro dev", "prebuild:docs": "node docs/llm.js", "build:docs": "astro build" diff --git a/rescript.json b/rescript.json index 8ac1cd9c..41c5e350 100644 --- a/rescript.json +++ b/rescript.json @@ -192,8 +192,11 @@ "ImageBitmapRenderingContext", "ImageData", "OffscreenCanvas", + "OffscreenCanvasRenderingContext2D", "Path2D", "VideoColorSpace", + "WebGLRenderingContext", + "WebGL2RenderingContext", "VideoFrame" ] }, diff --git a/scripts/check-consumer.mjs b/scripts/check-consumer.mjs index 9f1ec437..97c63f4c 100644 --- a/scripts/check-consumer.mjs +++ b/scripts/check-consumer.mjs @@ -67,7 +67,7 @@ const writeProject = (directory, sourceFile, features) => { dependencies: features === null ? ["@rescript/webapi"] : [{ name: "@rescript/webapi", features }], "experimental-features": { LetUnwrap: true }, - warnings: { error: "+8" }, + warnings: { error: "+8+111" }, "package-specs": { module: "esmodule", "in-source": true }, }, null, @@ -80,6 +80,29 @@ const writeProject = (directory, sourceFile, features) => { const compilerCli = (directory) => path.join(directory, "node_modules", "rescript", "cli", "rescript.js"); +const verifyPrivateRecordRelaxation = (directory) => { + const backingFile = path.join( + directory, + "node_modules", + "@rescript", + "webapi", + "src/core/DOM.res", + ); + const source = readFileSync(backingFile, "utf8"); + const privateDeclaration = "type domTokenList = private {"; + if (source.split(privateDeclaration).length !== 2) { + throw new Error("Expected exactly one private DOM.domTokenList backing declaration."); + } + writeFileSync(backingFile, source.replace(privateDeclaration, "type domTokenList = {")); + writeProject(directory, "scripts/fixtures/negative/DOMTokenListBackingRouteIsPrivate.res", [ + "WebAPI.DOM", + ]); + compile(directory); + console.log( + "[ok] DOM backing-route fixture compiles when only its privacy restriction is relaxed", + ); +}; + const compile = (directory, expectedFailure = null) => { const cli = compilerCli(directory); const clean = spawnSync(process.execPath, [cli, "clean"], { cwd: directory }); @@ -193,6 +216,16 @@ const checkConsumers = async () => { fixture: "scripts/consumer-concrete-types.res", features: null, }, + { + name: "native setters and constructible request options", + fixture: "scripts/consumer-native-setters.res", + features: null, + }, + { + name: "documented private-record coercion limitation", + fixture: "scripts/consumer-private-coercion-caveat.res", + features: null, + }, { name: "Fetch-only", fixture: "scripts/consumer-fetch.res", @@ -247,6 +280,7 @@ const checkConsumers = async () => { compile(projectDirectory, negativeCase.expectedDiagnostic); console.log(`[ok] expected rejection: ${negativeCase.name}`); } + verifyPrivateRecordRelaxation(projectDirectory); console.log(`[ok] tarball contents (${packagedFiles.length} files)`); } finally { rmSync(tempDirectory, { recursive: true, force: true }); diff --git a/scripts/check-native-instance-inventory.mjs b/scripts/check-native-instance-inventory.mjs new file mode 100644 index 00000000..edfe5df7 --- /dev/null +++ b/scripts/check-native-instance-inventory.mjs @@ -0,0 +1,420 @@ +import { createHash } from "node:crypto"; +import { readFileSync, readdirSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); +const inventoryPath = path.join(root, "scripts/native-instance-inventory.json"); +const inventory = JSON.parse(readFileSync(inventoryPath, "utf8")); +const config = JSON.parse(readFileSync(path.join(root, "rescript.json"), "utf8")); +const failures = []; + +const stripComments = (source) => + source + .replace(/\/\*[\s\S]*?\*\//g, (comment) => "\n".repeat(comment.split("\n").length - 1)) + .replace(/\/\/[^\n]*/g, ""); + +const sourceFiles = (directory) => + readdirSync(directory, { withFileTypes: true }).flatMap((entry) => { + const entryPath = path.join(directory, entry.name); + return entry.isDirectory() + ? sourceFiles(entryPath) + : entry.isFile() && /\.(?:res|resi)$/.test(entry.name) + ? [entryPath] + : []; + }); + +const sourcePaths = sourceFiles(path.join(root, "src")); +const publicModules = config.sources.flatMap((source) => source.public ?? []); +const manifestFingerprint = createHash("sha256") + .update( + config.sources + .flatMap((source) => (source.public ?? []).map((module) => `${source.dir}/${module}`)) + .sort() + .join("\n"), + ) + .digest("hex"); +const declarationPattern = + /^(?:@[A-Za-z0-9_.]+(?:\([^\n]*?\))?\s+)*(?:type|and)\s+(?:rec\s+)?[A-Za-z_]\w*(?:<[^>\n]*>)?\s*=/gm; +const declarationNames = []; +const declarationCount = sourcePaths.reduce((count, filePath) => { + const source = stripComments(readFileSync(filePath, "utf8")); + const matches = [...source.matchAll(declarationPattern)]; + for (const match of matches) { + const name = match[0].match(/\b(?:type|and)\s+(?:rec\s+)?([A-Za-z_]\w*)/)?.[1]; + if (name !== undefined) { + declarationNames.push(`${path.relative(root, filePath)}:${name}`); + } + } + return count + matches.length; +}, 0); +const declarationFingerprint = createHash("sha256") + .update(declarationNames.sort().join("\n")) + .digest("hex"); + +if (publicModules.length !== inventory.publicModules.expectedCount) { + failures.push( + `Expected ${inventory.publicModules.expectedCount} public modules, found ${publicModules.length}. Update the native-instance inventory for added or removed modules.`, + ); +} + +if (manifestFingerprint !== inventory.publicModules.manifestSha256) { + failures.push( + "The public module manifest changed. Review added or removed public type routes and update the native-instance inventory.", + ); +} + +if (declarationCount !== inventory.sourceTypeDeclarationCount) { + failures.push( + `Expected ${inventory.sourceTypeDeclarationCount} source type declarations, found ${declarationCount}. Review and update the native-instance inventory for added or removed types.`, + ); +} + +if (declarationFingerprint !== inventory.sourceTypeDeclarationSha256) { + failures.push( + "The source type declaration inventory changed. Review every added, removed, or renamed type and update native/data classifications.", + ); +} + +const declarationIsPrivate = (file, name) => { + const filePath = path.join(root, file); + if (!filePath.startsWith(`${path.join(root, "src")}${path.sep}`)) { + failures.push(`Inventory path is outside src/: ${file}`); + return false; + } + let source; + try { + source = stripComments(readFileSync(filePath, "utf8")); + } catch { + failures.push(`Inventory source file does not exist: ${file}`); + return false; + } + + const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const pattern = new RegExp( + `^(?:@[A-Za-z0-9_.]+(?:\\([^\\n]*?\\))?\\s+)*(?:type|and)\\s+(?:rec\\s+)?${escapedName}(?:<[^>\\n]*>)?\\s*=`, + "gm", + ); + const matches = [...source.matchAll(pattern)]; + if (matches.length !== 1) { + failures.push(`${file} must declare ${name} exactly once; found ${matches.length}.`); + return false; + } + + const declaration = source.slice(matches[0].index + matches[0][0].length); + const nextDeclaration = declaration.search( + /^(?:@[A-Za-z0-9_.]+(?:\([^\n]*?\))?\s+)*(?:type|and|let|external|module|include)\b/m, + ); + const headerText = + nextDeclaration === -1 ? declaration.slice(0, 1024) : declaration.slice(0, nextDeclaration); + const firstRecord = headerText.indexOf("{"); + const header = firstRecord === -1 ? headerText : headerText.slice(0, firstRecord); + return /\bprivate\b/.test(header); +}; + +const declarationIsConstructible = (file, name) => { + const filePath = path.join(root, file); + if (!filePath.startsWith(`${path.join(root, "src")}${path.sep}`)) { + failures.push(`Inventory path is outside src/: ${file}`); + return false; + } + let source; + try { + source = stripComments(readFileSync(filePath, "utf8")); + } catch { + failures.push(`Inventory source file does not exist: ${file}`); + return false; + } + + const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const pattern = new RegExp( + `^\\s*(?:@[A-Za-z0-9_.]+(?:\\([^\\n]*?\\))?\\s+)*(?:type|and)\\s+(?:rec\\s+)?${escapedName}(?:<[^>\\n]*>)?\\s*=`, + "gm", + ); + const matches = [...source.matchAll(pattern)]; + if (matches.length !== 1) { + failures.push( + `${file} must declare constructible exception ${name} exactly once; found ${matches.length}.`, + ); + return false; + } + + const declaration = source.slice(matches[0].index + matches[0][0].length); + const nextDeclaration = declaration.search( + /^(?:@[A-Za-z0-9_.]+(?:\([^\n]*?\))?\s+)*(?:type|and|let|external|module|include)\b/m, + ); + const headerText = + nextDeclaration === -1 ? declaration.slice(0, 1024) : declaration.slice(0, nextDeclaration); + const firstRecord = headerText.indexOf("{"); + const header = firstRecord === -1 ? headerText : headerText.slice(0, firstRecord); + if (/\bprivate\b/.test(header)) { + failures.push(`Constructible exception ${file}:${name} must remain public.`); + return false; + } + return true; +}; + +const verifyModuleAliases = (aliasInventory, label) => { + for (const [file, aliases] of Object.entries(aliasInventory)) { + const filePath = path.join(root, file); + let source; + try { + source = stripComments(readFileSync(filePath, "utf8")); + } catch { + failures.push(`${label} owner file does not exist: ${file}`); + continue; + } + for (const [alias, target] of Object.entries(aliases)) { + const pattern = new RegExp(`\\bmodule\\s+${alias}\\s*=\\s*${target}\\b`); + if (!pattern.test(source)) { + failures.push(`${label} route ${file}:${alias} -> ${target} is missing.`); + } + } + } +}; + +const verifyPublicInstanceTypes = () => { + for (const source of config.sources) { + for (const module of source.public ?? []) { + const file = path.join(source.dir, `${module}.res`); + let contents; + try { + contents = stripComments(readFileSync(path.join(root, file), "utf8")); + } catch { + failures.push(`Public module source file does not exist: ${file}`); + continue; + } + const pattern = + /^(?:@[A-Za-z0-9_.]+(?:\([^\n]*?\))?\s+)*(?:type|and)\s+(?:rec\s+)?t(?:<[^>\n]*>)?\s*=/gm; + const matches = [...contents.matchAll(pattern)]; + if (matches.length === 0) continue; + if (matches.length !== 1) { + failures.push( + `Public module ${file} declares t ${matches.length} times; review its native type views.`, + ); + continue; + } + const declaration = contents.slice(matches[0].index + matches[0][0].length); + const nextDeclaration = declaration.search( + /^(?:@[A-Za-z0-9_.]+(?:\([^\n]*?\))?\s+)*(?:type|and|let|external|module|include)\b/m, + ); + const headerText = + nextDeclaration === -1 ? declaration.slice(0, 1024) : declaration.slice(0, nextDeclaration); + const firstRecord = headerText.indexOf("{"); + if (firstRecord === -1) continue; + const header = headerText.slice(0, firstRecord); + if (!/\bprivate\b/.test(header)) { + failures.push(`Public module ${file} exposes a constructible record t.`); + } + } + } +}; + +const setterNames = (file) => { + const source = stripComments(readFileSync(path.join(root, file), "utf8")); + return new Set( + [...source.matchAll(/@set[\s\S]{0,160}?external\s+(set[A-Za-z0-9_]*)\b/g)].map( + (match) => match[1], + ), + ); +}; + +const methodNameForField = (field) => { + const fieldAliases = { + as_: "setAs", + open_: "setOpen", + type_: "setType", + }; + return fieldAliases[field] ?? `set${field[0].toUpperCase()}${field.slice(1)}`; +}; + +const verifySetterNames = (file, fields, overrides = {}) => { + const available = setterNames(file); + for (const field of fields) { + const methodName = overrides[field] ?? methodNameForField(field); + if (!available.has(methodName)) { + failures.push(`Expected typed native setter ${file}:${methodName} for ${field}.`); + } + } +}; + +for (const [file, fields] of Object.entries( + inventory.writablePropertyInventory.sharedImplementations, +)) { + verifySetterNames(file, fields); +} + +for (const [module, fields] of Object.entries(inventory.writablePropertyInventory.htmlLocal)) { + verifySetterNames(`src/html/${module}.res`, fields); +} + +verifySetterNames( + "src/url/URL.res", + inventory.writablePropertyInventory.url["src/url/UrlTypes.res"], +); +verifySetterNames( + "src/window/Location.res", + inventory.writablePropertyInventory.location["src/window/Location.res"], +); +verifySetterNames( + "src/window/History.res", + inventory.writablePropertyInventory.history["src/window/HistoryTypes.res"], +); +verifySetterNames( + "src/animation/Animation.res", + inventory.writablePropertyInventory.animation["Animation.t"], +); +verifySetterNames("src/cssom/MediaList.res", ["mediaText"]); +verifySetterNames("src/cssom/StyleSheet.res", ["disabled"]); +verifySetterNames("src/cssom/StyleSheet.res", ["disabled"], { + disabled: "setCSSStyleSheetDisabled", +}); +verifySetterNames("src/cssom/StyleSheet.res", ["cssText"], { + cssText: "setCSSRuleCssText", +}); +verifySetterNames( + "src/css-font-loading/FontFace.res", + inventory.writablePropertyInventory.cssAndFonts["CssFontLoadingTypes.fontFace"], +); +verifySetterNames("src/svg/SVGElement.res", ["baseVal"], { + baseVal: "setAnimatedStringBaseVal", +}); +verifySetterNames("src/svg/SVGElement.res", ["nonce", "autofocus", "tabIndex"]); +verifySetterNames("src/canvas/HTMLCanvasElement.res", ["width", "height"]); +verifySetterNames("src/canvas/OffscreenCanvas.res", ["width", "height"]); +verifySetterNames( + "src/canvas/OffscreenCanvasRenderingContext2D.res", + inventory.writablePropertyInventory.svgAndCanvas["CanvasTypes.offscreenCanvasRenderingContext2D"], +); +verifySetterNames( + "src/canvas/WebGLRenderingContext.res", + ["drawingBufferColorSpace", "unpackColorSpace"], + { + drawingBufferColorSpace: "setDrawingBufferColorSpace", + unpackColorSpace: "setUnpackColorSpace", + }, +); +verifySetterNames( + "src/canvas/WebGL2RenderingContext.res", + ["drawingBufferColorSpace", "unpackColorSpace"], + { + drawingBufferColorSpace: "setDrawingBufferColorSpace", + unpackColorSpace: "setUnpackColorSpace", + }, +); +verifySetterNames( + "src/web-audio/AudioNode.res", + inventory.writablePropertyInventory.webAudioBase["AudioNode.Impl"], +); + +const audioSetterModules = { + audioParam: "AudioParam", + biquadFilterNode: "BiquadFilterNode", + audioBufferSourceNode: "AudioBufferSourceNode", + convolverNode: "ConvolverNode", + oscillatorNode: "OscillatorNode", + pannerNode: "PannerNode", + analyserNode: "AnalyserNode", + waveShaperNode: "WaveShaperNode", +}; +for (const [nativeType, fields] of Object.entries(inventory.writablePropertyInventory.audioLocal)) { + const file = `src/web-audio/${audioSetterModules[nativeType]}.res`; + if (file.endsWith("undefined.res")) { + failures.push(`No setter owner is configured for Web Audio type ${nativeType}.`); + } else { + verifySetterNames(file, fields); + } +} + +verifySetterNames( + "src/media/DomHTMLMediaElement.res", + inventory.writablePropertyInventory.mediaElementBase, +); +const mediaSetterModules = { + HTMLVideoElement: "HTMLVideoElement", + HTMLTrackElement: "HTMLTrackElement", + MediaStreamTrack: "MediaCaptureAndStreamsTypes", + MediaMetadata: "MediaSessionTypes", + MediaSession: "MediaSessionTypes", + TextTrackCue: "WebVttTypes", + TextTrack: "WebVttTypes", + SpeechSynthesisUtterance: "WebSpeechTypes", +}; +for (const [nativeType, fields] of Object.entries(inventory.writablePropertyInventory.mediaLocal)) { + const file = `src/media/${mediaSetterModules[nativeType]}.res`; + const overrides = + nativeType === "TextTrackCue" + ? { + id: "setCueId", + startTime: "setCueStartTime", + endTime: "setCueEndTime", + pauseOnExit: "setCuePauseOnExit", + } + : nativeType === "TextTrack" + ? { mode: "setTextTrackMode" } + : {}; + verifySetterNames(file, fields, overrides); +} + +verifySetterNames("src/ui-events/DataTransfer.res", ["dropEffect", "effectAllowed"]); +verifySetterNames("src/storage/IDBObjectStore.res", ["name"]); +verifySetterNames("src/storage/IDBIndex.res", ["name"]); +verifySetterNames("src/messaging/WebSocket.res", ["binaryType"]); + +verifyModuleAliases(inventory.publicTypesModuleAliases, "Public Types alias"); +verifyModuleAliases(inventory.publicModuleAliases, "Public module alias"); +verifyPublicInstanceTypes(); + +for (const [file, policy] of Object.entries(inventory.largeUniformWritableFamilies)) { + const source = readFileSync(path.join(root, file), "utf8"); + const propertyCount = [...source.matchAll(/^\s*mutable\s+[A-Za-z_]\w*\s*:\s*string\b/gm)].length; + if (propertyCount !== policy.nativeMutableStringFieldCount) { + failures.push( + `${file} has ${propertyCount} mutable string fields; inventory expects ${policy.nativeMutableStringFieldCount}.`, + ); + } + if (!/\bexternal\s+setProperty\b/.test(source) || !/\bexternal\s+setCssText\b/.test(source)) { + failures.push(`${file} must keep its setProperty and setCssText write paths.`); + } +} + +for (const [file, names] of Object.entries(inventory.nativeBackings)) { + for (const name of names) { + if (!declarationIsPrivate(file, name)) { + failures.push(`Native backing ${file}:${name} must be private.`); + } + } +} + +for (const [file, names] of Object.entries(inventory.publicRecordViews)) { + for (const name of names) { + if (!declarationIsPrivate(file, name)) { + failures.push(`Public native view ${file}:${name} must preserve privacy.`); + } + } +} + +for (const [file, names] of Object.entries(inventory.constructibleRecordExceptions)) { + for (const name of names) { + declarationIsConstructible(file, name); + } +} + +if (failures.length > 0) { + console.error(failures.map((failure) => `- ${failure}`).join("\n")); + process.exitCode = 1; +} else { + const protectedCount = Object.values(inventory.nativeBackings).flat().length; + const viewCount = Object.values(inventory.publicRecordViews).flat().length; + const exceptionCount = Object.values(inventory.constructibleRecordExceptions).flat().length; + console.log( + `Verified ${publicModules.length} public modules, ${declarationCount} inventoried type declarations, ${protectedCount} private native backings, ${viewCount} private public views, and ${exceptionCount} constructible record exceptions.`, + ); + console.log( + `Explicit unprotected unknown-backed native handles: ${inventory.unknownBackedDispositions.notProtectedNativeHandles.join(", ")}.`, + ); + console.log(`Abstract native types retained: ${inventory.abstractNativeTypes.join(", ")}.`); + console.log( + `Unmodeled input/mixed unknown placeholders: ${inventory.unknownBackedDispositions.unmodeledInputOrMixedPlaceholders.join(", ")}.`, + ); +} diff --git a/scripts/consumer-concrete-types.res b/scripts/consumer-concrete-types.res index c1aee495..3e265704 100644 --- a/scripts/consumer-concrete-types.res +++ b/scripts/consumer-concrete-types.res @@ -101,6 +101,6 @@ let worklet: WebAPI.Worklet.t => WebAPI.Worklet.t = value => value let makePublicUrl = () => { let url: WebAPI.URL.t = WebAPI.URL.make(~url="https://example.invalid") - url.href = "https://example.com" + url->WebAPI.URL.setHref("https://example.com") url.href } diff --git a/scripts/consumer-native-setters.res b/scripts/consumer-native-setters.res new file mode 100644 index 00000000..14b771d6 --- /dev/null +++ b/scripts/consumer-native-setters.res @@ -0,0 +1,268 @@ +let requestInit: WebAPI.Request.requestInit = {method: "POST"} +let responseInit: WebAPI.Response.responseInit = {status: 201} +let request = WebAPI.Request.fromURL("https://example.invalid", ~init=requestInit) +let response = WebAPI.Response.fromString("ok", ~init=responseInit) +let responseStatus = response.status + +module NodeBindings = WebAPI.Node.Impl({type t = WebAPI.Node.t}) +let setNodeText = (node: WebAPI.Node.t) => { + node->NodeBindings.setTextContent(Null.Value("updated")) + node.textContent +} + +module ElementBindings = WebAPI.Element.Impl({type t = WebAPI.Element.t}) +let setElementScroll = (element: WebAPI.Element.t) => { + element->ElementBindings.setScrollTop(12.) + element.scrollTop +} + +let setCanvasSize = (canvas: WebAPI.HTMLCanvasElement.t) => { + canvas->WebAPI.HTMLCanvasElement.setWidth(640) + canvas->WebAPI.HTMLCanvasElement.setHeight(480) + (canvas.width, canvas.height) +} + +let setCanvasInheritedTitle = (canvas: WebAPI.HTMLCanvasElement.t) => { + canvas->WebAPI.HTMLCanvasElement.setTitle("example canvas") + canvas.title +} + +let setInputProperties = (input: WebAPI.HTMLInputElement.t) => { + input->WebAPI.HTMLInputElement.setType("text") + input->WebAPI.HTMLInputElement.setValue("ready") + input->WebAPI.HTMLInputElement.setSelectionStart(Null.Value(0)) + input->WebAPI.HTMLInputElement.setSelectionEnd(Null.Value(5)) + (input.type_, input.value, input.selectionStart, input.selectionEnd) +} + +let setButtonProperties = (button: WebAPI.HTMLButtonElement.t) => { + button->WebAPI.HTMLButtonElement.setDisabled(false) + button->WebAPI.HTMLButtonElement.setType("submit") + button->WebAPI.HTMLButtonElement.setValue("send") + (button.disabled, button.type_, button.value) +} + +let setTemplateProperties = (template: WebAPI.HTMLTemplateElement.t) => { + template->WebAPI.HTMLTemplateElement.setShadowRootMode(template.shadowRootMode) + template->WebAPI.HTMLTemplateElement.setShadowRootDelegatesFocus( + template.shadowRootDelegatesFocus, + ) + template->WebAPI.HTMLTemplateElement.setShadowRootClonable(template.shadowRootClonable) + template->WebAPI.HTMLTemplateElement.setShadowRootSerializable(template.shadowRootSerializable) +} + +let setTableProperties = (table: WebAPI.HTMLTableElement.t) => { + table->WebAPI.HTMLTableElement.setCaption(table.caption) + table->WebAPI.HTMLTableElement.setTHead(table.tHead) + table->WebAPI.HTMLTableElement.setTFoot(table.tFoot) +} + +let setOffscreen2D = () => { + let canvas = WebAPI.OffscreenCanvas.make(~width=1, ~height=1) + switch canvas->WebAPI.OffscreenCanvas.getContext2D { + | Null => () + | Value(context) => + context->WebAPI.OffscreenCanvasRenderingContext2D.setGlobalAlpha(0.5) + } +} + +let setOffscreenWebGL = () => { + let canvas = WebAPI.OffscreenCanvas.make(~width=1, ~height=1) + switch canvas->WebAPI.OffscreenCanvas.getContextWebGL { + | Null => () + | Value(context) => + context->WebAPI.WebGLRenderingContext.setDrawingBufferColorSpace(context.drawingBufferColorSpace) + } +} + +let setOffscreenWebGL2 = () => { + let canvas = WebAPI.OffscreenCanvas.make(~width=1, ~height=1) + switch canvas->WebAPI.OffscreenCanvas.getContextWebGL2 { + | Null => () + | Value(context) => + context->WebAPI.WebGL2RenderingContext.setUnpackColorSpace(context.unpackColorSpace) + } +} + +let setSvgStringValue = (element: WebAPI.SVGElement.t) => { + element.className->WebAPI.SVGElement.setAnimatedStringBaseVal("graphics") + element.className.baseVal +} + +module AudioNodeBindings = WebAPI.AudioNode.Impl({type t = WebAPI.AudioNode.t}) +let setAudioNodeChannels = (node: WebAPI.AudioNode.t) => { + node->AudioNodeBindings.setChannelCount(2) + node->AudioNodeBindings.setChannelCountMode(node.channelCountMode) + node->AudioNodeBindings.setChannelInterpretation(node.channelInterpretation) +} + +let setAudioFactoryProperties = () => { + let context = WebAPI.AudioContext.make() + let filter = context->WebAPI.AudioContext.createBiquadFilter + filter->WebAPI.BiquadFilterNode.setType(filter.type_) + let gain = context->WebAPI.AudioContext.createGain + gain.gain->WebAPI.AudioParam.setValue(gain.gain.value) +} + +let setAudioNodeLocalProperties = () => { + let context = WebAPI.AudioContext.make() + let source = context->WebAPI.AudioContext.createBufferSource + source->WebAPI.AudioBufferSourceNode.setBuffer(source.buffer) + source->WebAPI.AudioBufferSourceNode.setLoop(source.loop) + source->WebAPI.AudioBufferSourceNode.setLoopStart(source.loopStart) + source->WebAPI.AudioBufferSourceNode.setLoopEnd(source.loopEnd) + let convolver = context->WebAPI.AudioContext.createConvolver + convolver->WebAPI.ConvolverNode.setBuffer(convolver.buffer) + convolver->WebAPI.ConvolverNode.setNormalize(convolver.normalize) + let oscillator = context->WebAPI.AudioContext.createOscillator + oscillator->WebAPI.OscillatorNode.setType(oscillator.type_) + let panner = context->WebAPI.AudioContext.createPanner + panner->WebAPI.PannerNode.setPanningModel(panner.panningModel) + panner->WebAPI.PannerNode.setDistanceModel(panner.distanceModel) + panner->WebAPI.PannerNode.setRefDistance(panner.refDistance) + panner->WebAPI.PannerNode.setMaxDistance(panner.maxDistance) + panner->WebAPI.PannerNode.setRolloffFactor(panner.rolloffFactor) + panner->WebAPI.PannerNode.setConeInnerAngle(panner.coneInnerAngle) + panner->WebAPI.PannerNode.setConeOuterAngle(panner.coneOuterAngle) + panner->WebAPI.PannerNode.setConeOuterGain(panner.coneOuterGain) + let analyser = context->WebAPI.AudioContext.createAnalyser + analyser->WebAPI.AnalyserNode.setFftSize(analyser.fftSize) + analyser->WebAPI.AnalyserNode.setMinDecibels(analyser.minDecibels) + analyser->WebAPI.AnalyserNode.setMaxDecibels(analyser.maxDecibels) + analyser->WebAPI.AnalyserNode.setSmoothingTimeConstant(analyser.smoothingTimeConstant) + let waveShaper = context->WebAPI.AudioContext.createWaveShaper + waveShaper->WebAPI.WaveShaperNode.setCurve(waveShaper.curve) + waveShaper->WebAPI.WaveShaperNode.setOversample(waveShaper.oversample) +} + +let setTokenListValue = (tokens: WebAPI.DOMTokenList.t) => + { + tokens->WebAPI.DOMTokenList.setValue("active") + tokens.value + } + +let setDataTransferEffects = (transfer: WebAPI.DataTransfer.t) => { + transfer->WebAPI.DataTransfer.setDropEffect(transfer.dropEffect) + transfer->WebAPI.DataTransfer.setEffectAllowed(transfer.effectAllowed) + (transfer.dropEffect, transfer.effectAllowed) +} + +let setCssDeclaration = (style: WebAPI.CSSStyleDeclaration.t) => { + style->WebAPI.CSSStyleDeclaration.setProperty(~property="background-color", ~value="red") + let backgroundColor = style.backgroundColor + style->WebAPI.CSSStyleDeclaration.setCssText("background-color: blue") + backgroundColor +} + +let setUrlFields = (url: WebAPI.URL.t) => { + url->WebAPI.URL.setHref("https://example.invalid/path") + url->WebAPI.URL.setProtocol("https:") + url->WebAPI.URL.setUsername("user") + url->WebAPI.URL.setPassword("secret") + url->WebAPI.URL.setHost("example.invalid") + url->WebAPI.URL.setHostname("example.invalid") + url->WebAPI.URL.setPort("443") + url->WebAPI.URL.setPathname("/path") + url->WebAPI.URL.setSearch("?mode=test") + url->WebAPI.URL.setHash("#section") + url.href +} + +let setLocationFields = (location: WebAPI.Location.t) => { + location->WebAPI.Location.setHref("https://example.invalid/") + location->WebAPI.Location.setProtocol(location.protocol) + location->WebAPI.Location.setHost(location.host) + location->WebAPI.Location.setHostname(location.hostname) + location->WebAPI.Location.setPort(location.port) + location->WebAPI.Location.setPathname(location.pathname) + location->WebAPI.Location.setSearch(location.search) + location->WebAPI.Location.setHash(location.hash) + location.href +} + +let setHistoryRestoration = (history: WebAPI.History.t) => + history->WebAPI.History.setScrollRestoration(history.scrollRestoration) + +let setMediaListText = (media: WebAPI.MediaList.t) => { + media->WebAPI.MediaList.setMediaText(media.mediaText) + media.mediaText +} + +let setFontFaceProperties = (fontFace: WebAPI.FontFace.t) => { + fontFace->WebAPI.FontFace.setFamily(fontFace.family) + fontFace->WebAPI.FontFace.setStyle(fontFace.style) + fontFace->WebAPI.FontFace.setWeight(fontFace.weight) + fontFace->WebAPI.FontFace.setStretch(fontFace.stretch) + fontFace->WebAPI.FontFace.setUnicodeRange(fontFace.unicodeRange) + fontFace->WebAPI.FontFace.setFeatureSettings(fontFace.featureSettings) + fontFace->WebAPI.FontFace.setDisplay(fontFace.display) + fontFace->WebAPI.FontFace.setAscentOverride(fontFace.ascentOverride) + fontFace->WebAPI.FontFace.setDescentOverride(fontFace.descentOverride) + fontFace->WebAPI.FontFace.setLineGapOverride(fontFace.lineGapOverride) +} + +let setAnimationProperties = (animation: WebAPI.Animation.t) => { + animation->WebAPI.Animation.setId(animation.id) + animation->WebAPI.Animation.setEffect(animation.effect) + animation->WebAPI.Animation.setTimeline(animation.timeline) + animation->WebAPI.Animation.setPlaybackRate(animation.playbackRate) + animation->WebAPI.Animation.setStartTime(animation.startTime) + animation->WebAPI.Animation.setCurrentTime(animation.currentTime) +} + +let setStylesheetProperties = ( + sheet: WebAPI.StyleSheet.t, + cssStyleSheet: WebAPI.StyleSheet.cssStyleSheet, + cssRule: WebAPI.StyleSheet.cssRule, +) => { + sheet->WebAPI.StyleSheet.setDisabled(sheet.disabled) + cssStyleSheet->WebAPI.StyleSheet.setCSSStyleSheetDisabled(cssStyleSheet.disabled) + cssRule->WebAPI.StyleSheet.setCSSRuleCssText(cssRule.cssText) +} + +let setSocketBinaryType = (socket: WebAPI.WebSocket.t) => + socket->WebAPI.WebSocket.setBinaryType(socket.binaryType) + +let setIndexedDBNames = ( + store: WebAPI.IDBObjectStore.t, + index: WebAPI.IDBIndex.t, +) => { + store->WebAPI.IDBObjectStore.setName("records") + index->WebAPI.IDBIndex.setName("by-name") +} + +let asBaseElement = (canvas: WebAPI.HTMLCanvasElement.t) => + WebAPI.HTMLCanvasElement.asElement(canvas) + +let _ = ( + request, + responseStatus, + setNodeText, + setElementScroll, + setCanvasSize, + setCanvasInheritedTitle, + setInputProperties, + setButtonProperties, + setTemplateProperties, + setTableProperties, + setOffscreen2D, + setOffscreenWebGL, + setOffscreenWebGL2, + setSvgStringValue, + setAudioNodeChannels, + setAudioFactoryProperties, + setAudioNodeLocalProperties, + setTokenListValue, + setDataTransferEffects, + setCssDeclaration, + setUrlFields, + setLocationFields, + setHistoryRestoration, + setMediaListText, + setFontFaceProperties, + setAnimationProperties, + setStylesheetProperties, + setSocketBinaryType, + setIndexedDBNames, + asBaseElement, +) diff --git a/scripts/consumer-private-coercion-caveat.res b/scripts/consumer-private-coercion-caveat.res new file mode 100644 index 00000000..cb0c33f3 --- /dev/null +++ b/scripts/consumer-private-coercion-caveat.res @@ -0,0 +1,18 @@ +type responseShape = {...WebAPI.Response.t} + +let deliberatelyForgeResponse = (original: WebAPI.Response.t) => { + let ordinaryRecord: responseShape = { + type_: original.type_, + url: "", + redirected: false, + status: 200, + ok: true, + statusText: "OK", + headers: original.headers, + body: Null, + bodyUsed: false, + } + (ordinaryRecord :> WebAPI.Response.t) +} + +let _ = deliberatelyForgeResponse diff --git a/scripts/fixtures/negative/AudioNodeRecordUpdateIsPrivate.res b/scripts/fixtures/negative/AudioNodeRecordUpdateIsPrivate.res new file mode 100644 index 00000000..c0ad43fc --- /dev/null +++ b/scripts/fixtures/negative/AudioNodeRecordUpdateIsPrivate.res @@ -0,0 +1 @@ +let update = (node: WebAPI.AudioNode.t) => {...node, channelCount: 1} diff --git a/scripts/fixtures/negative/CSSStyleDeclarationUpdateIsPrivate.res b/scripts/fixtures/negative/CSSStyleDeclarationUpdateIsPrivate.res new file mode 100644 index 00000000..13affd0f --- /dev/null +++ b/scripts/fixtures/negative/CSSStyleDeclarationUpdateIsPrivate.res @@ -0,0 +1 @@ +let update = (style: WebAPI.CSSStyleDeclaration.t) => {...style, cssText: "color: red"} diff --git a/scripts/fixtures/negative/DOMTokenListBackingRouteIsPrivate.res b/scripts/fixtures/negative/DOMTokenListBackingRouteIsPrivate.res new file mode 100644 index 00000000..398410e6 --- /dev/null +++ b/scripts/fixtures/negative/DOMTokenListBackingRouteIsPrivate.res @@ -0,0 +1 @@ +let update = (tokens: WebAPI.DOM.domTokenList) => {...tokens, value: "forged"} diff --git a/scripts/fixtures/negative/DOMTokenListPublicViewIsPrivate.res b/scripts/fixtures/negative/DOMTokenListPublicViewIsPrivate.res new file mode 100644 index 00000000..8f176ecc --- /dev/null +++ b/scripts/fixtures/negative/DOMTokenListPublicViewIsPrivate.res @@ -0,0 +1 @@ +let update = (tokens: WebAPI.DOMTokenList.t) => {...tokens, value: "forged"} diff --git a/scripts/fixtures/negative/DOMTreeElementBackingRouteIsPrivate.res b/scripts/fixtures/negative/DOMTreeElementBackingRouteIsPrivate.res new file mode 100644 index 00000000..9c52afff --- /dev/null +++ b/scripts/fixtures/negative/DOMTreeElementBackingRouteIsPrivate.res @@ -0,0 +1 @@ +let update = (element: WebAPI.DOMTree.element) => {...element, id: "forged"} diff --git a/scripts/fixtures/negative/DataTransferRecordUpdateIsPrivate.res b/scripts/fixtures/negative/DataTransferRecordUpdateIsPrivate.res new file mode 100644 index 00000000..d5a3bb40 --- /dev/null +++ b/scripts/fixtures/negative/DataTransferRecordUpdateIsPrivate.res @@ -0,0 +1 @@ +let update = (transfer: WebAPI.DataTransfer.t) => {...transfer, dropEffect: "copy"} diff --git a/scripts/fixtures/negative/GenericNodeListUpdateIsPrivate.res b/scripts/fixtures/negative/GenericNodeListUpdateIsPrivate.res new file mode 100644 index 00000000..c9e38d62 --- /dev/null +++ b/scripts/fixtures/negative/GenericNodeListUpdateIsPrivate.res @@ -0,0 +1 @@ +let update = (nodes: WebAPI.NodeList.t) => {...nodes, length: 0} diff --git a/scripts/fixtures/negative/HTMLCanvasRecordUpdateIsPrivate.res b/scripts/fixtures/negative/HTMLCanvasRecordUpdateIsPrivate.res new file mode 100644 index 00000000..1d9c3277 --- /dev/null +++ b/scripts/fixtures/negative/HTMLCanvasRecordUpdateIsPrivate.res @@ -0,0 +1 @@ +let update = (canvas: WebAPI.HTMLCanvasElement.t) => {...canvas, width: 1} diff --git a/scripts/fixtures/negative/MutationRecordUpdateIsPrivate.res b/scripts/fixtures/negative/MutationRecordUpdateIsPrivate.res new file mode 100644 index 00000000..e0527658 --- /dev/null +++ b/scripts/fixtures/negative/MutationRecordUpdateIsPrivate.res @@ -0,0 +1 @@ +let update = (record: WebAPI.MutationObserver.mutationRecord) => {...record, type_: "attributes"} diff --git a/scripts/fixtures/negative/NativeRecordUpdateIsPrivate.res b/scripts/fixtures/negative/NativeRecordUpdateIsPrivate.res new file mode 100644 index 00000000..314ac65d --- /dev/null +++ b/scripts/fixtures/negative/NativeRecordUpdateIsPrivate.res @@ -0,0 +1 @@ +let update = (response: WebAPI.Response.t) => {...response, statusText: "forged"} diff --git a/scripts/fixtures/negative/URLRecordUpdateIsPrivate.res b/scripts/fixtures/negative/URLRecordUpdateIsPrivate.res new file mode 100644 index 00000000..5fc21f8d --- /dev/null +++ b/scripts/fixtures/negative/URLRecordUpdateIsPrivate.res @@ -0,0 +1 @@ +let update = (url: WebAPI.URL.t) => {...url, hash: "#forged"} diff --git a/scripts/native-instance-inventory.json b/scripts/native-instance-inventory.json new file mode 100644 index 00000000..669dde64 --- /dev/null +++ b/scripts/native-instance-inventory.json @@ -0,0 +1,1384 @@ +{ + "inventoryVersion": 1, + "baseline": "4888db2cc601ad568b76af6ed2add60da0ad96cb", + "issue": 403, + "publicModules": { + "baselineCount": 291, + "addedForSetters": [ + "OffscreenCanvasRenderingContext2D", + "WebGLRenderingContext", + "WebGL2RenderingContext" + ], + "expectedCount": 294, + "manifestSha256": "cbc55d544278568bfea29dea325c0fe9c3be4dd12f0ab638e1cca04144b21854" + }, + "publicTypesModuleAliases": { + "src/window/History.res": { "Types": "HistoryTypes" }, + "src/url/URL.res": { "Types": "UrlTypes" }, + "src/observers/ResizeObserver.res": { "Types": "ResizeObserverTypes" }, + "src/device/Geolocation.res": { "Types": "GeolocationTypes" }, + "src/device/WebMIDI.res": { "Types": "WebMidiTypes" }, + "src/navigator/Permissions.res": { "Types": "PermissionsTypes" }, + "src/media/MediaCapabilities.res": { "Types": "MediaCapabilitiesTypes" }, + "src/media/MediaSession.res": { "Types": "MediaSessionTypes" }, + "src/navigator/Clipboard.res": { "Types": "ClipboardTypes" }, + "src/media/RemotePlayback.res": { "Types": "RemotePlaybackTypes" }, + "src/media/PictureInPicture.res": { "Types": "PictureInPictureTypes" }, + "src/messaging/Notification.res": { "Types": "NotificationTypes" }, + "src/performance/Performance.res": { "Types": "PerformanceTypes" }, + "src/workers/ServiceWorker.res": { "Types": "ServiceWorkerTypes" } + }, + "publicModuleAliases": { + "src/url/URL.res": { "URLSearchParams": "URLSearchParams" }, + "src/device/Geolocation.res": { + "GeolocationCoordinates": "GeolocationCoordinates", + "GeolocationPosition": "GeolocationPosition" + }, + "src/navigator/Clipboard.res": { "ClipboardItem": "ClipboardItem" }, + "src/media/MediaSession.res": { "MediaMetadata": "MediaMetadata" }, + "src/performance/Performance.res": { + "PerformanceEntry": "PerformanceEntry", + "PerformanceMark": "PerformanceMark" + }, + "src/workers/ServiceWorker.res": { + "Cache": "Cache", + "Clients": "Clients", + "NavigationPreloadManager": "NavigationPreloadManager", + "ServiceWorkerContainer": "ServiceWorkerContainer", + "ServiceWorkerScope": "ServiceWorkerScope", + "ServiceWorkerRegistration": "ServiceWorkerRegistration" + } + }, + "largeUniformWritableFamilies": { + "src/cssom/CSSStyleDeclaration.res": { + "nativeMutableStringFieldCount": 423, + "ordinaryWritePath": "setProperty", + "specialWritePaths": { + "cssText": "setCssText", + "float": "setProperty(property=\"float\")", + "cssFloat": "setProperty(property=\"float\")" + } + } + }, + "sourceTypeDeclarationCount": 871, + "sourceTypeDeclarationSha256": "15e6f06b9b8280d016d7cd749e65803c70c5d6716d790b03b9ae5788a583de2f", + "nativeBackings": { + "src/core/DOM.res": [ + "document", + "domException", + "domStringList", + "window", + "userActivation", + "domTokenList", + "customElementRegistry", + "eventTarget", + "event", + "extendableEvent", + "abortController", + "abortSignal", + "screenOrientation", + "screen", + "documentTimeline", + "nodeList", + "domRectReadOnly", + "domRect", + "domRectList", + "validityState", + "caretPosition", + "idleDeadline", + "fileList", + "namedNodeMap", + "domMatrixReadOnly", + "domMatrix", + "videoColorSpace", + "videoFrame", + "imageData", + "domPointReadOnly", + "domPoint" + ], + "src/dom-nodes/DOMTree.res": [ + "customStateSet", + "node", + "element", + "documentFragment", + "shadowRoot", + "htmlElement", + "htmlFormElement", + "htmlFormControlsCollection", + "htmlSlotElement", + "elementInternals" + ], + "src/html/HTMLAnchorElement.res": ["t"], + "src/html/HTMLAreaElement.res": ["t"], + "src/html/HTMLBRElement.res": ["t"], + "src/html/HTMLBaseElement.res": ["t"], + "src/html/HTMLBodyElement.res": ["t"], + "src/html/HTMLButtonElement.res": ["t"], + "src/html/HTMLDListElement.res": ["t"], + "src/html/HTMLDataElement.res": ["t"], + "src/html/HTMLDataListElement.res": ["t"], + "src/html/HTMLDialogElement.res": ["t"], + "src/html/HTMLDivElement.res": ["t"], + "src/html/HTMLElement.res": ["t"], + "src/html/HTMLEmbedElement.res": ["t"], + "src/html/HTMLFieldSetElement.res": ["t"], + "src/html/HTMLFormElement.res": ["t"], + "src/html/HTMLFormControlsCollection.res": ["t"], + "src/html/HTMLFrameSetElement.res": ["t"], + "src/html/HTMLHRElement.res": ["t"], + "src/html/HTMLHeadElement.res": ["t"], + "src/html/HTMLHeadingElement.res": ["t"], + "src/html/HTMLHtmlElement.res": ["t"], + "src/html/HTMLIFrameElement.res": ["t"], + "src/html/HTMLImageElement.res": ["t"], + "src/html/HTMLInputElement.res": ["t"], + "src/html/HTMLLIElement.res": ["t"], + "src/html/HTMLLabelElement.res": ["t"], + "src/html/HTMLLegendElement.res": ["t"], + "src/html/HTMLLinkElement.res": ["t"], + "src/html/HTMLMapElement.res": ["t"], + "src/html/HTMLMenuElement.res": ["t"], + "src/html/HTMLMetaElement.res": ["t"], + "src/html/HTMLMeterElement.res": ["t"], + "src/html/HTMLModElement.res": ["t"], + "src/html/HTMLOListElement.res": ["t"], + "src/html/HTMLObjectElement.res": ["t"], + "src/html/HTMLOptGroupElement.res": ["t"], + "src/html/HTMLOptionElement.res": ["t"], + "src/html/HTMLOptionsCollection.res": ["t"], + "src/html/HTMLOutputElement.res": ["t"], + "src/html/HTMLParagraphElement.res": ["t"], + "src/html/HTMLPictureElement.res": ["t"], + "src/html/HTMLPreElement.res": ["t"], + "src/html/HTMLProgressElement.res": ["t"], + "src/html/HTMLQuoteElement.res": ["t"], + "src/html/HTMLScriptElement.res": ["t"], + "src/html/HTMLSelectElement.res": ["t"], + "src/html/HTMLSlotElement.res": ["t"], + "src/html/HTMLSourceElement.res": ["t"], + "src/html/HTMLSpanElement.res": ["t"], + "src/html/HTMLStyleElement.res": ["t"], + "src/html/HTMLTableCaptionElement.res": ["t"], + "src/html/HTMLTableCellElement.res": ["t"], + "src/html/HTMLTableElement.res": ["t"], + "src/html/HTMLTableRowElement.res": ["t"], + "src/html/HTMLTableSectionElement.res": ["t"], + "src/html/HTMLTemplateElement.res": ["t"], + "src/html/HTMLTextAreaElement.res": ["t"], + "src/html/HTMLTimeElement.res": ["t"], + "src/html/HTMLTitleElement.res": ["t"], + "src/html/HTMLUListElement.res": ["t"], + "src/html/HTMLUnknownElement.res": ["t"], + "src/dom-platform/DOMStringMap.res": ["t"], + "src/dom-nodes/AbstractRange.res": ["t"], + "src/dom-nodes/Attr.res": ["t"], + "src/dom-nodes/CDATASection.res": ["t"], + "src/dom-nodes/CharacterData.res": ["t"], + "src/dom-nodes/Comment.res": ["t"], + "src/dom-nodes/DOMImplementation.res": ["documentType", "xmlDocument"], + "src/dom-nodes/HTMLCollection.res": ["t"], + "src/dom-nodes/NodeFilter.res": ["t"], + "src/dom-nodes/NodeIterator.res": ["t"], + "src/dom-nodes/ProcessingInstruction.res": ["t"], + "src/dom-nodes/Range.res": ["t"], + "src/dom-nodes/Selection.res": ["t"], + "src/dom-nodes/StaticRange.res": ["t"], + "src/dom-nodes/Text.res": ["t"], + "src/dom-nodes/TreeWalker.res": ["t"], + "src/animation/Animation.res": ["animationTimeline", "t"], + "src/animation/AnimationEffect.res": ["t"], + "src/cssom/CSSRuleList.res": ["t"], + "src/cssom/CSSStyleDeclaration.res": ["t"], + "src/cssom/CSSStyleValue.res": ["t"], + "src/cssom/MediaList.res": ["t"], + "src/cssom/MediaQueryList.res": ["t"], + "src/cssom/StylePropertyMap.res": ["t"], + "src/cssom/StylePropertyMapReadOnly.res": ["t"], + "src/cssom/StyleSheet.res": ["t", "cssStyleSheet", "cssRule", "cssRuleList"], + "src/cssom/StyleSheetList.res": ["t"], + "src/css-font-loading/CssFontLoadingTypes.res": ["fontFace"], + "src/css-font-loading/BaseCSSFontLoading.res": ["fontFaceSet"], + "src/geometry/DOMMatrix.res": ["t"], + "src/geometry/DOMMatrixReadOnly.res": ["t"], + "src/geometry/DOMPoint.res": ["t"], + "src/geometry/DOMPointReadOnly.res": ["t"], + "src/geometry/DOMRect.res": ["t"], + "src/geometry/DOMRectList.res": ["t"], + "src/geometry/DOMRectReadOnly.res": ["t"], + "src/svg/SVGElement.res": [ + "svgAnimatedPreserveAspectRatio", + "svgAnimatedString", + "svgLength", + "svgAnimatedLength", + "t", + "svgGraphicsElement", + "svgImageElement" + ], + "src/svg/SVGGraphicsElement.res": ["t"], + "src/svg/SVGLength.res": ["t"], + "src/canvas/CanvasTypes.res": [ + "offscreenCanvas", + "imageBitmap", + "offscreenCanvasRenderingContext2D", + "imageBitmapRenderingContext", + "webGLRenderingContext", + "webGL2RenderingContext", + "canvasGradient", + "canvasPattern", + "path2D", + "textMetrics" + ], + "src/canvas/HTMLCanvasElement.res": ["t"], + "src/canvas/CanvasGradient.res": ["t"], + "src/canvas/CanvasPattern.res": ["t"], + "src/canvas/ImageBitmap.res": ["t"], + "src/canvas/ImageBitmapRenderingContext.res": ["t"], + "src/canvas/ImageData.res": ["t"], + "src/canvas/Path2D.res": ["t"], + "src/canvas/VideoColorSpace.res": ["t"], + "src/canvas/VideoFrame.res": ["t"], + "src/ui-events/UiEventsTypes.res": [ + "uiEvent", + "compositionEvent", + "focusEvent", + "dataTransfer", + "dataTransferItem", + "dataTransferItemList", + "inputEvent", + "keyboardEvent", + "mouseEvent", + "wheelEvent", + "touch", + "touchList", + "touchEvent", + "pointerEvent" + ], + "src/messaging/WebSocketsTypes.res": ["webSocket", "closeEvent", "messageEvent"], + "src/messaging/ChannelMessagingTypes.res": ["messagePort"], + "src/messaging/NotificationTypes.res": ["notification", "notificationEvent"], + "src/messaging/PushTypes.res": [ + "pushManager", + "pushSubscriptionOptions", + "pushSubscription", + "pushEvent" + ], + "src/navigator/ClipboardTypes.res": ["clipboardItem", "clipboard"], + "src/navigator/CredentialManagementTypes.res": ["credentialsContainer", "credential"], + "src/navigator/PermissionsTypes.res": ["permissions", "permissionStatus"], + "src/device/GeolocationTypes.res": [ + "geolocation", + "geolocationCoordinates", + "geolocationPosition", + "geolocationPositionError" + ], + "src/device/GamepadTypes.res": ["gamepadButton", "gamepadHapticActuator", "gamepad"], + "src/device/WebLocksTypes.res": ["lockManager", "lock"], + "src/device/ScreenWakeLockTypes.res": ["wakeLock", "wakeLockSentinel"], + "src/device/WebMidiTypes.res": ["midiInputMap", "midiOutputMap", "midiAccess"], + "src/device/Geolocation.res": ["t"], + "src/device/GeolocationCoordinates.res": ["t"], + "src/device/GeolocationPosition.res": ["t"], + "src/device/GamepadHapticActuator.res": ["t"], + "src/device/LockManager.res": ["t"], + "src/device/ScreenOrientation.res": ["t"], + "src/device/WakeLock.res": ["t"], + "src/window/HistoryTypes.res": ["history"], + "src/window/VisualViewportTypes.res": ["visualViewport"], + "src/window/Location.res": ["t"], + "src/window/XPathExpression.res": ["t"], + "src/window/XPathResult.res": ["t"], + "src/window/CustomElementRegistry.res": ["t"], + "src/window/IdleDeadline.res": ["t"], + "src/window/VisualViewport.res": ["t"], + "src/fetch/FetchTypes.res": ["headers", "request", "response", "formData"], + "src/file/FileTypes.res": [ + "blob", + "readableStream", + "writableStream", + "writableStreamDefaultController", + "file", + "fileSystemHandle", + "fileSystemDirectoryHandle", + "fileSystemFileHandle", + "fileSystemWritableFileStream" + ], + "src/file/BaseFileAndDirectoryEntries.res": [ + "fileSystemEntry", + "fileSystemDirectoryEntry", + "fileSystem" + ], + "src/file/FileAndDirectoryEntriesTypes.res": ["fileSystemDirectoryReader"], + "src/url/UrlTypes.res": ["urlSearchParams", "url"], + "src/storage/IndexedDbTypes.res": [ + "idbFactory", + "idbDatabase", + "idbTransaction", + "idbRequest", + "idbOpenDBRequest", + "idbObjectStore", + "idbIndex" + ], + "src/storage/WebStorageTypes.res": ["storage", "storageEvent"], + "src/storage/StorageTypes.res": ["storageManager"], + "src/storage/CacheTypes.res": ["cache", "cacheStorage"], + "src/observers/IntersectionObserverTypes.res": [ + "intersectionObserver", + "intersectionObserverEntry" + ], + "src/observers/ResizeObserverTypes.res": [ + "resizeObserverSize", + "resizeObserverEntry", + "resizeObserver" + ], + "src/observers/MutationObserver.res": ["mutationRecord"], + "src/media/MediaCaptureAndStreamsTypes.res": [ + "mediaDevices", + "mediaDeviceInfo", + "mediaStream", + "mediaStreamTrack" + ], + "src/media/BaseEncryptedMediaExtensions.res": [ + "mediaKeySystemAccess", + "mediaKeys", + "mediaKeyStatusMap", + "mediaKeySession" + ], + "src/media/MediaCapabilitiesTypes.res": ["mediaCapabilities"], + "src/media/MediaSessionTypes.res": ["mediaMetadata", "mediaSession"], + "src/media/WebVttTypes.res": ["textTrackCueList", "textTrackCue", "textTrack"], + "src/media/WebSpeechTypes.res": [ + "speechSynthesis", + "speechSynthesisVoice", + "speechSynthesisUtterance" + ], + "src/media/PictureInPictureTypes.res": ["pictureInPictureWindow"], + "src/media/RemotePlaybackTypes.res": ["remotePlayback"], + "src/media/DomHTMLMediaElement.res": ["t"], + "src/media/HTMLAudioElement.res": ["t"], + "src/media/HTMLVideoElement.res": ["t"], + "src/media/HTMLTrackElement.res": ["t"], + "src/media/VideoPlaybackQuality.res": ["t"], + "src/media/MediaCapabilities.res": ["t"], + "src/media/MediaDeviceInfo.res": ["t"], + "src/media/MediaDevices.res": ["t"], + "src/media/MediaError.res": ["t"], + "src/media/MediaKeyStatusMap.res": ["t"], + "src/media/MediaKeySystemAccess.res": ["t"], + "src/media/MediaKeys.res": ["t"], + "src/media/MediaMetadata.res": ["t"], + "src/media/MediaSession.res": ["t"], + "src/media/MediaStream.res": ["t"], + "src/media/MediaStreamTrack.res": ["t"], + "src/media/PictureInPicture.res": ["t"], + "src/media/RemotePlayback.res": ["t"], + "src/media/TextTrackCueList.res": ["t"], + "src/media/TextTrackList.res": ["t"], + "src/media/TimeRanges.res": ["t"], + "src/performance/PerformanceTypes.res": [ + "eventCounts", + "performance", + "performanceEntry", + "performanceMark", + "performanceMeasure" + ], + "src/performance/PerformanceEntry.res": ["t"], + "src/performance/PerformanceMark.res": ["t"], + "src/view-transitions/ViewTransitionsTypes.res": ["viewTransition"], + "src/workers/WebWorkersTypes.res": ["workerGlobalScope", "sharedWorkerGlobalScope"], + "src/workers/ServiceWorkerTypes.res": [ + "serviceWorker", + "navigationPreloadManager", + "serviceWorkerRegistration", + "serviceWorkerContainer", + "serviceWorkerGlobalScope", + "client", + "windowClient" + ], + "src/workers/NavigationPreloadManager.res": ["t"], + "src/workers/ServiceWorker.res": ["t"], + "src/workers/ServiceWorkerScope.res": ["t"], + "src/workers/SharedWorkerScope.res": ["t"], + "src/workers/WorkerLocation.res": ["t"], + "src/crypto/WebCryptoTypes.res": ["subtleCrypto", "crypto", "cryptoKey"], + "src/web-audio/WebAudioTypes.res": [ + "audioBuffer", + "audioProcessingEvent", + "offlineAudioCompletionEvent", + "worklet", + "audioNode", + "audioDestinationNode", + "baseAudioContext", + "biquadFilterNode", + "audioListener", + "audioWorklet", + "audioParam", + "audioScheduledSourceNode", + "audioBufferSourceNode", + "channelMergerNode", + "channelSplitterNode", + "constantSourceNode", + "convolverNode", + "delayNode", + "dynamicsCompressorNode", + "gainNode", + "iirFilterNode", + "oscillatorNode", + "pannerNode", + "analyserNode", + "periodicWave", + "stereoPannerNode", + "waveShaperNode", + "audioContext", + "mediaElementAudioSourceNode", + "mediaStreamAudioSourceNode", + "mediaStreamAudioDestinationNode", + "audioParamMap", + "audioWorkletNode", + "offlineAudioContext" + ] + }, + "publicRecordViews": { + "src/dom-platform/DOMException.res": ["t"], + "src/dom-platform/DOMStringList.res": ["t"], + "src/dom-platform/DOMTokenList.res": ["t"], + "src/dom-nodes/Node.res": ["t"], + "src/dom-nodes/Element.res": ["t"], + "src/dom-nodes/DocumentFragment.res": ["t"], + "src/dom-nodes/ShadowRoot.res": ["t"], + "src/dom-nodes/ElementInternals.res": ["t"], + "src/html/HTMLElement.res": ["t"], + "src/html/HTMLFormElement.res": ["t"], + "src/html/HTMLFormControlsCollection.res": ["t"], + "src/html/HTMLSlotElement.res": ["t"], + "src/css-font-loading/FontFace.res": ["t"], + "src/css-font-loading/CssFontLoadingTypes.res": ["fontFaceSet"], + "src/animation/Animation.res": ["t"], + "src/animation/AnimationEffect.res": ["t"], + "src/animation/DocumentTimeline.res": ["t"], + "src/cssom/CSSRuleList.res": ["t"], + "src/cssom/CSSStyleDeclaration.res": ["t"], + "src/cssom/CSSStyleValue.res": ["t"], + "src/cssom/MediaList.res": ["t"], + "src/cssom/MediaQueryList.res": ["t"], + "src/cssom/StylePropertyMap.res": ["t"], + "src/cssom/StylePropertyMapReadOnly.res": ["t"], + "src/cssom/StyleSheet.res": ["t"], + "src/cssom/StyleSheetList.res": ["t"], + "src/canvas/OffscreenCanvasRenderingContext2D.res": [], + "src/canvas/WebGLRenderingContext.res": [], + "src/canvas/WebGL2RenderingContext.res": [], + "src/fetch/Request.res": ["t"], + "src/fetch/Response.res": ["t"], + "src/file/ReadableStream.res": ["t"], + "src/file/WritableStream.res": ["t"], + "src/storage/IDBIndex.res": ["t"], + "src/storage/IDBObjectStore.res": ["t"], + "src/messaging/WebSocket.res": ["t", "messageEvent", "closeEvent"], + "src/messaging/MessageEvent.res": ["event", "eventTarget", "messageEvent"], + "src/messaging/Notification.res": ["t", "notificationEvent"], + "src/navigator/ClipboardItem.res": ["t"], + "src/navigator/CredentialsContainer.res": ["t"], + "src/navigator/Permissions.res": ["t"], + "src/device/Geolocation.res": ["t"], + "src/device/GeolocationCoordinates.res": ["t"], + "src/device/GeolocationPosition.res": ["t"], + "src/device/WebMIDI.res": ["t"], + "src/url/URL.res": ["t"], + "src/web-audio/AudioNode.res": ["t"], + "src/web-audio/AudioParam.res": ["t"], + "src/web-audio/AudioBuffer.res": ["t"], + "src/web-audio/BaseAudioContext.res": ["t"], + "src/web-audio/PeriodicWave.res": ["t"], + "src/web-audio/Worklet.res": ["t"], + "src/crypto/Crypto.res": ["t"], + "src/crypto/SubtleCrypto.res": ["t", "cryptoKey"], + "src/dom-nodes/AbstractRange.res": ["t"], + "src/dom-nodes/Attr.res": ["t"], + "src/dom-nodes/CaretPosition.res": ["t"], + "src/dom-nodes/CDATASection.res": ["t"], + "src/dom-nodes/CharacterData.res": ["t"], + "src/dom-nodes/Comment.res": ["t"], + "src/dom-nodes/DOMImplementation.res": ["t"], + "src/dom-nodes/HTMLCollection.res": ["t"], + "src/dom-nodes/NamedNodeMap.res": ["t"], + "src/dom-nodes/NodeFilter.res": ["t"], + "src/dom-nodes/NodeIterator.res": ["t"], + "src/dom-nodes/NodeList.res": ["t"], + "src/dom-nodes/ProcessingInstruction.res": ["t"], + "src/dom-nodes/Range.res": ["t"], + "src/dom-nodes/Selection.res": ["t"], + "src/dom-nodes/StaticRange.res": ["t"], + "src/dom-nodes/Text.res": ["t"], + "src/dom-nodes/TreeWalker.res": ["t"], + "src/geometry/DOMMatrix.res": ["t"], + "src/geometry/DOMMatrixReadOnly.res": ["t"], + "src/geometry/DOMPoint.res": ["t"], + "src/geometry/DOMPointReadOnly.res": ["t"], + "src/geometry/DOMRect.res": ["t"], + "src/geometry/DOMRectList.res": ["t"], + "src/geometry/DOMRectReadOnly.res": ["t"], + "src/canvas/CanvasGradient.res": ["t"], + "src/canvas/CanvasPattern.res": ["t"], + "src/canvas/HTMLCanvasElement.res": ["t"], + "src/canvas/ImageBitmap.res": ["t"], + "src/canvas/ImageBitmapRenderingContext.res": ["t"], + "src/canvas/ImageData.res": ["t"], + "src/canvas/Path2D.res": ["t"], + "src/canvas/VideoColorSpace.res": ["t"], + "src/canvas/VideoFrame.res": ["t"], + "src/svg/SVGElement.res": ["t", "svgGraphicsElement", "svgLength"], + "src/svg/SVGGraphicsElement.res": ["t"], + "src/svg/SVGLength.res": ["t"], + "src/ui-events/DataTransfer.res": ["t"], + "src/ui-events/DataTransferItem.res": ["t"], + "src/ui-events/DataTransferItemList.res": ["t"], + "src/ui-events/Touch.res": ["t"], + "src/ui-events/TouchList.res": ["t"], + "src/event/AbortController.res": ["t"], + "src/file/Blob.res": ["t"], + "src/file/File.res": ["t"], + "src/file/FileList.res": ["t"], + "src/file/FileSystemDirectoryEntry.res": ["t"], + "src/file/FileSystemDirectoryHandle.res": ["t"], + "src/file/FileSystemDirectoryReader.res": ["t"], + "src/file/FileSystemEntry.res": ["t"], + "src/file/FileSystemFileHandle.res": ["t"], + "src/file/FileSystemHandle.res": ["t"], + "src/file/FileSystemWritableFileStream.res": ["t"], + "src/file/WritableStreamDefaultController.res": ["t"], + "src/storage/Cache.res": ["t"], + "src/storage/CacheStorage.res": ["t"], + "src/storage/IDBFactory.res": ["t"], + "src/storage/Storage.res": ["t"], + "src/storage/StorageManager.res": ["t"], + "src/observers/IntersectionObserver.res": ["t"], + "src/observers/ResizeObserver.res": ["t"], + "src/media/HTMLAudioElement.res": ["t"], + "src/media/HTMLTrackElement.res": ["t"], + "src/media/HTMLVideoElement.res": ["t"], + "src/media/MediaCapabilities.res": ["t"], + "src/media/MediaDeviceInfo.res": ["t"], + "src/media/MediaDevices.res": ["t"], + "src/media/MediaError.res": ["t"], + "src/media/MediaKeyStatusMap.res": ["t"], + "src/media/MediaKeySystemAccess.res": ["t"], + "src/media/MediaKeys.res": ["t"], + "src/media/MediaMetadata.res": ["t"], + "src/media/MediaSession.res": ["t"], + "src/media/MediaStream.res": ["t"], + "src/media/MediaStreamTrack.res": ["t"], + "src/media/PictureInPicture.res": ["t"], + "src/media/RemotePlayback.res": ["t"], + "src/media/TextTrackCueList.res": ["t"], + "src/media/TextTrackList.res": ["t"], + "src/media/TimeRanges.res": ["t"], + "src/media/VideoPlaybackQuality.res": ["t"], + "src/performance/PerformanceEntry.res": ["t"], + "src/performance/PerformanceMark.res": ["t"], + "src/workers/NavigationPreloadManager.res": ["t"], + "src/workers/ServiceWorker.res": ["t"], + "src/workers/ServiceWorkerScope.res": ["t"], + "src/workers/SharedWorkerScope.res": ["t"], + "src/workers/WorkerLocation.res": ["t"], + "src/view-transitions/ViewTransition.res": ["t"], + "src/window/CustomElementRegistry.res": ["t"], + "src/window/History.res": ["t"], + "src/window/IdleDeadline.res": ["t"], + "src/window/Location.res": ["t"], + "src/window/VisualViewport.res": ["t"], + "src/window/Window.res": ["t"], + "src/window/XPathExpression.res": ["t"], + "src/window/XPathResult.res": ["t"] + }, + "constructibleRecordExceptions": { + "src/core/DOM.res": [ + "shareData", + "eventInit", + "structuredSerializeOptions", + "canvasContext2DAttributes", + "elementDefinitionOptions", + "documentTimelineOptions", + "checkVisibilityOptions", + "scrollOptions", + "scrollToOptions", + "fullscreenOptions", + "pointerLockOptions", + "idleRequestOptions", + "domRectInit", + "validityStateFlags", + "assignedNodesOptions", + "focusOptions", + "imageBitmapOptions", + "scrollIntoViewOptions", + "windowPostMessageOptions", + "domMatrix2DInit", + "domMatrixInit", + "videoFrameInit", + "videoColorSpaceInit", + "planeLayout", + "videoFrameBufferInit", + "imageDataSettings", + "videoFrameCopyToOptions", + "domPointInit", + "options", + "addEventListenerOptions" + ], + "src/dom-nodes/Node.res": ["getRootNodeOptions"], + "src/dom-nodes/HTML.res": ["shadowRootInit", "getHTMLOptions"], + "src/window/Document.res": ["elementCreationOptions", "caretPositionFromPointOptions"], + "src/window/Window.res": ["windowPostMessageOptions"], + "src/event/Event.res": ["eventInit"], + "src/animation/AnimationEffect.res": [ + "effectTiming", + "computedEffectTiming", + "optionalEffectTiming", + "getAnimationsOptions" + ], + "src/animation/Animation.res": ["keyframeEffectOptions", "keyframeAnimationOptions"], + "src/css-font-loading/CssFontLoadingTypes.res": ["fontFaceDescriptors"], + "src/cssom/StyleSheet.res": ["init"], + "src/svg/SVGElement.res": ["svgBoundingBoxOptions"], + "src/ui-events/UiEventsTypes.res": [ + "uiEventInit", + "eventModifierInit", + "mouseEventInit", + "focusEventInit", + "compositionEventInit", + "wheelEventInit", + "keyboardEventInit", + "inputEventInit", + "touchInit", + "pointerEventInit", + "touchEventInit" + ], + "src/fetch/FetchTypes.res": ["requestInit", "responseInit"], + "src/canvas/CanvasTypes.res": [ + "imageEncodeOptions", + "canvasRenderingContext2DSettings", + "webGLContextAttributes", + "imageBitmapRenderingContextSettings" + ], + "src/file/FileTypes.res": [ + "underlyingByteSource", + "readableStreamGetReaderOptions", + "readableWritablePair", + "streamPipeOptions", + "blobPropertyBag", + "filePropertyBag", + "fileSystemGetFileOptions", + "fileSystemGetDirectoryOptions", + "fileSystemRemoveOptions", + "fileSystemCreateWritableOptions", + "writeParams" + ], + "src/file/FileAndDirectoryEntriesTypes.res": ["fileSystemFlags"], + "src/storage/IndexedDbTypes.res": [ + "idbDatabaseInfo", + "idbTransactionOptions", + "idbObjectStoreParameters", + "idbIndexParameters" + ], + "src/storage/WebStorageTypes.res": ["storageEventInit"], + "src/storage/StorageTypes.res": ["storageEstimate"], + "src/storage/CacheTypes.res": ["cacheQueryOptions", "multiCacheQueryOptions"], + "src/observers/IntersectionObserverTypes.res": ["intersectionObserverInit"], + "src/observers/ResizeObserverTypes.res": ["resizeObserverOptions"], + "src/observers/MutationObserver.res": ["mutationObserverInit"], + "src/media/MediaCapabilitiesTypes.res": [ + "audioConfiguration", + "videoConfiguration", + "mediaConfiguration", + "mediaDecodingConfiguration", + "mediaCapabilitiesInfo", + "mediaCapabilitiesDecodingInfo", + "mediaEncodingConfiguration", + "mediaCapabilitiesEncodingInfo" + ], + "src/media/MediaCaptureAndStreamsTypes.res": [ + "mediaTrackSupportedConstraints", + "mediaStreamConstraints", + "displayMediaStreamOptions", + "uLongRange", + "doubleRange", + "mediaTrackCapabilities", + "mediaTrackConstraintSet", + "mediaTrackConstraints", + "mediaTrackSettings" + ], + "src/media/HTMLVideoElement.res": ["videoFrameCallbackMetadata"], + "src/media/MediaSessionTypes.res": [ + "mediaImage", + "mediaMetadataInit", + "mediaSessionActionDetails", + "mediaPositionState" + ], + "src/media/MediaMetadata.res": ["mediaMetadataInit"], + "src/media/MediaSession.res": ["mediaPositionState"], + "src/media/BaseEncryptedMediaExtensions.res": [ + "mediaKeySystemMediaCapability", + "mediaKeySystemConfiguration" + ], + "src/media/EncryptedMediaExtensionsTypes.res": [ + "mediaKeySystemMediaCapability", + "mediaKeySystemConfiguration", + "mediaKeysPolicy" + ], + "src/media/MediaDevices.res": [ + "mediaTrackSupportedConstraints", + "mediaStreamConstraints", + "displayMediaStreamOptions" + ], + "src/media/MediaStreamTrack.res": [ + "mediaTrackCapabilities", + "mediaTrackConstraints", + "mediaTrackSettings" + ], + "src/messaging/WebSocketsTypes.res": ["closeEventInit", "messageEventInit"], + "src/messaging/ChannelMessagingTypes.res": ["structuredSerializeOptions"], + "src/messaging/MessagePort.res": ["structuredSerializeOptions"], + "src/messaging/MessageEvent.res": ["messageEventInit"], + "src/messaging/NotificationTypes.res": [ + "notificationAction", + "notificationOptions", + "getNotificationOptions" + ], + "src/messaging/Notification.res": [ + "notificationAction", + "notificationOptions", + "getNotificationOptions" + ], + "src/messaging/PushTypes.res": [ + "pushSubscriptionOptionsInit", + "pushSubscriptionJSONKeys", + "pushSubscriptionJSON" + ], + "src/navigator/ClipboardTypes.res": ["clipboardItemOptions"], + "src/navigator/CredentialManagementTypes.res": [ + "publicKeyCredentialDescriptor", + "authenticationExtensionsPRFValues", + "authenticationExtensionsPRFInputs", + "authenticationExtensionsClientInputs", + "publicKeyCredentialRequestOptions", + "credentialRequestOptions", + "publicKeyCredentialEntity", + "publicKeyCredentialRpEntity", + "publicKeyCredentialUserEntity", + "publicKeyCredentialParameters", + "authenticatorSelectionCriteria", + "publicKeyCredentialCreationOptions", + "credentialCreationOptions" + ], + "src/navigator/PermissionsTypes.res": ["permissionDescriptor"], + "src/crypto/WebCryptoTypes.res": ["keyAlgorithm", "cryptoKeyPair", "algorithm"], + "src/device/GeolocationTypes.res": ["positionOptions"], + "src/device/GamepadTypes.res": ["gamepadEffectParameters"], + "src/device/WebLocksTypes.res": ["lockInfo", "lockManagerSnapshot", "lockOptions"], + "src/device/WebMidiTypes.res": ["midiOptions"], + "src/device/WebMIDI.res": ["midiOptions"], + "src/web-audio/WebAudioTypes.res": [ + "audioTimestamp", + "uLongRange", + "doubleRange", + "periodicWaveConstraints", + "mediaTrackCapabilities", + "mediaTrackConstraintSet", + "mediaTrackConstraints", + "mediaTrackSettings", + "audioBufferOptions", + "audioProcessingEventInit", + "offlineAudioCompletionEventInit", + "audioNodeOptions", + "biquadFilterOptions", + "audioBufferSourceOptions", + "channelMergerOptions", + "channelSplitterOptions", + "constantSourceOptions", + "convolverOptions", + "delayOptions", + "dynamicsCompressorOptions", + "gainOptions", + "iirFilterOptions", + "oscillatorOptions", + "pannerOptions", + "analyserOptions", + "periodicWaveOptions", + "stereoPannerOptions", + "waveShaperOptions", + "audioContextOptions", + "mediaElementAudioSourceOptions", + "mediaStreamAudioSourceOptions", + "audioWorkletNodeOptions", + "offlineAudioContextOptions", + "workletOptions" + ], + "src/performance/PerformanceTypes.res": ["performanceMarkOptions", "performanceMeasureOptions"], + "src/workers/WebWorkersTypes.res": ["workerOptions"], + "src/workers/SharedWorker.res": ["workerOptions"], + "src/workers/ServiceWorkerTypes.res": ["navigationPreloadState", "registrationOptions"], + "src/workers/Clients.res": ["clientQueryOptions"], + "src/safe-fetch/SafeFetch.res": ["httpError", "readError", "bodyError", "response"], + "src/safe-fetch/SafeFetch.resi": ["httpError", "readError", "bodyError", "response"] + }, + "writablePropertyInventory": { + "sharedImplementations": { + "src/dom-nodes/Node.res": ["nodeValue", "textContent"], + "src/dom-nodes/Element.res": [ + "id", + "slot", + "scrollTop", + "scrollLeft", + "innerHTML", + "outerHTML", + "ariaAtomic", + "ariaAutoComplete", + "ariaBrailleLabel", + "ariaBrailleRoleDescription", + "ariaBusy", + "ariaChecked", + "ariaColCount", + "ariaColIndex", + "ariaColIndexText", + "ariaColSpan", + "ariaCurrent", + "ariaDescription", + "ariaDisabled", + "ariaExpanded", + "ariaHasPopup", + "ariaHidden", + "ariaKeyShortcuts", + "ariaLabel", + "ariaLevel", + "ariaLive", + "ariaModal", + "ariaMultiLine", + "ariaMultiSelectable", + "ariaOrientation", + "ariaPlaceholder", + "ariaPosInSet", + "ariaPressed", + "ariaReadOnly", + "ariaRequired", + "ariaRoleDescription", + "ariaRowCount", + "ariaRowIndex", + "ariaRowIndexText", + "ariaRowSpan", + "ariaSelected", + "ariaSetSize", + "ariaSort", + "ariaValueMax", + "ariaValueMin", + "ariaValueNow", + "ariaValueText" + ], + "src/html/HTMLElement.res": [ + "title", + "lang", + "translate", + "dir", + "inert", + "accessKey", + "draggable", + "spellcheck", + "autocapitalize", + "innerText", + "outerText", + "popover", + "contentEditable", + "enterKeyHint", + "inputMode", + "nonce", + "autofocus", + "tabIndex", + "className" + ], + "src/html/HTMLFormElement.res": [ + "acceptCharset", + "action", + "autocomplete", + "enctype", + "encoding", + "method", + "name", + "target" + ], + "src/html/HTMLSlotElement.res": ["name"], + "src/dom-nodes/CharacterData.res": ["data"], + "src/dom-nodes/Attr.res": ["value"], + "src/dom-nodes/TreeWalker.res": ["currentNode"], + "src/dom-nodes/ElementInternals.res": [ + "ariaAtomic", + "ariaAutoComplete", + "ariaBrailleLabel", + "ariaBrailleRoleDescription", + "ariaBusy", + "ariaChecked", + "ariaColCount", + "ariaColIndex", + "ariaColIndexText", + "ariaColSpan", + "ariaCurrent", + "ariaDescription", + "ariaDisabled", + "ariaExpanded", + "ariaHasPopup", + "ariaHidden", + "ariaKeyShortcuts", + "ariaLabel", + "ariaLevel", + "ariaLive", + "ariaModal", + "ariaMultiLine", + "ariaMultiSelectable", + "ariaOrientation", + "ariaPlaceholder", + "ariaPosInSet", + "ariaPressed", + "ariaReadOnly", + "ariaRequired", + "ariaRoleDescription", + "ariaRowCount", + "ariaRowIndex", + "ariaRowIndexText", + "ariaRowSpan", + "ariaSelected", + "ariaSetSize", + "ariaSort", + "ariaValueMax", + "ariaValueMin", + "ariaValueNow", + "ariaValueText" + ] + }, + "htmlLocal": { + "HTMLAnchorElement": [ + "target", + "download", + "ping", + "rel", + "hreflang", + "type_", + "text", + "referrerPolicy", + "href", + "protocol", + "username", + "password", + "host", + "hostname", + "port", + "pathname", + "search", + "hash" + ], + "HTMLAreaElement": [ + "target", + "ping", + "rel", + "referrerPolicy", + "href", + "protocol", + "username", + "password", + "host", + "hostname", + "port", + "pathname", + "search", + "hash" + ], + "HTMLBaseElement": ["href", "target"], + "HTMLButtonElement": [ + "disabled", + "formAction", + "formEnctype", + "formMethod", + "name", + "type_", + "value", + "popoverTargetElement", + "popoverTargetAction" + ], + "HTMLDataElement": ["value"], + "HTMLDialogElement": ["open_", "returnValue"], + "HTMLEmbedElement": ["src", "width", "height"], + "HTMLFieldSetElement": ["disabled", "name"], + "HTMLIFrameElement": [ + "src", + "srcdoc", + "name", + "allow", + "allowFullscreen", + "width", + "height", + "referrerPolicy", + "loading" + ], + "HTMLImageElement": [ + "alt", + "src", + "srcset", + "sizes", + "crossOrigin", + "useMap", + "isMap", + "width", + "height", + "referrerPolicy", + "decoding", + "loading", + "fetchPriority" + ], + "HTMLInputElement": [ + "accept", + "alt", + "autocomplete", + "defaultChecked", + "checked", + "disabled", + "files", + "formAction", + "formEnctype", + "formMethod", + "height", + "indeterminate", + "max", + "maxLength", + "min", + "minLength", + "multiple", + "name", + "pattern", + "placeholder", + "readOnly", + "required", + "size", + "src", + "step", + "type_", + "defaultValue", + "value", + "valueAsDate", + "valueAsNumber", + "width", + "selectionStart", + "selectionEnd", + "selectionDirection", + "webkitdirectory", + "capture", + "popoverTargetElement", + "popoverTargetAction" + ], + "HTMLLabelElement": ["htmlFor"], + "HTMLLinkElement": [ + "href", + "crossOrigin", + "rel", + "as_", + "media", + "integrity", + "hreflang", + "type_", + "referrerPolicy", + "disabled", + "fetchPriority" + ], + "HTMLMapElement": ["name"], + "HTMLMetaElement": ["name", "httpEquiv", "content", "media"], + "HTMLMeterElement": ["value", "min", "max", "low", "high", "optimum"], + "HTMLOListElement": ["reversed", "start", "type_"], + "HTMLObjectElement": ["data", "type_", "name", "width", "height"], + "HTMLOptGroupElement": ["disabled", "label"], + "HTMLOptionElement": ["disabled", "label", "defaultSelected", "selected", "value", "text"], + "HTMLOptionsCollection": ["selectedIndex"], + "HTMLOutputElement": ["name", "defaultValue", "value"], + "HTMLProgressElement": ["value", "max"], + "HTMLScriptElement": [ + "src", + "type_", + "noModule", + "async", + "defer", + "crossOrigin", + "text", + "integrity", + "referrerPolicy", + "fetchPriority" + ], + "HTMLSelectElement": [ + "disabled", + "multiple", + "name", + "required", + "size", + "length", + "selectedIndex", + "value" + ], + "HTMLSourceElement": ["width", "height"], + "HTMLStyleElement": ["disabled", "media"], + "HTMLTableCellElement": ["colSpan", "rowSpan", "headers", "scope", "abbr"], + "HTMLTableElement": ["caption", "tHead", "tFoot"], + "HTMLTemplateElement": [ + "shadowRootMode", + "shadowRootDelegatesFocus", + "shadowRootClonable", + "shadowRootSerializable" + ], + "HTMLTextAreaElement": [ + "autocomplete", + "cols", + "disabled", + "maxLength", + "minLength", + "name", + "placeholder", + "readOnly", + "required", + "rows", + "wrap", + "defaultValue", + "value", + "selectionStart", + "selectionEnd", + "selectionDirection" + ], + "HTMLTimeElement": ["dateTime"], + "HTMLTitleElement": ["text"] + }, + "cssStyleDeclaration": { + "file": "src/cssom/CSSStyleDeclaration.res", + "nativeMutableStringFieldCount": 423, + "writes": { + "ordinaryProperties": "CSSStyleDeclaration.setProperty with CSS property names", + "cssText": "CSSStyleDeclaration.setCssText", + "float": "setProperty(property=\"float\")", + "cssFloat": "setProperty(property=\"float\")" + } + }, + "url": { + "src/url/UrlTypes.res": [ + "href", + "protocol", + "username", + "password", + "host", + "hostname", + "port", + "pathname", + "search", + "hash" + ] + }, + "history": { + "src/window/HistoryTypes.res": ["scrollRestoration"] + }, + "location": { + "src/window/Location.res": [ + "href", + "protocol", + "host", + "hostname", + "port", + "pathname", + "search", + "hash" + ] + }, + "animation": { + "Animation.t": ["id", "effect", "timeline", "playbackRate", "startTime", "currentTime"] + }, + "cssAndFonts": { + "MediaList.t": ["mediaText"], + "StyleSheet.t": ["disabled"], + "StyleSheet.cssStyleSheet": ["disabled"], + "StyleSheet.cssRule": ["cssText"], + "CssFontLoadingTypes.fontFace": [ + "family", + "style", + "weight", + "stretch", + "unicodeRange", + "featureSettings", + "display", + "ascentOverride", + "descentOverride", + "lineGapOverride" + ] + }, + "svgAndCanvas": { + "SVGElement.svgAnimatedString": ["baseVal"], + "SVGElement.t": { + "inherits": ["src/dom-nodes/Node.res", "src/dom-nodes/Element.res"], + "local": ["nonce", "autofocus", "tabIndex"] + }, + "HTMLCanvasElement.t": { + "inherits": ["src/html/HTMLElement.res"], + "local": ["width", "height"] + }, + "CanvasTypes.offscreenCanvas": ["width", "height"], + "CanvasTypes.offscreenCanvasRenderingContext2D": [ + "globalAlpha", + "globalCompositeOperation", + "imageSmoothingEnabled", + "imageSmoothingQuality", + "strokeStyle", + "fillStyle", + "shadowOffsetX", + "shadowOffsetY", + "shadowBlur", + "shadowColor", + "filter", + "lineWidth", + "lineCap", + "lineJoin", + "miterLimit", + "lineDashOffset", + "font", + "textAlign", + "textBaseline", + "direction", + "letterSpacing", + "fontKerning", + "fontStretch", + "fontVariantCaps", + "textRendering", + "wordSpacing" + ], + "CanvasTypes.webGLRenderingContext": ["drawingBufferColorSpace", "unpackColorSpace"], + "CanvasTypes.webGL2RenderingContext": ["drawingBufferColorSpace", "unpackColorSpace"], + "DOM.canvasRenderingContext2D": ["existing setters: setFillStyle, setFont, setTextBaseline"] + }, + "audioNodeBase": ["channelCount", "channelCountMode", "channelInterpretation"], + "audioLocal": { + "audioParam": ["value"], + "biquadFilterNode": ["type_"], + "audioBufferSourceNode": ["buffer", "loop", "loopStart", "loopEnd"], + "convolverNode": ["buffer", "normalize"], + "oscillatorNode": ["type_"], + "pannerNode": [ + "panningModel", + "distanceModel", + "refDistance", + "maxDistance", + "rolloffFactor", + "coneInnerAngle", + "coneOuterAngle", + "coneOuterGain" + ], + "analyserNode": ["fftSize", "minDecibels", "maxDecibels", "smoothingTimeConstant"], + "waveShaperNode": ["curve", "oversample"] + }, + "mediaElementBase": [ + "src", + "srcObject", + "crossOrigin", + "preload", + "currentTime", + "defaultPlaybackRate", + "playbackRate", + "preservesPitch", + "autoplay", + "loop", + "controls", + "volume", + "muted", + "defaultMuted", + "disableRemotePlayback" + ], + "mediaLocal": { + "HTMLVideoElement": ["width", "height", "poster", "disablePictureInPicture"], + "HTMLTrackElement": ["src"], + "MediaStreamTrack": ["enabled", "contentHint"], + "MediaMetadata": ["title", "artist", "album", "artwork"], + "MediaSession": ["metadata", "playbackState"], + "TextTrackCue": ["id", "startTime", "endTime", "pauseOnExit"], + "TextTrack": ["mode"], + "SpeechSynthesisUtterance": ["text", "lang", "voice", "volume", "rate", "pitch"] + }, + "webAudioLocal": { + "AudioParam": ["value"], + "BiquadFilterNode": ["type_"], + "AudioBufferSourceNode": ["buffer", "loop", "loopStart", "loopEnd"], + "ConvolverNode": ["buffer", "normalize"], + "OscillatorNode": ["type_"], + "PannerNode": [ + "panningModel", + "distanceModel", + "refDistance", + "maxDistance", + "rolloffFactor", + "coneInnerAngle", + "coneOuterAngle", + "coneOuterGain" + ], + "AnalyserNode": ["fftSize", "minDecibels", "maxDecibels", "smoothingTimeConstant"], + "WaveShaperNode": ["curve", "oversample"] + }, + "webAudioBase": { + "AudioNode.Impl": ["channelCount", "channelCountMode", "channelInterpretation"], + "appliesTo": [ + "audioNode", + "audioDestinationNode", + "biquadFilterNode", + "audioScheduledSourceNode", + "audioBufferSourceNode", + "channelMergerNode", + "channelSplitterNode", + "constantSourceNode", + "convolverNode", + "delayNode", + "dynamicsCompressorNode", + "gainNode", + "iirFilterNode", + "oscillatorNode", + "pannerNode", + "analyserNode", + "stereoPannerNode", + "waveShaperNode", + "mediaElementAudioSourceNode", + "mediaStreamAudioSourceNode", + "mediaStreamAudioDestinationNode", + "audioWorkletNode" + ] + }, + "otherNative": { + "src/ui-events/UiEventsTypes.res": ["dropEffect", "effectAllowed"], + "src/storage/IndexedDbTypes.res": { + "idbObjectStore": ["name"], + "idbIndex": ["name"] + }, + "src/messaging/WebSocketsTypes.res": ["binaryType"] + } + }, + "abstractNativeTypes": [ + "WebWorkersTypes.sharedWorker", + "PushTypes.pushMessageData", + "DOM.navigator", + "DOM.canvasRenderingContext2D", + "MutationObserver.t", + "ServiceWorkerTypes.clients" + ], + "unknownBackedDispositions": { + "notProtectedNativeHandles": [ + "FileTypes.readableStreamReader", + "FileTypes.writableStreamDefaultWriter", + "DOM.renderingContext", + "DOM.offscreenRenderingContext", + "CanvasTypes.offscreenRenderingContext", + "PerformanceTypes.performanceEntryList" + ], + "unmodeledInputOrMixedPlaceholders": [ + "FileTypes.queuingStrategy", + "FileTypes.underlyingSink", + "FileTypes.underlyingSource", + "FileTypes.fileSystemWriteChunkType", + "FetchTypes.requestInfo", + "ServiceWorkerTypes.requestInfo", + "IndexedDbTypes.idbValidKey", + "WebSocketsTypes.messageEventSource", + "DomHTMLMediaElement.mediaProvider", + "WebCryptoTypes.algorithmIdentifier" + ] + } +} diff --git a/scripts/negative-fixtures.json b/scripts/negative-fixtures.json index 98b868fb..2cc133f4 100644 --- a/scripts/negative-fixtures.json +++ b/scripts/negative-fixtures.json @@ -59,6 +59,72 @@ "features": ["WebAPI.Canvas"], "expectedDiagnostic": "Cannot create values of the" }, + { + "name": "native Response record updates are private", + "fixture": "scripts/fixtures/negative/NativeRecordUpdateIsPrivate.res", + "features": ["WebAPI.Fetch"], + "expectedDiagnostic": "Cannot create values of the" + }, + { + "name": "DOMTokenList public view rejects record updates", + "fixture": "scripts/fixtures/negative/DOMTokenListPublicViewIsPrivate.res", + "features": ["WebAPI.DOMPlatform"], + "expectedDiagnostic": "Cannot create values of the" + }, + { + "name": "DOM backing route rejects record updates", + "fixture": "scripts/fixtures/negative/DOMTokenListBackingRouteIsPrivate.res", + "features": ["WebAPI.DOM"], + "expectedDiagnostic": "Cannot create values of the" + }, + { + "name": "HTML canvas record updates are private", + "fixture": "scripts/fixtures/negative/HTMLCanvasRecordUpdateIsPrivate.res", + "features": ["WebAPI.Canvas"], + "expectedDiagnostic": "Cannot create values of the" + }, + { + "name": "DOMTree backing route rejects record updates", + "fixture": "scripts/fixtures/negative/DOMTreeElementBackingRouteIsPrivate.res", + "features": ["WebAPI.DOMNodes"], + "expectedDiagnostic": "Cannot create values of the" + }, + { + "name": "generic native NodeList updates are private", + "fixture": "scripts/fixtures/negative/GenericNodeListUpdateIsPrivate.res", + "features": ["WebAPI.DOMNodes"], + "expectedDiagnostic": "Cannot create values of the" + }, + { + "name": "URL native record updates are private", + "fixture": "scripts/fixtures/negative/URLRecordUpdateIsPrivate.res", + "features": ["WebAPI.URL"], + "expectedDiagnostic": "Cannot create values of the" + }, + { + "name": "CSSStyleDeclaration record updates are private", + "fixture": "scripts/fixtures/negative/CSSStyleDeclarationUpdateIsPrivate.res", + "features": ["WebAPI.CSSOM"], + "expectedDiagnostic": "Cannot create values of the" + }, + { + "name": "AudioNode native record updates are private", + "fixture": "scripts/fixtures/negative/AudioNodeRecordUpdateIsPrivate.res", + "features": ["WebAPI.WebAudio"], + "expectedDiagnostic": "Cannot create values of the" + }, + { + "name": "DataTransfer native record updates are private", + "fixture": "scripts/fixtures/negative/DataTransferRecordUpdateIsPrivate.res", + "features": ["WebAPI.UIEvents"], + "expectedDiagnostic": "Cannot create values of the" + }, + { + "name": "MutationRecord native snapshot updates are private", + "fixture": "scripts/fixtures/negative/MutationRecordUpdateIsPrivate.res", + "features": ["WebAPI.Observers"], + "expectedDiagnostic": "Cannot create values of the" + }, { "name": "Web Locks callback accepts nullable Lock", "fixture": "scripts/fixtures/negative/WebLocksRejectsNonNullableCallback.res", diff --git a/src/animation/Animation.res b/src/animation/Animation.res index c6836ca3..7f038e54 100644 --- a/src/animation/Animation.res +++ b/src/animation/Animation.res @@ -22,7 +22,7 @@ type animationTimeline = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Animation) */ -type rec t = { +type rec t = private { ...DOM.eventTarget, /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Animation/id) @@ -78,6 +78,13 @@ external make: (~effect: AnimationEffect.t=?, ~timeline: animationTimeline=?) => include EventTarget.Impl({type t = t}) +@set external setId: (t, string) => unit = "id" +@set external setEffect: (t, Null.t) => unit = "effect" +@set external setTimeline: (t, Null.t) => unit = "timeline" +@set external setPlaybackRate: (t, float) => unit = "playbackRate" +@set external setStartTime: (t, Null.t) => unit = "startTime" +@set external setCurrentTime: (t, Null.t) => unit = "currentTime" + /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Animation/cancel) */ diff --git a/src/canvas/CanvasTypes.res b/src/canvas/CanvasTypes.res index 532085e1..5c457ba4 100644 --- a/src/canvas/CanvasTypes.res +++ b/src/canvas/CanvasTypes.res @@ -118,10 +118,9 @@ type webGLPowerPreference = /** [See OffscreenCanvas on MDN](https://developer.mozilla.org/docs/Web/API/OffscreenCanvas) -TODO: mark as private once mutating fields of private records is allowed */ @editor.completeFrom(OffscreenCanvas) -type offscreenCanvas = { +type offscreenCanvas = private { ...DOM.eventTarget, /** These attributes return the dimensions of the OffscreenCanvas object's bitmap. @@ -159,7 +158,7 @@ type imageBitmap = private { /** [See OffscreenCanvasRenderingContext2D on MDN](https://developer.mozilla.org/docs/Web/API/OffscreenCanvasRenderingContext2D) */ -type offscreenCanvasRenderingContext2D = { +type offscreenCanvasRenderingContext2D = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/CanvasRenderingContext2D/canvas) */ @@ -286,7 +285,7 @@ type imageBitmapRenderingContext = private { Provides an interface to the OpenGL ES 2.0 graphics rendering context for the drawing surface of an HTML element. [See WebGLRenderingContext on MDN](https://developer.mozilla.org/docs/Web/API/WebGLRenderingContext) */ -type webGLRenderingContext = { +type webGLRenderingContext = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/WebGLRenderingContext/canvas) */ @@ -312,7 +311,7 @@ type webGLRenderingContext = { /** [See WebGL2RenderingContext on MDN](https://developer.mozilla.org/docs/Web/API/WebGL2RenderingContext) */ -type webGL2RenderingContext = { +type webGL2RenderingContext = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/WebGLRenderingContext/canvas) */ @@ -360,7 +359,7 @@ type path2D = private {} The dimensions of a piece of text in the canvas, as created by the CanvasRenderingContext2D.measureText() method. [See TextMetrics on MDN](https://developer.mozilla.org/docs/Web/API/TextMetrics) */ -type textMetrics = { +type textMetrics = private { /** Returns the measurement described below. [Read more on MDN](https://developer.mozilla.org/docs/Web/API/TextMetrics/width) diff --git a/src/canvas/HTMLCanvasElement.res b/src/canvas/HTMLCanvasElement.res index a60d303d..2c110cd5 100644 --- a/src/canvas/HTMLCanvasElement.res +++ b/src/canvas/HTMLCanvasElement.res @@ -1,9 +1,8 @@ /** Provides properties and methods for manipulating the layout and presentation of elements. The HTMLCanvasElement interface also inherits the properties and methods of the HTMLElement interface. [See HTMLCanvasElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLCanvasElement) -TODO: mark as private once mutating fields of private records is allowed */ -type t = { +type t = private { ...DOMTree.htmlElement, /** Gets or sets the width of a canvas element on a document. @@ -19,6 +18,12 @@ type t = { include HTMLElement.Impl({type t = t}) +@set +external setWidth: (t, int) => unit = "width" + +@set +external setHeight: (t, int) => unit = "height" + /** `isInstanceOf(value)` diff --git a/src/canvas/OffscreenCanvas.res b/src/canvas/OffscreenCanvas.res index ed5e4e58..e9e2903c 100644 --- a/src/canvas/OffscreenCanvas.res +++ b/src/canvas/OffscreenCanvas.res @@ -4,6 +4,12 @@ @new external make: (~width: int, ~height: int) => CanvasTypes.offscreenCanvas = "OffscreenCanvas" +@set +external setWidth: (CanvasTypes.offscreenCanvas, int) => unit = "width" + +@set +external setHeight: (CanvasTypes.offscreenCanvas, int) => unit = "height" + include EventTarget.Impl({type t = CanvasTypes.offscreenCanvas}) /** diff --git a/src/canvas/OffscreenCanvasRenderingContext2D.res b/src/canvas/OffscreenCanvasRenderingContext2D.res new file mode 100644 index 00000000..776417c6 --- /dev/null +++ b/src/canvas/OffscreenCanvasRenderingContext2D.res @@ -0,0 +1,102 @@ +/** Native writable properties exposed by OffscreenCanvasRenderingContext2D. */ +@set +external setGlobalAlpha: (CanvasTypes.offscreenCanvasRenderingContext2D, float) => unit = + "globalAlpha" +@set +external setGlobalCompositeOperation: ( + CanvasTypes.offscreenCanvasRenderingContext2D, + CanvasTypes.globalCompositeOperation, +) => unit = "globalCompositeOperation" +@set +external setImageSmoothingEnabled: (CanvasTypes.offscreenCanvasRenderingContext2D, bool) => unit = + "imageSmoothingEnabled" +@set +external setImageSmoothingQuality: ( + CanvasTypes.offscreenCanvasRenderingContext2D, + CanvasTypes.imageSmoothingQuality, +) => unit = "imageSmoothingQuality" +@set +external setStrokeStyle: ( + CanvasTypes.offscreenCanvasRenderingContext2D, + CanvasTypes.fillStyle, +) => unit = "strokeStyle" +@set +external setFillStyle: ( + CanvasTypes.offscreenCanvasRenderingContext2D, + CanvasTypes.fillStyle, +) => unit = "fillStyle" +@set +external setShadowOffsetX: (CanvasTypes.offscreenCanvasRenderingContext2D, float) => unit = + "shadowOffsetX" +@set +external setShadowOffsetY: (CanvasTypes.offscreenCanvasRenderingContext2D, float) => unit = + "shadowOffsetY" +@set +external setShadowBlur: (CanvasTypes.offscreenCanvasRenderingContext2D, float) => unit = + "shadowBlur" +@set +external setShadowColor: (CanvasTypes.offscreenCanvasRenderingContext2D, string) => unit = + "shadowColor" +@set +external setFilter: (CanvasTypes.offscreenCanvasRenderingContext2D, string) => unit = "filter" +@set +external setLineWidth: (CanvasTypes.offscreenCanvasRenderingContext2D, float) => unit = "lineWidth" +@set +external setLineCap: ( + CanvasTypes.offscreenCanvasRenderingContext2D, + CanvasTypes.canvasLineCap, +) => unit = "lineCap" +@set +external setLineJoin: ( + CanvasTypes.offscreenCanvasRenderingContext2D, + CanvasTypes.canvasLineJoin, +) => unit = "lineJoin" +@set +external setMiterLimit: (CanvasTypes.offscreenCanvasRenderingContext2D, float) => unit = + "miterLimit" +@set +external setLineDashOffset: (CanvasTypes.offscreenCanvasRenderingContext2D, float) => unit = + "lineDashOffset" +@set +external setFont: (CanvasTypes.offscreenCanvasRenderingContext2D, string) => unit = "font" +@set +external setTextAlign: ( + CanvasTypes.offscreenCanvasRenderingContext2D, + CanvasTypes.canvasTextAlign, +) => unit = "textAlign" +@set +external setTextBaseline: ( + CanvasTypes.offscreenCanvasRenderingContext2D, + CanvasTypes.canvasTextBaseline, +) => unit = "textBaseline" +@set +external setDirection: ( + CanvasTypes.offscreenCanvasRenderingContext2D, + CanvasTypes.canvasDirection, +) => unit = "direction" +@set +external setLetterSpacing: (CanvasTypes.offscreenCanvasRenderingContext2D, string) => unit = + "letterSpacing" +@set +external setFontKerning: ( + CanvasTypes.offscreenCanvasRenderingContext2D, + CanvasTypes.canvasFontKerning, +) => unit = "fontKerning" +@set +external setFontStretch: ( + CanvasTypes.offscreenCanvasRenderingContext2D, + CanvasTypes.canvasFontStretch, +) => unit = "fontStretch" +@set +external setFontVariantCaps: ( + CanvasTypes.offscreenCanvasRenderingContext2D, + CanvasTypes.canvasFontVariantCaps, +) => unit = "fontVariantCaps" +@set +external setTextRendering: ( + CanvasTypes.offscreenCanvasRenderingContext2D, + CanvasTypes.canvasTextRendering, +) => unit = "textRendering" +@set +external setWordSpacing: (CanvasTypes.offscreenCanvasRenderingContext2D, string) => unit = + "wordSpacing" diff --git a/src/canvas/WebGL2RenderingContext.res b/src/canvas/WebGL2RenderingContext.res new file mode 100644 index 00000000..fb0abcd4 --- /dev/null +++ b/src/canvas/WebGL2RenderingContext.res @@ -0,0 +1,12 @@ +/** Native writable color-space properties exposed by WebGL2 contexts. */ +@set +external setDrawingBufferColorSpace: ( + CanvasTypes.webGL2RenderingContext, + CanvasTypes.predefinedColorSpace, +) => unit = "drawingBufferColorSpace" + +@set +external setUnpackColorSpace: ( + CanvasTypes.webGL2RenderingContext, + CanvasTypes.predefinedColorSpace, +) => unit = "unpackColorSpace" diff --git a/src/canvas/WebGLRenderingContext.res b/src/canvas/WebGLRenderingContext.res new file mode 100644 index 00000000..5bf4e739 --- /dev/null +++ b/src/canvas/WebGLRenderingContext.res @@ -0,0 +1,12 @@ +/** Native writable color-space properties exposed by WebGL contexts. */ +@set +external setDrawingBufferColorSpace: ( + CanvasTypes.webGLRenderingContext, + CanvasTypes.predefinedColorSpace, +) => unit = "drawingBufferColorSpace" + +@set +external setUnpackColorSpace: ( + CanvasTypes.webGLRenderingContext, + CanvasTypes.predefinedColorSpace, +) => unit = "unpackColorSpace" diff --git a/src/core/DOM.res b/src/core/DOM.res index 50a9a5cd..e1371dfb 100644 --- a/src/core/DOM.res +++ b/src/core/DOM.res @@ -11,7 +11,7 @@ An abnormal event (called an exception) which occurs as a result of calling a me [See DOMException on MDN](https://developer.mozilla.org/docs/Web/API/DOMException) */ @editor.completeFrom(DOMException) -type domException = { +type domException = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/DOMException/name) */ @@ -27,7 +27,7 @@ A type returned by some APIs which contains a list of DOMString (strings). [See DOMStringList on MDN](https://developer.mozilla.org/docs/Web/API/DOMStringList) */ @editor.completeFrom(DOMStringList) -type domStringList = { +type domStringList = private { /** Returns the number of strings in strings. [Read more on MDN](https://developer.mozilla.org/docs/Web/API/DOMStringList/length) @@ -201,7 +201,7 @@ type shareData = { /** [See UserActivation on MDN](https://developer.mozilla.org/docs/Web/API/UserActivation) */ -type userActivation = { +type userActivation = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/UserActivation/hasBeenActive) */ @@ -219,9 +219,8 @@ The state and the identity of the user agent. It allows scripts to query it and @editor.completeFrom(Navigator) type navigator -// TODO: mark as private once mutating fields of private records is allowed @editor.completeFrom(DOMTokenList) -type domTokenList = { +type domTokenList = private { /** Returns the number of tokens. [Read more on MDN](https://developer.mozilla.org/docs/Web/API/DOMTokenList/length) @@ -239,7 +238,7 @@ Can be set, to change the associated attribute. /** [See FragmentDirective on MDN](https://developer.mozilla.org/docs/Web/API/FragmentDirective) */ -type fragmentDirective = {} +type fragmentDirective = private {} /** [See CustomElementRegistry on MDN](https://developer.mozilla.org/docs/Web/API/CustomElementRegistry) @@ -250,7 +249,7 @@ type customElementRegistry = private {} /** [See BarProp on MDN](https://developer.mozilla.org/docs/Web/API/BarProp) */ -type barProp = { +type barProp = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/BarProp/visible) */ @@ -401,7 +400,7 @@ type screenOrientation = private { A screen, usually the one on which the current window is being rendered, and is obtained using window.screen. [See Screen on MDN](https://developer.mozilla.org/docs/Web/API/Screen) */ -type screen = { +type screen = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Screen/availWidth) */ @@ -516,7 +515,7 @@ type domRect = private { The validity states that an element can be in, with respect to constraint validation. Together, they help explain why an element's value fails to validate, if it's not valid. [See ValidityState on MDN](https://developer.mozilla.org/docs/Web/API/ValidityState) */ -type validityState = { +type validityState = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/ValidityState/valueMissing) */ diff --git a/src/crypto/Crypto.res b/src/crypto/Crypto.res index 485e9bd6..a02f2b34 100644 --- a/src/crypto/Crypto.res +++ b/src/crypto/Crypto.res @@ -9,4 +9,4 @@ external getRandomValues: (WebCryptoTypes.crypto, 't) => 't = "getRandomValues" */ @send external randomUUID: WebCryptoTypes.crypto => string = "randomUUID" -type t = WebCryptoTypes.crypto +type t = WebCryptoTypes.crypto = private {...WebCryptoTypes.crypto} diff --git a/src/crypto/SubtleCrypto.res b/src/crypto/SubtleCrypto.res index 9d8f29ad..8c733df3 100644 --- a/src/crypto/SubtleCrypto.res +++ b/src/crypto/SubtleCrypto.res @@ -1,4 +1,4 @@ -type t = WebCryptoTypes.subtleCrypto +type t = WebCryptoTypes.subtleCrypto = private {...WebCryptoTypes.subtleCrypto} type binaryData = ArrayBufferTypedArrayOrDataView.t type keyType = WebCryptoTypes.keyType type keyUsage = WebCryptoTypes.keyUsage @@ -11,7 +11,7 @@ type binaryKeyFormat = | @as("spki") Spki type jwkKeyFormat = | @as("jwk") Jwk type keyAlgorithm = WebCryptoTypes.keyAlgorithm -type cryptoKey = WebCryptoTypes.cryptoKey +type cryptoKey = WebCryptoTypes.cryptoKey = private {...WebCryptoTypes.cryptoKey} type cryptoKeyPair = WebCryptoTypes.cryptoKeyPair external algorithmString: string => algorithmIdentifier = "%identity" diff --git a/src/crypto/WebCryptoTypes.res b/src/crypto/WebCryptoTypes.res index 17565a5b..ecc7ca13 100644 --- a/src/crypto/WebCryptoTypes.res +++ b/src/crypto/WebCryptoTypes.res @@ -46,7 +46,7 @@ type crypto = private { The CryptoKey dictionary of the Web Crypto API represents a cryptographic key. [See CryptoKey on MDN](https://developer.mozilla.org/docs/Web/API/CryptoKey) */ -type cryptoKey = { +type cryptoKey = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/CryptoKey/type) */ diff --git a/src/css-font-loading/CssFontLoadingTypes.res b/src/css-font-loading/CssFontLoadingTypes.res index 4885c35d..05fb0f62 100644 --- a/src/css-font-loading/CssFontLoadingTypes.res +++ b/src/css-font-loading/CssFontLoadingTypes.res @@ -19,10 +19,9 @@ type fontFaceSetLoadStatus = BaseCSSFontLoading.fontFaceSetLoadStatus = /** [See FontFace on MDN](https://developer.mozilla.org/docs/Web/API/FontFace) -TODO: mark as private once mutating fields of private records is allowed */ @editor.completeFrom(FontFace) -type rec fontFace = { +type rec fontFace = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/FontFace/family) */ @@ -77,7 +76,7 @@ type rec fontFace = { [See FontFaceSet on MDN](https://developer.mozilla.org/docs/Web/API/FontFaceSet) */ @editor.completeFrom(FontFaceSet) -type fontFaceSet = BaseCSSFontLoading.fontFaceSet +type fontFaceSet = BaseCSSFontLoading.fontFaceSet = private {...BaseCSSFontLoading.fontFaceSet} type fontFaceDescriptors = { mutable style?: string, diff --git a/src/css-font-loading/FontFace.res b/src/css-font-loading/FontFace.res index 4f8defe9..829cb225 100644 --- a/src/css-font-loading/FontFace.res +++ b/src/css-font-loading/FontFace.res @@ -1,4 +1,15 @@ -type t = CssFontLoadingTypes.fontFace = {...CssFontLoadingTypes.fontFace} +type t = CssFontLoadingTypes.fontFace = private {...CssFontLoadingTypes.fontFace} + +@set external setFamily: (t, string) => unit = "family" +@set external setStyle: (t, string) => unit = "style" +@set external setWeight: (t, string) => unit = "weight" +@set external setStretch: (t, string) => unit = "stretch" +@set external setUnicodeRange: (t, string) => unit = "unicodeRange" +@set external setFeatureSettings: (t, string) => unit = "featureSettings" +@set external setDisplay: (t, CssFontLoadingTypes.fontDisplay) => unit = "display" +@set external setAscentOverride: (t, string) => unit = "ascentOverride" +@set external setDescentOverride: (t, string) => unit = "descentOverride" +@set external setLineGapOverride: (t, string) => unit = "lineGapOverride" /** `fromString(~family: string, ~source: string, ~descriptors: fontFaceDescriptors=?)` diff --git a/src/cssom/CSSStyleDeclaration.res b/src/cssom/CSSStyleDeclaration.res index 42e7ae6f..50d82cc0 100644 --- a/src/cssom/CSSStyleDeclaration.res +++ b/src/cssom/CSSStyleDeclaration.res @@ -1,5 +1,5 @@ @editor.completeFrom(CSSStyleDeclaration) -type t = { +type t = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/CSSStyleDeclaration/cssText) */ @@ -1702,6 +1702,13 @@ type t = { mutable cssFloat: string, } +/** +Replaces the declaration block's complete serialized contents. +[Read more on MDN](https://developer.mozilla.org/docs/Web/API/CSSStyleDeclaration/cssText) +*/ +@set +external setCssText: (t, string) => unit = "cssText" + /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/CSSStyleDeclaration/item) */ diff --git a/src/cssom/MediaList.res b/src/cssom/MediaList.res index 0472d672..4a7553f1 100644 --- a/src/cssom/MediaList.res +++ b/src/cssom/MediaList.res @@ -1,5 +1,5 @@ @editor.completeFrom(MediaList) -type t = { +type t = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/MediaList/mediaText) */ @@ -10,6 +10,9 @@ type t = { length: int, } +@set +external setMediaText: (t, string) => unit = "mediaText" + /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/MediaList/item) */ diff --git a/src/cssom/StyleSheet.res b/src/cssom/StyleSheet.res index a47fe7a5..9df5ea07 100644 --- a/src/cssom/StyleSheet.res +++ b/src/cssom/StyleSheet.res @@ -4,7 +4,7 @@ A single style sheet. CSS style sheets will further implement the more specialized CSSStyleSheet interface. [See StyleSheet on MDN](https://developer.mozilla.org/docs/Web/API/StyleSheet) */ -type rec t = { +type rec t = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/StyleSheet/type) */ @@ -39,9 +39,8 @@ type rec t = { /** A single CSS style sheet. It inherits properties and methods from its parent, StyleSheet. [See CSSStyleSheet on MDN](https://developer.mozilla.org/docs/Web/API/CSSStyleSheet) -TODO: mark as private once mutating fields of private records is allowed */ -@editor.completeFrom(CSSStyleSheet) and cssStyleSheet = { +@editor.completeFrom(CSSStyleSheet) and cssStyleSheet = private { // Base properties from StyleSheet /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/StyleSheet/type) @@ -88,7 +87,7 @@ TODO: mark as private once mutating fields of private records is allowed A single CSS rule. There are several types of rules, listed in the Type constants section below. [See CSSRule on MDN](https://developer.mozilla.org/docs/Web/API/CSSRule) */ -and cssRule = { +and cssRule = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/CSSRule/cssText) */ @@ -114,6 +113,10 @@ A CSSRuleList is an (indirect-modify only) array-like object containing an order length: int, } +@set external setDisabled: (t, bool) => unit = "disabled" +@set external setCSSStyleSheetDisabled: (cssStyleSheet, bool) => unit = "disabled" +@set external setCSSRuleCssText: (cssRule, string) => unit = "cssText" + type init = { mutable baseURL?: string, mutable media?: unknown, diff --git a/src/device/GamepadTypes.res b/src/device/GamepadTypes.res index 702ff322..0a8420a0 100644 --- a/src/device/GamepadTypes.res +++ b/src/device/GamepadTypes.res @@ -15,7 +15,7 @@ type gamepadHapticsResult = An individual button of a gamepad or other controller, allowing access to the current state of different types of buttons available on the control device. [See GamepadButton on MDN](https://developer.mozilla.org/docs/Web/API/GamepadButton) */ -type gamepadButton = { +type gamepadButton = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/GamepadButton/pressed) */ @@ -41,7 +41,7 @@ type gamepadHapticActuator = private {} This WebApiGamepad API interface defines an individual gamepad or other controller, allowing access to information such as button presses, axis positions, and id. [See WebApiGamepad on MDN](https://developer.mozilla.org/docs/Web/API/Gamepad) */ -type gamepad = { +type gamepad = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Gamepad/id) */ diff --git a/src/device/GeolocationTypes.res b/src/device/GeolocationTypes.res index d9149242..6bf32eec 100644 --- a/src/device/GeolocationTypes.res +++ b/src/device/GeolocationTypes.res @@ -59,7 +59,7 @@ type geolocationPosition = private { /** [See GeolocationPositionError on MDN](https://developer.mozilla.org/docs/Web/API/GeolocationPositionError) */ -type geolocationPositionError = { +type geolocationPositionError = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/GeolocationPositionError/code) */ diff --git a/src/device/ScreenOrientation.res b/src/device/ScreenOrientation.res index d4e74797..e5605af7 100644 --- a/src/device/ScreenOrientation.res +++ b/src/device/ScreenOrientation.res @@ -1,4 +1,5 @@ -include EventTarget.Impl({type t = DOM.screenOrientation}) +type t = DOM.screenOrientation = private {...DOM.screenOrientation} +include EventTarget.Impl({type t = t}) /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/ScreenOrientation/unlock) diff --git a/src/device/WebLocksTypes.res b/src/device/WebLocksTypes.res index 69cea70c..187ca9ea 100644 --- a/src/device/WebLocksTypes.res +++ b/src/device/WebLocksTypes.res @@ -13,7 +13,7 @@ type lockManager = private {} /** [See Lock on MDN](https://developer.mozilla.org/docs/Web/API/Lock) */ -type lock = { +type lock = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Lock/name) */ diff --git a/src/device/WebMIDI.res b/src/device/WebMIDI.res index c46c76ad..a458633e 100644 --- a/src/device/WebMIDI.res +++ b/src/device/WebMIDI.res @@ -1,4 +1,4 @@ -type t = WebMidiTypes.midiAccess = {...WebMidiTypes.midiAccess} +type t = WebMidiTypes.midiAccess = private {...WebMidiTypes.midiAccess} include EventTarget.Impl({type t = t}) diff --git a/src/device/WebMidiTypes.res b/src/device/WebMidiTypes.res index 3c2eab56..16aa0787 100644 --- a/src/device/WebMidiTypes.res +++ b/src/device/WebMidiTypes.res @@ -3,18 +3,18 @@ /** [See MIDIInputMap on MDN](https://developer.mozilla.org/docs/Web/API/MIDIInputMap) */ -type midiInputMap = {} +type midiInputMap = private {} /** [See MIDIOutputMap on MDN](https://developer.mozilla.org/docs/Web/API/MIDIOutputMap) */ -type midiOutputMap = {} +type midiOutputMap = private {} /** [See MIDIAccess on MDN](https://developer.mozilla.org/docs/Web/API/MIDIAccess) */ @editor.completeFrom(WebMIDI) -type midiAccess = { +type midiAccess = private { ...DOM.eventTarget, /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/MIDIAccess/inputs) diff --git a/src/dom-nodes/AbstractRange.res b/src/dom-nodes/AbstractRange.res index cdc09c8e..aaf9b210 100644 --- a/src/dom-nodes/AbstractRange.res +++ b/src/dom-nodes/AbstractRange.res @@ -1,7 +1,7 @@ /** [See AbstractRange on MDN](https://developer.mozilla.org/docs/Web/API/AbstractRange) */ -type t = { +type t = private { /** Returns range's start node. [Read more on MDN](https://developer.mozilla.org/docs/Web/API/AbstractRange/startContainer) diff --git a/src/dom-nodes/Attr.res b/src/dom-nodes/Attr.res index a21ac964..4d503956 100644 --- a/src/dom-nodes/Attr.res +++ b/src/dom-nodes/Attr.res @@ -2,7 +2,7 @@ A WebApiDOM element's attribute as an object. In most WebApiDOM methods, you will probably directly retrieve the attribute as a string (e.g., Element.getAttribute(), but certain functions (e.g., Element.getAttributeNode()) or means of iterating give Attr types. [See Attr on MDN](https://developer.mozilla.org/docs/Web/API/Attr) */ -type t = { +type t = private { // Base properties from Node /** Returns the type of node. @@ -99,3 +99,6 @@ type t = { */ ownerElement: Null.t, } + +@set +external setValue: (t, string) => unit = "value" diff --git a/src/dom-nodes/CDATASection.res b/src/dom-nodes/CDATASection.res index 11809c99..4a72fb49 100644 --- a/src/dom-nodes/CDATASection.res +++ b/src/dom-nodes/CDATASection.res @@ -2,6 +2,6 @@ A CDATA section that can be used within XML to include extended portions of unescaped text. The symbols < and & don't need escaping as they normally do when inside a CDATA section. [See CDATASection on MDN](https://developer.mozilla.org/docs/Web/API/CDATASection) */ -type t = { +type t = private { ...Text.t, } diff --git a/src/dom-nodes/CharacterData.res b/src/dom-nodes/CharacterData.res index e9ec7107..c0a3bd36 100644 --- a/src/dom-nodes/CharacterData.res +++ b/src/dom-nodes/CharacterData.res @@ -1,9 +1,8 @@ /** The CharacterData abstract interface represents a Node object that contains characters. This is an abstract interface, meaning there aren't any object of type CharacterData: it is implemented by other interfaces, like Text, Comment, or ProcessingInstruction which aren't abstract. [See CharacterData on MDN](https://developer.mozilla.org/docs/Web/API/CharacterData) -TODO: mark as private once mutating fields of private records is allowed */ -type t = { +type t = private { // Base properties from Node /** Returns the type of node. @@ -104,6 +103,9 @@ module Impl = ( external asCharacterData: T.t => t = "%identity" + @set + external setData: (T.t, string) => unit = "data" + /** Inserts nodes just after node, while replacing strings in nodes with equivalent Text nodes. diff --git a/src/dom-nodes/DOMImplementation.res b/src/dom-nodes/DOMImplementation.res index 40b46a92..9d8a7d2b 100644 --- a/src/dom-nodes/DOMImplementation.res +++ b/src/dom-nodes/DOMImplementation.res @@ -8,7 +8,7 @@ type t = private {} A Node containing a doctype. [See DocumentType on MDN](https://developer.mozilla.org/docs/Web/API/DocumentType) */ -type documentType = { +type documentType = private { // Base properties from Node /** Returns the type of node. @@ -98,7 +98,7 @@ type documentType = { An XML document. It inherits from the generic Document and does not add any specific methods or properties to it: nevertheless, several algorithms behave differently with the two types of documents. [See XMLDocument on MDN](https://developer.mozilla.org/docs/Web/API/XMLDocument) */ -type xmlDocument = { +type xmlDocument = private { ...DOM.document, } diff --git a/src/dom-nodes/DOMTree.res b/src/dom-nodes/DOMTree.res index 9611fd51..72b99f00 100644 --- a/src/dom-nodes/DOMTree.res +++ b/src/dom-nodes/DOMTree.res @@ -8,15 +8,14 @@ type slotAssignmentMode = | @as("manual") Manual | @as("named") Named -type customStateSet = {} +type customStateSet = private {} /** Node is an interface from which a number of WebApiDOM API object types inherit. It allows those types to be treated similarly; for example, inheriting the same set of methods, or being tested in the same way. [See Node on MDN](https://developer.mozilla.org/docs/Web/API/Node) -TODO: mark as private once mutating fields of private records is allowed */ @editor.completeFrom(Node) -type rec node = { +type rec node = private { ...DOM.eventTarget, /** Returns the type of node. @@ -91,9 +90,8 @@ type rec node = { /** Element is the most general base class from which all objects in a Document inherit. It only has methods and properties common to all kinds of elements. More specific classes inherit from Element. [See Element on MDN](https://developer.mozilla.org/docs/Web/API/Element) -TODO: mark as private once mutating fields of private records is allowed */ -@editor.completeFrom(Element) and element = { +@editor.completeFrom(Element) and element = private { // Base properties from Node /** Returns the type of node. @@ -461,9 +459,8 @@ TODO: mark as private once mutating fields of private records is allowed /** A minimal document object that has no parent. It is used as a lightweight version of Document that stores a segment of a document structure comprised of nodes just like a standard document. The key difference is that because the document fragment isn't part of the active document tree structure, changes made to the fragment don't affect the document, cause reflow, or incur any performance impact that can occur when changes are made. [See DocumentFragment on MDN](https://developer.mozilla.org/docs/Web/API/DocumentFragment) -TODO: mark as private once mutating fields of private records is allowed */ -@editor.completeFrom(DocumentFragment) and documentFragment = { +@editor.completeFrom(DocumentFragment) and documentFragment = private { // Base properties from Node /** Returns the type of node. @@ -558,9 +555,8 @@ TODO: mark as private once mutating fields of private records is allowed /** [See ShadowRoot on MDN](https://developer.mozilla.org/docs/Web/API/ShadowRoot) -TODO: mark as private once mutating fields of private records is allowed */ -@editor.completeFrom(ShadowRoot) and shadowRoot = { +@editor.completeFrom(ShadowRoot) and shadowRoot = private { // Base properties from DocumentFragment // End base properties from DocumentFragment @@ -701,9 +697,8 @@ Similarly, when the focused element is in a different node tree than documentOrS /** Any HTML element. Some elements directly implement this interface, while others implement it via an interface that inherits it. [See HTMLElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLElement) -TODO: mark as private once mutating fields of private records is allowed */ -@editor.completeFrom(HTMLElement) and htmlElement = { +@editor.completeFrom(HTMLElement) and htmlElement = private { // Base properties from Element /** Returns the namespace. @@ -994,9 +989,8 @@ TODO: mark as private once mutating fields of private records is allowed /** A
element in the WebApiDOM; it allows access to and in some cases modification of aspects of the form, as well as access to its component elements. [See HTMLFormElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLFormElement) -TODO: mark as private once mutating fields of private records is allowed */ -@editor.completeFrom(HTMLFormElement) and htmlFormElement = { +@editor.completeFrom(HTMLFormElement) and htmlFormElement = private { // Base properties from HTMLElement /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/HTMLElement/title) @@ -1298,12 +1292,12 @@ TODO: mark as private once mutating fields of private records is allowed } @editor.completeFrom(HTMLFormControlsCollection) -and htmlFormControlsCollection = { +and htmlFormControlsCollection = private { length: int, } @editor.completeFrom(HTMLSlotElement) -and htmlSlotElement = { +and htmlSlotElement = private { // Base properties from Element /** Returns the namespace. @@ -1596,7 +1590,7 @@ and htmlSlotElement = { } @editor.completeFrom(ElementInternals) -and elementInternals = { +and elementInternals = private { shadowRoot: Null.t, form: Null.t, willValidate: bool, diff --git a/src/dom-nodes/Element.res b/src/dom-nodes/Element.res index 7ae7d68e..0f2dcaf2 100644 --- a/src/dom-nodes/Element.res +++ b/src/dom-nodes/Element.res @@ -11,6 +11,108 @@ module Impl = ( external asElement: T.t => DOMTree.element = "%identity" + @set + external setId: (T.t, string) => unit = "id" + + @set + external setSlot: (T.t, string) => unit = "slot" + + @set + external setScrollTop: (T.t, float) => unit = "scrollTop" + + @set + external setScrollLeft: (T.t, float) => unit = "scrollLeft" + + @throws(JsExn) @set + external setInnerHTML: (T.t, string) => unit = "innerHTML" + + @throws(JsExn) @set + external setOuterHTML: (T.t, string) => unit = "outerHTML" + + @set + external setAriaAtomic: (T.t, Null.t) => unit = "ariaAtomic" + @set + external setAriaAutoComplete: (T.t, Null.t) => unit = "ariaAutoComplete" + @set + external setAriaBrailleLabel: (T.t, Null.t) => unit = "ariaBrailleLabel" + @set + external setAriaBrailleRoleDescription: (T.t, Null.t) => unit = + "ariaBrailleRoleDescription" + @set + external setAriaBusy: (T.t, Null.t) => unit = "ariaBusy" + @set + external setAriaChecked: (T.t, Null.t) => unit = "ariaChecked" + @set + external setAriaColCount: (T.t, Null.t) => unit = "ariaColCount" + @set + external setAriaColIndex: (T.t, Null.t) => unit = "ariaColIndex" + @set + external setAriaColIndexText: (T.t, Null.t) => unit = "ariaColIndexText" + @set + external setAriaColSpan: (T.t, Null.t) => unit = "ariaColSpan" + @set + external setAriaCurrent: (T.t, Null.t) => unit = "ariaCurrent" + @set + external setAriaDescription: (T.t, Null.t) => unit = "ariaDescription" + @set + external setAriaDisabled: (T.t, Null.t) => unit = "ariaDisabled" + @set + external setAriaExpanded: (T.t, Null.t) => unit = "ariaExpanded" + @set + external setAriaHasPopup: (T.t, Null.t) => unit = "ariaHasPopup" + @set + external setAriaHidden: (T.t, Null.t) => unit = "ariaHidden" + @set + external setAriaKeyShortcuts: (T.t, Null.t) => unit = "ariaKeyShortcuts" + @set + external setAriaLabel: (T.t, Null.t) => unit = "ariaLabel" + @set + external setAriaLevel: (T.t, Null.t) => unit = "ariaLevel" + @set + external setAriaLive: (T.t, Null.t) => unit = "ariaLive" + @set + external setAriaModal: (T.t, Null.t) => unit = "ariaModal" + @set + external setAriaMultiLine: (T.t, Null.t) => unit = "ariaMultiLine" + @set + external setAriaMultiSelectable: (T.t, Null.t) => unit = "ariaMultiSelectable" + @set + external setAriaOrientation: (T.t, Null.t) => unit = "ariaOrientation" + @set + external setAriaPlaceholder: (T.t, Null.t) => unit = "ariaPlaceholder" + @set + external setAriaPosInSet: (T.t, Null.t) => unit = "ariaPosInSet" + @set + external setAriaPressed: (T.t, Null.t) => unit = "ariaPressed" + @set + external setAriaReadOnly: (T.t, Null.t) => unit = "ariaReadOnly" + @set + external setAriaRequired: (T.t, Null.t) => unit = "ariaRequired" + @set + external setAriaRoleDescription: (T.t, Null.t) => unit = "ariaRoleDescription" + @set + external setAriaRowCount: (T.t, Null.t) => unit = "ariaRowCount" + @set + external setAriaRowIndex: (T.t, Null.t) => unit = "ariaRowIndex" + @set + external setAriaRowIndexText: (T.t, Null.t) => unit = "ariaRowIndexText" + @set + external setAriaRowSpan: (T.t, Null.t) => unit = "ariaRowSpan" + @set + external setAriaSelected: (T.t, Null.t) => unit = "ariaSelected" + @set + external setAriaSetSize: (T.t, Null.t) => unit = "ariaSetSize" + @set + external setAriaSort: (T.t, Null.t) => unit = "ariaSort" + @set + external setAriaValueMax: (T.t, Null.t) => unit = "ariaValueMax" + @set + external setAriaValueMin: (T.t, Null.t) => unit = "ariaValueMin" + @set + external setAriaValueNow: (T.t, Null.t) => unit = "ariaValueNow" + @set + external setAriaValueText: (T.t, Null.t) => unit = "ariaValueText" + /** Inserts nodes just after node, while replacing strings in nodes with equivalent Text nodes. diff --git a/src/dom-nodes/ElementInternals.res b/src/dom-nodes/ElementInternals.res index b4d942cc..fd975c59 100644 --- a/src/dom-nodes/ElementInternals.res +++ b/src/dom-nodes/ElementInternals.res @@ -2,6 +2,49 @@ type t = DOMTree.elementInternals = private { ...DOMTree.elementInternals, } +@set external setAriaAtomic: (t, Null.t) => unit = "ariaAtomic" +@set external setAriaAutoComplete: (t, Null.t) => unit = "ariaAutoComplete" +@set external setAriaBrailleLabel: (t, Null.t) => unit = "ariaBrailleLabel" +@set +external setAriaBrailleRoleDescription: (t, Null.t) => unit = "ariaBrailleRoleDescription" +@set external setAriaBusy: (t, Null.t) => unit = "ariaBusy" +@set external setAriaChecked: (t, Null.t) => unit = "ariaChecked" +@set external setAriaColCount: (t, Null.t) => unit = "ariaColCount" +@set external setAriaColIndex: (t, Null.t) => unit = "ariaColIndex" +@set external setAriaColIndexText: (t, Null.t) => unit = "ariaColIndexText" +@set external setAriaColSpan: (t, Null.t) => unit = "ariaColSpan" +@set external setAriaCurrent: (t, Null.t) => unit = "ariaCurrent" +@set external setAriaDescription: (t, Null.t) => unit = "ariaDescription" +@set external setAriaDisabled: (t, Null.t) => unit = "ariaDisabled" +@set external setAriaExpanded: (t, Null.t) => unit = "ariaExpanded" +@set external setAriaHasPopup: (t, Null.t) => unit = "ariaHasPopup" +@set external setAriaHidden: (t, Null.t) => unit = "ariaHidden" +@set external setAriaKeyShortcuts: (t, Null.t) => unit = "ariaKeyShortcuts" +@set external setAriaLabel: (t, Null.t) => unit = "ariaLabel" +@set external setAriaLevel: (t, Null.t) => unit = "ariaLevel" +@set external setAriaLive: (t, Null.t) => unit = "ariaLive" +@set external setAriaModal: (t, Null.t) => unit = "ariaModal" +@set external setAriaMultiLine: (t, Null.t) => unit = "ariaMultiLine" +@set external setAriaMultiSelectable: (t, Null.t) => unit = "ariaMultiSelectable" +@set external setAriaOrientation: (t, Null.t) => unit = "ariaOrientation" +@set external setAriaPlaceholder: (t, Null.t) => unit = "ariaPlaceholder" +@set external setAriaPosInSet: (t, Null.t) => unit = "ariaPosInSet" +@set external setAriaPressed: (t, Null.t) => unit = "ariaPressed" +@set external setAriaReadOnly: (t, Null.t) => unit = "ariaReadOnly" +@set external setAriaRequired: (t, Null.t) => unit = "ariaRequired" +@set external setAriaRoleDescription: (t, Null.t) => unit = "ariaRoleDescription" +@set external setAriaRowCount: (t, Null.t) => unit = "ariaRowCount" +@set external setAriaRowIndex: (t, Null.t) => unit = "ariaRowIndex" +@set external setAriaRowIndexText: (t, Null.t) => unit = "ariaRowIndexText" +@set external setAriaRowSpan: (t, Null.t) => unit = "ariaRowSpan" +@set external setAriaSelected: (t, Null.t) => unit = "ariaSelected" +@set external setAriaSetSize: (t, Null.t) => unit = "ariaSetSize" +@set external setAriaSort: (t, Null.t) => unit = "ariaSort" +@set external setAriaValueMax: (t, Null.t) => unit = "ariaValueMax" +@set external setAriaValueMin: (t, Null.t) => unit = "ariaValueMin" +@set external setAriaValueNow: (t, Null.t) => unit = "ariaValueNow" +@set external setAriaValueText: (t, Null.t) => unit = "ariaValueText" + /** Sets both the state and submission value of internals's target element to value. diff --git a/src/dom-nodes/Node.res b/src/dom-nodes/Node.res index 6d35787a..59206404 100644 --- a/src/dom-nodes/Node.res +++ b/src/dom-nodes/Node.res @@ -11,6 +11,12 @@ module Impl = ( external asNode: T.t => DOMTree.node = "%identity" + @set + external setNodeValue: (T.t, Null.t) => unit = "nodeValue" + + @set + external setTextContent: (T.t, Null.t) => unit = "textContent" + type getRootNodeOptions = {mutable composed?: bool} /** diff --git a/src/dom-nodes/ProcessingInstruction.res b/src/dom-nodes/ProcessingInstruction.res index 8084d0c5..97c75514 100644 --- a/src/dom-nodes/ProcessingInstruction.res +++ b/src/dom-nodes/ProcessingInstruction.res @@ -2,7 +2,7 @@ A processing instruction embeds application-specific instructions in XML which can be ignored by other applications that don't recognize them. [See ProcessingInstruction on MDN](https://developer.mozilla.org/docs/Web/API/ProcessingInstruction) */ -type t = { +type t = private { ...CharacterData.t, /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/ProcessingInstruction/target) diff --git a/src/dom-nodes/ShadowRoot.res b/src/dom-nodes/ShadowRoot.res index d7ce0cf1..d35c6972 100644 --- a/src/dom-nodes/ShadowRoot.res +++ b/src/dom-nodes/ShadowRoot.res @@ -4,6 +4,20 @@ type t = DOMTree.shadowRoot = private { include DocumentFragment.Impl({type t = t}) +/** +Sets the serialized HTML contents of this shadow root. +[Read more on MDN](https://developer.mozilla.org/docs/Web/API/ShadowRoot/innerHTML) +*/ +@throws(JsExn) @set +external setInnerHTML: (t, string) => unit = "innerHTML" + +/** +Sets the adopted style sheets for this shadow root. +[Read more on MDN](https://developer.mozilla.org/docs/Web/API/Document/adoptedStyleSheets) +*/ +@throws(JsExn) @set +external setAdoptedStyleSheets: (t, array) => unit = "adoptedStyleSheets" + /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Document/getAnimations) */ diff --git a/src/dom-nodes/TreeWalker.res b/src/dom-nodes/TreeWalker.res index a5f00340..46f1c218 100644 --- a/src/dom-nodes/TreeWalker.res +++ b/src/dom-nodes/TreeWalker.res @@ -1,9 +1,8 @@ /** The nodes of a document subtree and a position within them. [See TreeWalker on MDN](https://developer.mozilla.org/docs/Web/API/TreeWalker) -TODO: mark as private once mutating fields of private records is allowed */ -type t = { +type t = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/TreeWalker/root) */ @@ -22,6 +21,9 @@ type t = { mutable currentNode: DOMTree.node, } +@set +external setCurrentNode: (t, DOMTree.node) => unit = "currentNode" + /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/TreeWalker/parentNode) */ diff --git a/src/dom-platform/DOMException.res b/src/dom-platform/DOMException.res index 216b546d..023b872d 100644 --- a/src/dom-platform/DOMException.res +++ b/src/dom-platform/DOMException.res @@ -1,4 +1,4 @@ -type t = DOM.domException +type t = DOM.domException = private {...DOM.domException} /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/DOMException) diff --git a/src/dom-platform/DOMStringList.res b/src/dom-platform/DOMStringList.res index 42fdaa5e..bcae05a6 100644 --- a/src/dom-platform/DOMStringList.res +++ b/src/dom-platform/DOMStringList.res @@ -1,4 +1,4 @@ -type t = DOM.domStringList +type t = DOM.domStringList = private {...DOM.domStringList} /** Returns the string with index index from strings. diff --git a/src/dom-platform/DOMTokenList.res b/src/dom-platform/DOMTokenList.res index 931e826e..f73d978c 100644 --- a/src/dom-platform/DOMTokenList.res +++ b/src/dom-platform/DOMTokenList.res @@ -1,4 +1,7 @@ -type t = DOM.domTokenList = {...DOM.domTokenList} +type t = DOM.domTokenList = private {...DOM.domTokenList} + +@set +external setValue: (t, string) => unit = "value" /** Returns the token with index index. diff --git a/src/fetch/Request.res b/src/fetch/Request.res index f51ad3fd..40f319d0 100644 --- a/src/fetch/Request.res +++ b/src/fetch/Request.res @@ -1,6 +1,11 @@ /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Request) */ +/** +This private view prevents record construction through this module's public type. +ReScript's explicit structural coercion (`:>`) can still escape this restriction; +this is not an opaque brand guarantee. +*/ type t = FetchTypes.request = private {...FetchTypes.request} type requestInit = FetchTypes.requestInit type bodyInit = BodyInit.t diff --git a/src/fetch/Response.res b/src/fetch/Response.res index aa76cfcb..c838e1ac 100644 --- a/src/fetch/Response.res +++ b/src/fetch/Response.res @@ -1,6 +1,11 @@ /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/Response) */ +/** +This private view prevents record construction through this module's public type. +ReScript's explicit structural coercion (`:>`) can still escape this restriction; +this is not an opaque brand guarantee. +*/ type t = FetchTypes.response = private {...FetchTypes.response} type responseInit = FetchTypes.responseInit type bodyInit = BodyInit.t diff --git a/src/file/BaseFileAndDirectoryEntries.res b/src/file/BaseFileAndDirectoryEntries.res index 74dee7be..bacad9ac 100644 --- a/src/file/BaseFileAndDirectoryEntries.res +++ b/src/file/BaseFileAndDirectoryEntries.res @@ -58,7 +58,7 @@ type rec fileSystemEntry = private { /** [See FileSystem on MDN](https://developer.mozilla.org/docs/Web/API/FileSystem) */ -and fileSystem = { +and fileSystem = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/FileSystem/name) */ diff --git a/src/file/FileAndDirectoryEntriesTypes.res b/src/file/FileAndDirectoryEntriesTypes.res index 76e3fc84..4a89eb38 100644 --- a/src/file/FileAndDirectoryEntriesTypes.res +++ b/src/file/FileAndDirectoryEntriesTypes.res @@ -15,7 +15,9 @@ type fileSystemDirectoryEntry = BaseFileAndDirectoryEntries.fileSystemDirectoryE /** [See FileSystem on MDN](https://developer.mozilla.org/docs/Web/API/FileSystem) */ -type fileSystem = BaseFileAndDirectoryEntries.fileSystem +type fileSystem = BaseFileAndDirectoryEntries.fileSystem = private { + ...BaseFileAndDirectoryEntries.fileSystem, +} /** [See FileSystemDirectoryReader on MDN](https://developer.mozilla.org/docs/Web/API/FileSystemDirectoryReader) diff --git a/src/file/FileTypes.res b/src/file/FileTypes.res index d50b7cd1..48165b4e 100644 --- a/src/file/FileTypes.res +++ b/src/file/FileTypes.res @@ -37,7 +37,7 @@ This Streams API interface represents a readable stream of byte data. The WebApi [See ReadableStream on MDN](https://developer.mozilla.org/docs/Web/API/ReadableStream) */ @editor.completeFrom(ReadableStream) -type readableStream<'r> = { +type readableStream<'r> = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/ReadableStream/locked) */ @@ -49,7 +49,7 @@ This Streams API interface provides a standard abstraction for writing streamin [See WritableStream on MDN](https://developer.mozilla.org/docs/Web/API/WritableStream) */ @editor.completeFrom(WritableStream) -type writableStream<'w> = { +type writableStream<'w> = private { /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/WritableStream/locked) */ diff --git a/src/file/ReadableStream.res b/src/file/ReadableStream.res index 65011c93..694a5fd2 100644 --- a/src/file/ReadableStream.res +++ b/src/file/ReadableStream.res @@ -1,7 +1,12 @@ /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/ReadableStream) */ -type t<'r> = FileTypes.readableStream<'r> +/** +This private view prevents record construction through this module's public type. +ReScript's explicit structural coercion (`:>`) can still escape this restriction; +this is not an opaque brand guarantee. +*/ +type t<'r> = FileTypes.readableStream<'r> = private {...FileTypes.readableStream<'r>} /** `make()` diff --git a/src/file/WritableStream.res b/src/file/WritableStream.res index d2cabeb4..87847c82 100644 --- a/src/file/WritableStream.res +++ b/src/file/WritableStream.res @@ -1,5 +1,10 @@ +/** +This private view prevents record construction through this module's public type. +ReScript's explicit structural coercion (`:>`) can still escape this restriction; +this is not an opaque brand guarantee. +*/ @editor.completeFrom(WritableStream) -type t<'w> = FileTypes.writableStream<'w> = {...FileTypes.writableStream<'w>} +type t<'w> = FileTypes.writableStream<'w> = private {...FileTypes.writableStream<'w>} /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/WritableStream) diff --git a/src/html/HTMLAnchorElement.res b/src/html/HTMLAnchorElement.res index c5a79b50..0227f4d5 100644 --- a/src/html/HTMLAnchorElement.res +++ b/src/html/HTMLAnchorElement.res @@ -1,9 +1,8 @@ /** Hyperlink elements and provides special properties and methods (beyond those of the regular HTMLElement object interface that they inherit from) for manipulating the layout and presentation of such elements. [See HTMLAnchorElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLAnchorElement) -TODO: mark as private once mutating fields of private records is allowed */ -type t = { +type t = private { // Base properties from HTMLElement /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/HTMLElement/title) @@ -371,3 +370,22 @@ Can be set, to change the WebApiURL's fragment (ignores leading "#"). } include HTMLElement.Impl({type t = t}) + +@set external setTarget: (t, string) => unit = "target" +@set external setDownload: (t, string) => unit = "download" +@set external setPing: (t, string) => unit = "ping" +@set external setRel: (t, string) => unit = "rel" +@set external setHreflang: (t, string) => unit = "hreflang" +@set external setType: (t, string) => unit = "type" +@set external setText: (t, string) => unit = "text" +@set external setReferrerPolicy: (t, string) => unit = "referrerPolicy" +@set external setHref: (t, string) => unit = "href" +@set external setProtocol: (t, string) => unit = "protocol" +@set external setUsername: (t, string) => unit = "username" +@set external setPassword: (t, string) => unit = "password" +@set external setHost: (t, string) => unit = "host" +@set external setHostname: (t, string) => unit = "hostname" +@set external setPort: (t, string) => unit = "port" +@set external setPathname: (t, string) => unit = "pathname" +@set external setSearch: (t, string) => unit = "search" +@set external setHash: (t, string) => unit = "hash" diff --git a/src/html/HTMLAreaElement.res b/src/html/HTMLAreaElement.res index 7f312888..e2a79a46 100644 --- a/src/html/HTMLAreaElement.res +++ b/src/html/HTMLAreaElement.res @@ -1,9 +1,8 @@ /** Provides special properties and methods (beyond those of the regular object HTMLElement interface it also has available to it by inheritance) for manipulating the layout and presentation of elements. [See HTMLAreaElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLAreaElement) -TODO: mark as private once mutating fields of private records is allowed */ -type t = { +type t = private { // Base properties from HTMLElement /** [Read more on MDN](https://developer.mozilla.org/docs/Web/API/HTMLElement/title) @@ -351,3 +350,18 @@ Can be set, to change the WebApiURL's fragment (ignores leading "#"). } include HTMLElement.Impl({type t = t}) + +@set external setTarget: (t, string) => unit = "target" +@set external setPing: (t, string) => unit = "ping" +@set external setRel: (t, string) => unit = "rel" +@set external setReferrerPolicy: (t, string) => unit = "referrerPolicy" +@set external setHref: (t, string) => unit = "href" +@set external setProtocol: (t, string) => unit = "protocol" +@set external setUsername: (t, string) => unit = "username" +@set external setPassword: (t, string) => unit = "password" +@set external setHost: (t, string) => unit = "host" +@set external setHostname: (t, string) => unit = "hostname" +@set external setPort: (t, string) => unit = "port" +@set external setPathname: (t, string) => unit = "pathname" +@set external setSearch: (t, string) => unit = "search" +@set external setHash: (t, string) => unit = "hash" diff --git a/src/html/HTMLBaseElement.res b/src/html/HTMLBaseElement.res index be86410d..1828b4ca 100644 --- a/src/html/HTMLBaseElement.res +++ b/src/html/HTMLBaseElement.res @@ -1,9 +1,8 @@ /** Contains the base URI for a document. This object inherits all of the properties and methods as described in the HTMLElement interface. [See HTMLBaseElement on MDN](https://developer.mozilla.org/docs/Web/API/HTMLBaseElement) -TODO: mark as private once mutating fields of private records is allowed */ -type t = { +type t = private { ...DOMTree.htmlElement, /** Gets or sets the baseline WebApiURL on which relative links are based. @@ -18,3 +17,6 @@ type t = { } include HTMLElement.Impl({type t = t}) + +@set external setHref: (t, string) => unit = "href" +@set external setTarget: (t, string) => unit = "target" diff --git a/src/html/HTMLButtonElement.res b/src/html/HTMLButtonElement.res index 2195b74a..36d4f5db 100644 --- a/src/html/HTMLButtonElement.res +++ b/src/html/HTMLButtonElement.res @@ -1,9 +1,8 @@ /** Provides properties and methods (beyond the regular HTMLElement interface it also has available to it by inheritance) for manipulating