From 9846b90a1061a7e22f3f02c2d93773ade2ef71eb Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Tue, 20 May 2025 17:30:07 +0200 Subject: [PATCH 01/21] Demonstrate JSON Schema as source of truth --- .prettierignore | 3 +- index.ts | 2 +- new.quicktype.ts | 209 +++++++++ newtypes.ts | 63 +++ package-lock.json | 956 ++++++++++++++++++++++++++++++++++++++- package.json | 3 + schemas/new.schema.json | 302 +++++++++++++ scripts/update-drafts.ts | 2 +- scripts/validate.ts | 2 +- 9 files changed, 1527 insertions(+), 15 deletions(-) create mode 100644 new.quicktype.ts create mode 100644 newtypes.ts create mode 100644 schemas/new.schema.json diff --git a/.prettierignore b/.prettierignore index 743a54d17e2..b7f9c64c149 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,7 +2,6 @@ /**/*.dist /**/dist - # Exclude the website includes since these are typically fragments, or include # things that makes Prettier unhappy. /gh-pages/src/_includes/** @@ -18,7 +17,7 @@ towards-features.md /docs !/docs/publishing.md /features/draft -/schemas +/schemas/data.schema.json /scripts/caniuse.ts /scripts/schema.ts /scripts/specs.ts diff --git a/index.ts b/index.ts index d7df26716bf..e28d397aafa 100644 --- a/index.ts +++ b/index.ts @@ -4,7 +4,7 @@ import path from 'path'; import { Temporal } from '@js-temporal/polyfill'; import { fdir } from 'fdir'; import YAML from 'yaml'; -import { FeatureData, GroupData, SnapshotData, WebFeaturesData } from './types'; +import { FeatureData, GroupData, SnapshotData, WebFeaturesData } from './newtypes'; import { toString as hastTreeToString } from 'hast-util-to-string'; import rehypeStringify from 'rehype-stringify'; diff --git a/new.quicktype.ts b/new.quicktype.ts new file mode 100644 index 00000000000..8a0828f425f --- /dev/null +++ b/new.quicktype.ts @@ -0,0 +1,209 @@ +/** + * The top-level web-features data package + */ +export interface WebFeaturesData { + /** + * Browsers and browser release data + */ + browsers: Browsers; + /** + * Feature identifiers and data + */ + features: { [key: string]: FeatureData }; + /** + * Group identifiers and data + */ + groups: { [key: string]: GroupData }; + /** + * Snapshot identifiers and data + */ + snapshots?: { [key: string]: SnapshotData }; +} + +/** + * Browsers and browser release data + */ +export interface Browsers { + chrome: BrowserData; + chrome_android: BrowserData; + edge: BrowserData; + firefox: BrowserData; + firefox_android: BrowserData; + safari: BrowserData; + safari_ios: BrowserData; +} + +/** + * Browser information + */ +export interface BrowserData { + /** + * The name of the browser, as in "Edge" or "Safari on iOS" + */ + name: string; + releases: Release[]; +} + +/** + * Browser release information + */ +export interface Release { + /** + * The release date, as in "2023-12-11" + */ + date: string; + /** + * The version string, as in "10" or "17.1" + */ + version: string; +} + +/** + * A feature data entry + */ +export interface FeatureData { + /** + * caniuse.com identifier(s) + */ + caniuse?: string[] | string; + /** + * Sources of support data for this feature + */ + compat_features?: string[]; + /** + * Short description of the feature, as a plain text string + */ + description: string; + /** + * Short description of the feature, as an HTML string + */ + description_html: string; + /** + * Whether developers are formally discouraged from using this feature + */ + discouraged?: Discouraged; + /** + * Group identifier(s) + */ + group?: string[] | string; + /** + * Short name + */ + name: string; + /** + * Snapshot identifier(s) + */ + snapshot?: string[] | string; + /** + * Specification URL(s) + */ + spec: string[] | string; + /** + * Whether a feature is considered a "Baseline" web platform feature and when it achieved + * that status + */ + status?: StatusHeadline; + [property: string]: any; +} + +/** + * Whether developers are formally discouraged from using this feature + */ +export interface Discouraged { + /** + * Links to a formal discouragement notice, such as specification text, intent-to-unship, + * etc. + */ + according_to: string[]; + /** + * IDs for features that substitute some or all of this feature's utility + */ + alternatives?: string[]; +} + +/** + * Whether a feature is considered a "Baseline" web platform feature and when it achieved + * that status + */ +export interface StatusHeadline { + /** + * Whether the feature is Baseline (low substatus), Baseline (high substatus), or not (false) + */ + baseline: boolean | BaselineEnum; + /** + * Date the feature achieved Baseline high status + */ + baseline_high_date?: string; + /** + * Date the feature achieved Baseline low status + */ + baseline_low_date?: string; + /** + * Browser versions that most-recently introduced the feature + */ + support: Support; + /** + * Statuses for each key in the feature's compat_features list, if applicable. Not available + * to the npm release of web-features. + */ + by_compat_key?: { [key: string]: Status }; + [property: string]: any; +} + +export type BaselineEnum = "high" | "low"; + +export interface Status { + /** + * Whether the feature is Baseline (low substatus), Baseline (high substatus), or not (false) + */ + baseline: boolean | BaselineEnum; + /** + * Date the feature achieved Baseline high status + */ + baseline_high_date?: string; + /** + * Date the feature achieved Baseline low status + */ + baseline_low_date?: string; + /** + * Browser versions that most-recently introduced the feature + */ + support: Support; + [property: string]: any; +} + +/** + * Browser versions that most-recently introduced the feature + */ +export interface Support { + chrome?: string; + chrome_android?: string; + edge?: string; + firefox?: string; + firefox_android?: string; + safari?: string; + safari_ios?: string; +} + +export interface GroupData { + /** + * Short name + */ + name: string; + /** + * Identifier of parent group + */ + parent?: string; +} + +export interface SnapshotData { + /** + * Short name + */ + name?: string; + /** + * Specification + */ + spec?: string; + [property: string]: any; +} diff --git a/newtypes.ts b/newtypes.ts new file mode 100644 index 00000000000..c7cba19a56f --- /dev/null +++ b/newtypes.ts @@ -0,0 +1,63 @@ +// Quicktype produces a definitions that are correct, but not as narrow or +// well-named as hand-written type definition might produce. This module takes +// the Quicktype-generated types as renames or modifies the types to be somewhat +// nicer to work with in TypeScript. + +import type { + BaselineEnum as BaselineHighLow, + BrowserData, + Browsers, + GroupData, + FeatureData as QuicktypeMonolithicFeatureData, + WebFeaturesData as QuicktypeWebFeaturesData, + Release, + SnapshotData, + Status, + StatusHeadline, + Support, +} from "./new.quicktype"; + +// Passthrough types +export type { + BaselineHighLow, + BrowserData, + Browsers, + GroupData, + Release, + SnapshotData, + Status, + StatusHeadline, + Support, +}; + +export interface WebFeaturesData + extends Pick { + features: { [key: string]: FeatureData }; +} + +export type FeatureData = Required< + Pick< + QuicktypeMonolithicFeatureData, + "description_html" | "description" | "name" | "spec" | "status" + > +> & + Partial< + Pick< + QuicktypeMonolithicFeatureData, + "caniuse" | "compat_features" | "discouraged" + > + >; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const t1: FeatureData = { + name: "Test", + description: "Hi", + description_html: "Hi", + spec: "", + status: { + baseline: false, + support: {}, + }, +}; + +export type BrowserIdentifier = keyof Browsers; diff --git a/package-lock.json b/package-lock.json index 52bd7148ac8..9207d9e6869 100644 --- a/package-lock.json +++ b/package-lock.json @@ -24,6 +24,7 @@ "fdir": "^6.4.4", "hast-util-to-string": "^3.0.1", "prettier": "^3.5.3", + "quicktype": "^23.2.5", "rehype-stringify": "^10.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", @@ -60,6 +61,30 @@ "node": ">=0.1.90" } }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@dabh/diagnostics": { "version": "2.0.3", "dev": true, @@ -608,6 +633,13 @@ "url": "https://eslint.org/donate" } }, + "node_modules/@glideapps/ts-necessities": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@glideapps/ts-necessities/-/ts-necessities-2.4.0.tgz", + "integrity": "sha512-mDC+qosuNa4lxR3ioMBb6CD0XLRsQBplU+zRPUYiMLXKeVPZ6UYphdNG/EGReig0YyfnVlBKZEXl1wzTotYmPA==", + "dev": true, + "license": "MIT" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -812,6 +844,93 @@ "node": ">=12" } }, + "node_modules/@mark.probst/typescript-json-schema": { + "version": "0.55.0", + "resolved": "https://registry.npmjs.org/@mark.probst/typescript-json-schema/-/typescript-json-schema-0.55.0.tgz", + "integrity": "sha512-jI48mSnRgFQxXiE/UTUCVCpX8lK3wCFKLF1Ss2aEreboKNuLQGt3e0/YFqWVHe/WENxOaqiJvwOz+L/SrN2+qQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@types/json-schema": "^7.0.9", + "@types/node": "^16.9.2", + "glob": "^7.1.7", + "path-equal": "^1.1.2", + "safe-stable-stringify": "^2.2.0", + "ts-node": "^10.9.1", + "typescript": "4.9.4", + "yargs": "^17.1.1" + }, + "bin": { + "typescript-json-schema": "bin/typescript-json-schema" + } + }, + "node_modules/@mark.probst/typescript-json-schema/node_modules/@types/node": { + "version": "16.18.126", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.126.tgz", + "integrity": "sha512-OTcgaiwfGFBKacvfwuHzzn1KLxH/er8mluiy8/uM3sGXHaRe73RrSIj01jow9t4kJEW633Ov+cOexXeiApTyAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@mark.probst/typescript-json-schema/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@mark.probst/typescript-json-schema/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@mark.probst/typescript-json-schema/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@mark.probst/typescript-json-schema/node_modules/typescript": { + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/@mdn/browser-compat-data": { "version": "6.0.15", "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-6.0.15.tgz", @@ -863,6 +982,34 @@ "node": ">=14" } }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/caniuse-lite": { "version": "1.0.5", "dev": true, @@ -1184,12 +1331,24 @@ "dev": true, "license": "ISC" }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/acorn": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", "dev": true, - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1207,6 +1366,19 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", @@ -1261,11 +1433,28 @@ "dev": true, "license": "MIT" }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, "node_modules/argparse": { "version": "2.0.1", "dev": true, "license": "Python-2.0" }, + "node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/assertion-error": { "version": "2.0.1", "dev": true, @@ -1293,6 +1482,27 @@ "dev": true, "license": "MIT" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "2.0.1", "dev": true, @@ -1314,11 +1524,43 @@ "node": ">=8" } }, + "node_modules/browser-or-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-3.0.0.tgz", + "integrity": "sha512-iczIdVJzGEYhP5DqQxYM9Hh7Ztpqqi+CXZpSmX8ALFs9ecXkQIeqRyM6TfxEfMVpwhl3dSuDvxdzzo9sUOIVBQ==", + "dev": true, + "license": "MIT" + }, "node_modules/browser-stdout": { "version": "1.3.1", "dev": true, "license": "ISC" }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/c8": { "version": "10.1.3", "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.3.tgz", @@ -1446,6 +1688,62 @@ "node": ">=4" } }, + "node_modules/chalk-template": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-0.4.0.tgz", + "integrity": "sha512-/ghrgmhfY8RaSdeo43hNXxpoHAtxdbskUHjPpfqUWGttFgycUhYPGx3YZBCnUCvOa7Doivn1IZec3DEGFoMgLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" + } + }, + "node_modules/chalk-template/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk-template/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk-template/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/chalk/node_modules/ansi-styles": { "version": "3.2.1", "dev": true, @@ -1522,6 +1820,13 @@ "node": ">=12" } }, + "node_modules/collection-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collection-utils/-/collection-utils-1.0.1.tgz", + "integrity": "sha512-LA2YTIlR7biSpXkKYwwuzGjwL5rjWEZVOSnvdUc7gObvWe4WkjxOpfrdhoP7Hs09YWDVfg0Mal9BpAqLfVEzQg==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/color": { "version": "3.2.1", "dev": true, @@ -1571,6 +1876,58 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/command-line-args": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^3.1.0", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-usage": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-7.0.3.tgz", + "integrity": "sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "chalk-template": "^0.4.0", + "table-layout": "^4.1.0", + "typical": "^7.1.1" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/command-line-usage/node_modules/array-back": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", + "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, + "node_modules/command-line-usage/node_modules/typical": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", + "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, "node_modules/commander": { "version": "13.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz", @@ -1594,8 +1951,7 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "peer": true + "dev": true }, "node_modules/convert-source-map": { "version": "2.0.0", @@ -1603,6 +1959,23 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-fetch": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", + "integrity": "sha512-uKm5PU+MHTootlWEY+mZ4vvXoCn4fLQxT9dSc1sXVMSFkINTJVN8cAQROpwcKm8bJ/c7rgZVIBWzH5T78sNZZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-fetch": "^2.7.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2086,6 +2459,26 @@ "node": ">=0.10.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/extend": { "version": "3.0.2", "dev": true, @@ -2187,6 +2580,19 @@ "node": ">=8" } }, + "node_modules/find-replace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^3.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/find-up": { "version": "5.0.0", "dev": true, @@ -2256,8 +2662,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/fsevents": { "version": "2.3.3", @@ -2369,6 +2774,17 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/graphql": { + "version": "0.11.7", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.11.7.tgz", + "integrity": "sha512-x7uDjyz8Jx+QPbpCFCMQ8lltnQa4p4vSYHx6ADe8rVYRTdsyhCJbvSty5DAsLVmU6cGakl+r8HQYolKHxk/tiw==", + "deprecated": "No longer supported; please update to a newer version. Details: https://github.com/graphql/graphql-js#version-support", + "dev": true, + "license": "MIT", + "dependencies": { + "iterall": "1.1.3" + } + }, "node_modules/has-flag": { "version": "3.0.0", "dev": true, @@ -2537,6 +2953,27 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -2578,7 +3015,6 @@ "version": "1.0.6", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -2671,6 +3107,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "dev": true, + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -2734,6 +3177,13 @@ "node": ">=8" } }, + "node_modules/iterall": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.1.3.tgz", + "integrity": "sha512-Cu/kb+4HiNSejAPhSaN1VukdNTTi/r4/e+yykqjlG/IW+1gZH5b4+Bq3whDX4tvbYugta3r8KTMUiqT3fIGxuQ==", + "dev": true, + "license": "MIT" + }, "node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", @@ -2797,6 +3247,13 @@ "pretty-format": "^21.2.1" } }, + "node_modules/js-base64": { + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz", + "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/js-yaml": { "version": "4.1.0", "dev": true, @@ -2885,6 +3342,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -2995,6 +3466,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, "node_modules/mdast-util-from-markdown": { "version": "2.0.0", "dev": true, @@ -3626,6 +4104,16 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.3", "dev": true, @@ -3636,6 +4124,27 @@ "dev": true, "license": "MIT" }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/normalize-path": { "version": "3.0.0", "dev": true, @@ -3648,7 +4157,6 @@ "version": "1.4.0", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "wrappy": "1" } @@ -3713,6 +4221,13 @@ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3737,6 +4252,13 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/path-equal": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/path-equal/-/path-equal-1.2.5.tgz", + "integrity": "sha512-i73IctDr3F2W+bsOWDyyVm/lqsXO47aY9nsFZUjTT/aljSbkxHxxCoyZ9UUrM8jK0JVod+An+rl48RCsvWM+9g==", + "dev": true, + "license": "MIT" + }, "node_modules/path-exists": { "version": "4.0.0", "dev": true, @@ -3750,7 +4272,6 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "peer": true, "engines": { "node": ">=0.10.0" } @@ -3808,6 +4329,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -3862,6 +4393,16 @@ "node": ">=4" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/property-information": { "version": "6.5.0", "dev": true, @@ -3901,6 +4442,185 @@ } ] }, + "node_modules/quicktype": { + "version": "23.2.5", + "resolved": "https://registry.npmjs.org/quicktype/-/quicktype-23.2.5.tgz", + "integrity": "sha512-G6bvD5sGD1Fr/9HHH4juuwZ0l5JUIQDySuzR89pI1Rcw0IN2K5MVsKfiNQgQhfJP0gbE3wspFKWiYKSkCWUaJQ==", + "dev": true, + "license": "Apache-2.0", + "workspaces": [ + "./packages/quicktype-core", + "./packages/quicktype-graphql-input", + "./packages/quicktype-typescript-input", + "./packages/quicktype-vscode" + ], + "dependencies": { + "@glideapps/ts-necessities": "^2.2.3", + "chalk": "^4.1.2", + "collection-utils": "^1.0.1", + "command-line-args": "^5.2.1", + "command-line-usage": "^7.0.1", + "cross-fetch": "^4.0.0", + "graphql": "^0.11.7", + "lodash": "^4.17.21", + "moment": "^2.30.1", + "quicktype-core": "23.2.5", + "quicktype-graphql-input": "23.2.5", + "quicktype-typescript-input": "23.2.5", + "readable-stream": "^4.5.2", + "stream-json": "1.8.0", + "string-to-stream": "^3.0.1", + "typescript": "~5.8.3" + }, + "bin": { + "quicktype": "dist/index.js" + }, + "engines": { + "node": ">=18.12.0" + } + }, + "node_modules/quicktype-core": { + "version": "23.2.5", + "resolved": "https://registry.npmjs.org/quicktype-core/-/quicktype-core-23.2.5.tgz", + "integrity": "sha512-vbeDPIrhK3TIwU592+c5l5EAknqLhquWCEFzDeuC3cPuuK8dblMcfsnfnT5Cj7FeopDMI93Bq3ql0cMG90yFkg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@glideapps/ts-necessities": "2.2.3", + "browser-or-node": "^3.0.0", + "collection-utils": "^1.0.1", + "cross-fetch": "^4.0.0", + "is-url": "^1.2.4", + "js-base64": "^3.7.7", + "lodash": "^4.17.21", + "pako": "^1.0.6", + "pluralize": "^8.0.0", + "readable-stream": "4.5.2", + "unicode-properties": "^1.4.1", + "urijs": "^1.19.1", + "wordwrap": "^1.0.0", + "yaml": "^2.4.1" + } + }, + "node_modules/quicktype-core/node_modules/@glideapps/ts-necessities": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/@glideapps/ts-necessities/-/ts-necessities-2.2.3.tgz", + "integrity": "sha512-gXi0awOZLHk3TbW55GZLCPP6O+y/b5X1pBXKBVckFONSwF1z1E5ND2BGJsghQFah+pW7pkkyFb2VhUQI2qhL5w==", + "dev": true, + "license": "MIT" + }, + "node_modules/quicktype-core/node_modules/readable-stream": { + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", + "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/quicktype-graphql-input": { + "version": "23.2.5", + "resolved": "https://registry.npmjs.org/quicktype-graphql-input/-/quicktype-graphql-input-23.2.5.tgz", + "integrity": "sha512-PNLxMqd6cjFs/EuXvVcgQnJPyon0Yp22CqujR8I7X4CRrDgoivG8NIBWOhZ1mdzTZJbe7Cas0h9RVltEdpyKIQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "collection-utils": "^1.0.1", + "graphql": "^0.11.7", + "quicktype-core": "23.2.5" + } + }, + "node_modules/quicktype-typescript-input": { + "version": "23.2.5", + "resolved": "https://registry.npmjs.org/quicktype-typescript-input/-/quicktype-typescript-input-23.2.5.tgz", + "integrity": "sha512-bLivZDfBfKKS5Iqcqb6pMyncKH9xerY2f9uZ+p6YoN7M44iDXo8W0yG55L1mAg6d40tWrMkgvtaBmsHL3i2n/Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@mark.probst/typescript-json-schema": "0.55.0", + "quicktype-core": "23.2.5", + "typescript": "4.9.5" + } + }, + "node_modules/quicktype-typescript-input/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/quicktype/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/quicktype/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/quicktype/node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/quicktype/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -4225,6 +4945,23 @@ "node": "*" } }, + "node_modules/stream-chain": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz", + "integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stream-json": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.8.0.tgz", + "integrity": "sha512-HZfXngYHUAr1exT4fxlbc1IOce1RYxp2ldeaf97LYCOPSoOqY/1Psp7iGvpb+6JIOgkra9zDYnPX01hGAHzEPw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "stream-chain": "^2.2.5" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -4234,6 +4971,16 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-to-stream": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/string-to-stream/-/string-to-stream-3.0.1.tgz", + "integrity": "sha512-Hl092MV3USJuUCC6mfl9sPzGloA3K5VwdIeJjYIkXY/8K+mUvaeEabWJgArp+xXrsWxCajeT2pc4axbVhIZJyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^3.4.0" + } + }, "node_modules/string-width": { "version": "4.2.3", "dev": true, @@ -4321,6 +5068,30 @@ "node": ">=4" } }, + "node_modules/table-layout": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-4.1.1.tgz", + "integrity": "sha512-iK5/YhZxq5GO5z8wb0bY1317uDF3Zjpha0QFFLA8/trAoiLbQD0HUbMesEaxyzUgDxi2QlcbM8IvqOlEjgoXBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-back": "^6.2.2", + "wordwrapjs": "^5.1.0" + }, + "engines": { + "node": ">=12.17" + } + }, + "node_modules/table-layout/node_modules/array-back": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", + "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, "node_modules/test-exclude": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", @@ -4362,6 +5133,13 @@ "dev": true, "peer": true }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "dev": true, + "license": "MIT" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4375,6 +5153,13 @@ "node": ">=8.0" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, "node_modules/trim-lines": { "version": "3.0.1", "dev": true, @@ -4521,6 +5306,60 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -4611,6 +5450,16 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/typical": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/undici-types": { "version": "6.19.8", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", @@ -4618,6 +5467,35 @@ "dev": true, "license": "MIT" }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, + "node_modules/unicode-trie/node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "dev": true, + "license": "MIT" + }, "node_modules/unified": { "version": "11.0.5", "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", @@ -4721,12 +5599,26 @@ "punycode": "^2.1.0" } }, + "node_modules/urijs": { + "version": "1.19.11", + "resolved": "https://registry.npmjs.org/urijs/-/urijs-1.19.11.tgz", + "integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==", + "dev": true, + "license": "MIT" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, "node_modules/v8-to-istanbul": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", @@ -4797,6 +5689,24 @@ "dev": true, "license": "CC0-1.0" }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4858,6 +5768,23 @@ "node": ">=0.10.0" } }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wordwrapjs": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-5.1.0.tgz", + "integrity": "sha512-JNjcULU2e4KJwUNv6CHgI46UvDGitb6dGryHajXTDiLgg1/RiGoPSDw4kZfYnwGtEXf2ZMeIewDQgFGzkCB2Sg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.17" + } + }, "node_modules/workerpool": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", @@ -4901,8 +5828,7 @@ "node_modules/wrappy": { "version": "1.0.2", "dev": true, - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/y18n": { "version": "5.0.8", @@ -4964,6 +5890,16 @@ "node": ">=10" } }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "dev": true, diff --git a/package.json b/package.json index 5b42e55c06a..a62ae12b8a1 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,8 @@ "format": "npx prettier --write .", "schema:write": "npm run schema -- --out ./schemas/data.schema.json", "schema": "ts-json-schema-generator --tsconfig ./tsconfig.json --path ./types.ts --type=WebFeaturesData", + "schematypes": "sh -c \"npx quicktype --lang='typescript' --src-lang schema --src ./schemas/new.schema.json --just-types='yes' --prefer-unions='yes' --prefer-const-values='yes' --top-level=WebFeaturesData | prettier --stdin-filepath ./new.quicktype.ts\"", + "schematypes:write": "npm run --silent schematypes > ./new.quicktype.ts", "test:caniuse": "tsx scripts/caniuse.ts", "test:coverage": "npm run --workspaces test:coverage", "test:dist": "tsx scripts/dist.ts --check", @@ -50,6 +52,7 @@ "fdir": "^6.4.4", "hast-util-to-string": "^3.0.1", "prettier": "^3.5.3", + "quicktype": "^23.2.5", "rehype-stringify": "^10.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", diff --git a/schemas/new.schema.json b/schemas/new.schema.json new file mode 100644 index 00000000000..e2305ad2d62 --- /dev/null +++ b/schemas/new.schema.json @@ -0,0 +1,302 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "description": "The top-level web-features data package", + "type": "object", + "properties": { + "browsers": { + "description": "Browsers and browser release data", + "type": "object", + "properties": { + "chrome": { + "$ref": "#/definitions/BrowserData" + }, + "chrome_android": { + "$ref": "#/definitions/BrowserData" + }, + "edge": { + "$ref": "#/definitions/BrowserData" + }, + "firefox": { + "$ref": "#/definitions/BrowserData" + }, + "firefox_android": { + "$ref": "#/definitions/BrowserData" + }, + "safari": { + "$ref": "#/definitions/BrowserData" + }, + "safari_ios": { + "$ref": "#/definitions/BrowserData" + } + }, + "required": [ + "chrome", + "chrome_android", + "edge", + "firefox", + "firefox_android", + "safari", + "safari_ios" + ], + "additionalProperties": false + }, + "features": { + "description": "Feature identifiers and data", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/FeatureData" + } + }, + "groups": { + "description": "Group identifiers and data", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/GroupData" + } + }, + "snapshots": { + "description": "Snapshot identifiers and data", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/SnapshotData" + } + } + }, + "required": ["browsers", "features", "groups"], + "additionalProperties": false, + "definitions": { + "Discouraged": { + "type": "object", + "properties": { + "according_to": { + "description": "Links to a formal discouragement notice, such as specification text, intent-to-unship, etc.", + "$ref": "#/definitions/URLs" + }, + "alternatives": { + "description": "IDs for features that substitute some or all of this feature's utility", + "$ref": "#/definitions/Strings" + } + }, + "required": ["according_to"], + "additionalProperties": false + }, + "BrowserData": { + "description": "Browser information", + "type": "object", + "properties": { + "name": { + "description": "The name of the browser, as in \"Edge\" or \"Safari on iOS\"", + "type": "string" + }, + "releases": { + "type": "array", + "items": { + "$ref": "#/definitions/Release" + } + } + }, + "required": ["name", "releases"], + "additionalProperties": false + }, + "FeatureData": { + "description": "A feature data entry", + "type": "object", + "properties": { + "name": { + "description": "Short name", + "type": "string" + }, + "description": { + "description": "Short description of the feature, as a plain text string", + "type": "string" + }, + "description_html": { + "description": "Short description of the feature, as an HTML string", + "type": "string" + }, + "spec": { + "description": "Specification URL(s)", + "$ref": "#/definitions/URLOrURLs" + }, + "group": { + "description": "Group identifier(s)", + "$ref": "#/definitions/StringOrStrings" + }, + "snapshot": { + "description": "Snapshot identifier(s)", + "$ref": "#/definitions/StringOrStrings" + }, + "caniuse": { + "description": "caniuse.com identifier(s)", + "$ref": "#/definitions/StringOrStrings" + }, + "compat_features": { + "description": "Sources of support data for this feature", + "$ref": "#/definitions/Strings" + }, + "status": { + "description": "Whether a feature is considered a \"Baseline\" web platform feature and when it achieved that status", + "$ref": "#/definitions/StatusHeadline" + }, + "discouraged": { + "description": "Whether developers are formally discouraged from using this feature", + "$ref": "#/definitions/Discouraged" + } + }, + "required": ["name", "description", "description_html", "spec"] + }, + "GroupData": { + "type": "object", + "properties": { + "name": { + "description": "Short name", + "type": "string" + }, + "parent": { + "description": "Identifier of parent group", + "type": "string" + } + }, + "required": ["name"], + "additionalProperties": false + }, + "Status": { + "type": "object", + "properties": { + "baseline": { + "description": "Whether the feature is Baseline (low substatus), Baseline (high substatus), or not (false)", + "enum": ["high", "low", false] + }, + "baseline_high_date": { + "description": "Date the feature achieved Baseline high status", + "type": "string" + }, + "baseline_low_date": { + "description": "Date the feature achieved Baseline low status", + "type": "string" + }, + "support": { + "description": "Browser versions that most-recently introduced the feature", + "type": "object", + "properties": { + "chrome": { + "type": "string" + }, + "chrome_android": { + "type": "string" + }, + "edge": { + "type": "string" + }, + "firefox": { + "type": "string" + }, + "firefox_android": { + "type": "string" + }, + "safari": { + "type": "string" + }, + "safari_ios": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "required": ["baseline", "support"] + }, + "StatusHeadline": { + "type": "object", + "allOf": [ + { "$ref": "#/definitions/Status" }, + { + "type": "object", + "properties": { + "by_compat_key": { + "description": "Statuses for each key in the feature's compat_features list, if applicable. Not available to the npm release of web-features.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/Status" + } + } + } + } + ] + }, + "Release": { + "description": "Browser release information", + "type": "object", + "properties": { + "version": { + "description": "The version string, as in \"10\" or \"17.1\"", + "type": "string" + }, + "date": { + "description": " The release date, as in \"2023-12-11\"", + "type": "string" + } + }, + "required": ["version", "date"], + "additionalProperties": false + }, + "SnapshotData": { + "type": "object", + "properties": { + "name": { + "description": "Short name", + "type": "string" + }, + "spec": { + "description": "Specification", + "$ref": "#/definitions/URL" + } + } + }, + "StringOrStrings": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 2 + } + ] + }, + "Strings": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "URL": { + "type": "string" + }, + "URLs": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "URLOrURLs": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 2 + } + ] + } + } +} diff --git a/scripts/update-drafts.ts b/scripts/update-drafts.ts index f3fac0babdf..96b55c608c0 100644 --- a/scripts/update-drafts.ts +++ b/scripts/update-drafts.ts @@ -11,7 +11,7 @@ import { Document, parse } from "yaml"; import yargs from "yargs"; import { features } from "../index.js"; -import { FeatureData } from "../types.js"; +import { FeatureData } from "../newtypes.js"; type WebSpecsSpec = (typeof webSpecs)[number]; diff --git a/scripts/validate.ts b/scripts/validate.ts index 8c88eecfef1..2120c4e1913 100644 --- a/scripts/validate.ts +++ b/scripts/validate.ts @@ -1,7 +1,7 @@ import Ajv from "ajv"; import assert from "node:assert/strict"; -import * as schema from "../schemas/data.schema.json" with { type: "json" }; +import * as schema from "../schemas/new.schema.json" with { type: "json" }; const ajv = new Ajv({ allErrors: true, allowUnionTypes: true }); // TODO: turn on strictNullChecks, fix all the errors, and replace this with: From 1012068a312a5a990fc66de3ddc7c0bff3a7801c Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Mon, 26 May 2025 19:18:41 +0200 Subject: [PATCH 02/21] Replace types with newly generated types --- .prettierignore | 1 - index.ts | 2 +- newtypes.ts | 63 --- package.json | 10 +- schemas/data.schema.json | 537 +++++++++++-------------- schemas/new.schema.json | 302 -------------- scripts/schema.ts | 16 +- scripts/update-drafts.ts | 2 +- scripts/validate.ts | 2 +- new.quicktype.ts => types.quicktype.ts | 0 types.ts | 148 +++---- 11 files changed, 298 insertions(+), 785 deletions(-) delete mode 100644 newtypes.ts delete mode 100644 schemas/new.schema.json rename new.quicktype.ts => types.quicktype.ts (100%) diff --git a/.prettierignore b/.prettierignore index b7f9c64c149..7df15e39874 100644 --- a/.prettierignore +++ b/.prettierignore @@ -17,7 +17,6 @@ towards-features.md /docs !/docs/publishing.md /features/draft -/schemas/data.schema.json /scripts/caniuse.ts /scripts/schema.ts /scripts/specs.ts diff --git a/index.ts b/index.ts index e28d397aafa..d7df26716bf 100644 --- a/index.ts +++ b/index.ts @@ -4,7 +4,7 @@ import path from 'path'; import { Temporal } from '@js-temporal/polyfill'; import { fdir } from 'fdir'; import YAML from 'yaml'; -import { FeatureData, GroupData, SnapshotData, WebFeaturesData } from './newtypes'; +import { FeatureData, GroupData, SnapshotData, WebFeaturesData } from './types'; import { toString as hastTreeToString } from 'hast-util-to-string'; import rehypeStringify from 'rehype-stringify'; diff --git a/newtypes.ts b/newtypes.ts deleted file mode 100644 index c7cba19a56f..00000000000 --- a/newtypes.ts +++ /dev/null @@ -1,63 +0,0 @@ -// Quicktype produces a definitions that are correct, but not as narrow or -// well-named as hand-written type definition might produce. This module takes -// the Quicktype-generated types as renames or modifies the types to be somewhat -// nicer to work with in TypeScript. - -import type { - BaselineEnum as BaselineHighLow, - BrowserData, - Browsers, - GroupData, - FeatureData as QuicktypeMonolithicFeatureData, - WebFeaturesData as QuicktypeWebFeaturesData, - Release, - SnapshotData, - Status, - StatusHeadline, - Support, -} from "./new.quicktype"; - -// Passthrough types -export type { - BaselineHighLow, - BrowserData, - Browsers, - GroupData, - Release, - SnapshotData, - Status, - StatusHeadline, - Support, -}; - -export interface WebFeaturesData - extends Pick { - features: { [key: string]: FeatureData }; -} - -export type FeatureData = Required< - Pick< - QuicktypeMonolithicFeatureData, - "description_html" | "description" | "name" | "spec" | "status" - > -> & - Partial< - Pick< - QuicktypeMonolithicFeatureData, - "caniuse" | "compat_features" | "discouraged" - > - >; - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const t1: FeatureData = { - name: "Test", - description: "Hi", - description_html: "Hi", - spec: "", - status: { - baseline: false, - support: {}, - }, -}; - -export type BrowserIdentifier = keyof Browsers; diff --git a/package.json b/package.json index a62ae12b8a1..ff246a3a5b5 100644 --- a/package.json +++ b/package.json @@ -22,19 +22,17 @@ "undist": "tsx scripts/undist.ts", "feature-init": "tsx scripts/feature-init.ts", "format": "npx prettier --write .", - "schema:write": "npm run schema -- --out ./schemas/data.schema.json", - "schema": "ts-json-schema-generator --tsconfig ./tsconfig.json --path ./types.ts --type=WebFeaturesData", - "schematypes": "sh -c \"npx quicktype --lang='typescript' --src-lang schema --src ./schemas/new.schema.json --just-types='yes' --prefer-unions='yes' --prefer-const-values='yes' --top-level=WebFeaturesData | prettier --stdin-filepath ./new.quicktype.ts\"", - "schematypes:write": "npm run --silent schematypes > ./new.quicktype.ts", + "schematypes": "sh -c \"npx quicktype --lang='typescript' --src-lang schema --src ./schemas/data.schema.json --just-types='yes' --prefer-unions='yes' --prefer-const-values='yes' --top-level=WebFeaturesData | prettier --stdin-filepath ./types.quicktype.ts\"", + "schematypes:write": "npm run --silent schematypes > ./types.quicktype.ts", "test:caniuse": "tsx scripts/caniuse.ts", "test:coverage": "npm run --workspaces test:coverage", "test:dist": "tsx scripts/dist.ts --check", "test:format": "prettier --check .", "test:lint": "npx eslint .", - "test:schema": "tsx scripts/schema.ts", + "test:schematypes": "tsx scripts/schema.ts", "test:specs": "tsx scripts/specs.ts", "test:types": "npm run --workspaces test:types && tsc", - "test": "npm run test:caniuse -- --quiet && npm run test:schema && npm run test:specs && npm run test:types && npm run test:format && npm run test:dist && npm run test --workspaces && npm run test:lint", + "test": "npm run test:caniuse -- --quiet && npm run test:schematypes && npm run test:specs && npm run test:types && npm run test:format && npm run test:dist && npm run test --workspaces && npm run test:lint", "update-drafts": "tsx scripts/update-drafts.ts", "remove-tagged-compat-features": "tsx scripts/remove-tagged-compat-features.ts && npm run format" }, diff --git a/schemas/data.schema.json b/schemas/data.schema.json index 2d34661eed7..e2305ad2d62 100644 --- a/schemas/data.schema.json +++ b/schemas/data.schema.json @@ -1,53 +1,110 @@ { - "$ref": "#/definitions/WebFeaturesData", - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "http://json-schema.org/draft-07/schema", + "description": "The top-level web-features data package", + "type": "object", + "properties": { + "browsers": { + "description": "Browsers and browser release data", + "type": "object", + "properties": { + "chrome": { + "$ref": "#/definitions/BrowserData" + }, + "chrome_android": { + "$ref": "#/definitions/BrowserData" + }, + "edge": { + "$ref": "#/definitions/BrowserData" + }, + "firefox": { + "$ref": "#/definitions/BrowserData" + }, + "firefox_android": { + "$ref": "#/definitions/BrowserData" + }, + "safari": { + "$ref": "#/definitions/BrowserData" + }, + "safari_ios": { + "$ref": "#/definitions/BrowserData" + } + }, + "required": [ + "chrome", + "chrome_android", + "edge", + "firefox", + "firefox_android", + "safari", + "safari_ios" + ], + "additionalProperties": false + }, + "features": { + "description": "Feature identifiers and data", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/FeatureData" + } + }, + "groups": { + "description": "Group identifiers and data", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/GroupData" + } + }, + "snapshots": { + "description": "Snapshot identifiers and data", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/SnapshotData" + } + } + }, + "required": ["browsers", "features", "groups"], + "additionalProperties": false, "definitions": { + "Discouraged": { + "type": "object", + "properties": { + "according_to": { + "description": "Links to a formal discouragement notice, such as specification text, intent-to-unship, etc.", + "$ref": "#/definitions/URLs" + }, + "alternatives": { + "description": "IDs for features that substitute some or all of this feature's utility", + "$ref": "#/definitions/Strings" + } + }, + "required": ["according_to"], + "additionalProperties": false + }, "BrowserData": { - "additionalProperties": false, "description": "Browser information", + "type": "object", "properties": { "name": { "description": "The name of the browser, as in \"Edge\" or \"Safari on iOS\"", "type": "string" }, "releases": { - "description": "The browser's releases", + "type": "array", "items": { "$ref": "#/definitions/Release" - }, - "type": "array" + } } }, - "required": [ - "name", - "releases" - ], - "type": "object" + "required": ["name", "releases"], + "additionalProperties": false }, "FeatureData": { - "additionalProperties": false, + "description": "A feature data entry", + "type": "object", "properties": { - "caniuse": { - "anyOf": [ - { - "type": "string" - }, - { - "items": { - "type": "string" - }, - "minItems": 2, - "type": "array" - } - ], - "description": "caniuse.com identifier(s)" - }, - "compat_features": { - "description": "Sources of support data for this feature", - "items": { - "type": "string" - }, - "type": "array" + "name": { + "description": "Short name", + "type": "string" }, "description": { "description": "Short description of the feature, as a plain text string", @@ -57,212 +114,39 @@ "description": "Short description of the feature, as an HTML string", "type": "string" }, - "discouraged": { - "additionalProperties": false, - "description": "Whether developers are formally discouraged from using this feature", - "properties": { - "according_to": { - "description": "Links to a formal discouragement notice, such as specification text, intent-to-unship, etc.", - "items": { - "type": "string" - }, - "type": "array" - }, - "alternatives": { - "description": "IDs for features that substitute some or all of this feature's utility", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "according_to" - ], - "type": "object" + "spec": { + "description": "Specification URL(s)", + "$ref": "#/definitions/URLOrURLs" }, "group": { - "anyOf": [ - { - "type": "string" - }, - { - "items": { - "type": "string" - }, - "minItems": 2, - "type": "array" - } - ], - "description": "Group identifier(s)" - }, - "name": { - "description": "Short name", - "type": "string" + "description": "Group identifier(s)", + "$ref": "#/definitions/StringOrStrings" }, "snapshot": { - "anyOf": [ - { - "type": "string" - }, - { - "items": { - "type": "string" - }, - "minItems": 2, - "type": "array" - } - ], - "description": "Snapshot identifier(s)" + "description": "Snapshot identifier(s)", + "$ref": "#/definitions/StringOrStrings" }, - "spec": { - "anyOf": [ - { - "type": "string" - }, - { - "items": { - "type": "string" - }, - "minItems": 2, - "type": "array" - } - ], - "description": "Specification URL(s)" + "caniuse": { + "description": "caniuse.com identifier(s)", + "$ref": "#/definitions/StringOrStrings" + }, + "compat_features": { + "description": "Sources of support data for this feature", + "$ref": "#/definitions/Strings" }, "status": { - "additionalProperties": false, - "description": "Whether a feature is considered a \"baseline\" web platform feature and when it achieved that status", - "properties": { - "baseline": { - "description": "Whether the feature is Baseline (low substatus), Baseline (high substatus), or not (false)", - "enum": [ - "high", - "low", - false - ], - "type": [ - "string", - "boolean" - ] - }, - "baseline_high_date": { - "description": "Date the feature achieved Baseline high status", - "type": "string" - }, - "baseline_low_date": { - "description": "Date the feature achieved Baseline low status", - "type": "string" - }, - "by_compat_key": { - "additionalProperties": { - "additionalProperties": false, - "properties": { - "baseline": { - "description": "Whether the feature is Baseline (low substatus), Baseline (high substatus), or not (false)", - "enum": [ - "high", - "low", - false - ], - "type": [ - "string", - "boolean" - ] - }, - "baseline_high_date": { - "description": "Date the feature achieved Baseline high status", - "type": "string" - }, - "baseline_low_date": { - "description": "Date the feature achieved Baseline low status", - "type": "string" - }, - "support": { - "additionalProperties": false, - "description": "Browser versions that most-recently introduced the feature", - "properties": { - "chrome": { - "type": "string" - }, - "chrome_android": { - "type": "string" - }, - "edge": { - "type": "string" - }, - "firefox": { - "type": "string" - }, - "firefox_android": { - "type": "string" - }, - "safari": { - "type": "string" - }, - "safari_ios": { - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "baseline", - "support" - ], - "type": "object" - }, - "description": "Statuses for each key in the feature's compat_features list, if applicable. Not available to the npm release of web-features.", - "type": "object" - }, - "support": { - "additionalProperties": false, - "description": "Browser versions that most-recently introduced the feature", - "properties": { - "chrome": { - "type": "string" - }, - "chrome_android": { - "type": "string" - }, - "edge": { - "type": "string" - }, - "firefox": { - "type": "string" - }, - "firefox_android": { - "type": "string" - }, - "safari": { - "type": "string" - }, - "safari_ios": { - "type": "string" - } - }, - "type": "object" - } - }, - "required": [ - "baseline", - "support" - ], - "type": "object" + "description": "Whether a feature is considered a \"Baseline\" web platform feature and when it achieved that status", + "$ref": "#/definitions/StatusHeadline" + }, + "discouraged": { + "description": "Whether developers are formally discouraged from using this feature", + "$ref": "#/definitions/Discouraged" } }, - "required": [ - "name", - "description", - "description_html", - "spec", - "status" - ], - "type": "object" + "required": ["name", "description", "description_html", "spec"] }, "GroupData": { - "additionalProperties": false, + "type": "object", "properties": { "name": { "description": "Short name", @@ -273,117 +157,146 @@ "type": "string" } }, - "required": [ - "name" - ], - "type": "object" + "required": ["name"], + "additionalProperties": false }, - "Release": { - "additionalProperties": false, - "description": "Browser release information", + "Status": { + "type": "object", "properties": { - "date": { - "description": "The release date, as in \"2023-12-11\"", - "type": "string" + "baseline": { + "description": "Whether the feature is Baseline (low substatus), Baseline (high substatus), or not (false)", + "enum": ["high", "low", false] }, - "version": { - "description": "The version string, as in \"10\" or \"17.1\"", - "type": "string" - } - }, - "required": [ - "version", - "date" - ], - "type": "object" - }, - "SnapshotData": { - "additionalProperties": false, - "properties": { - "name": { - "description": "Short name", + "baseline_high_date": { + "description": "Date the feature achieved Baseline high status", "type": "string" }, - "spec": { - "description": "Specification", + "baseline_low_date": { + "description": "Date the feature achieved Baseline low status", "type": "string" - } - }, - "required": [ - "name", - "spec" - ], - "type": "object" - }, - "WebFeaturesData": { - "additionalProperties": false, - "properties": { - "browsers": { - "additionalProperties": false, - "description": "Browsers and browser release data", + }, + "support": { + "description": "Browser versions that most-recently introduced the feature", + "type": "object", "properties": { "chrome": { - "$ref": "#/definitions/BrowserData" + "type": "string" }, "chrome_android": { - "$ref": "#/definitions/BrowserData" + "type": "string" }, "edge": { - "$ref": "#/definitions/BrowserData" + "type": "string" }, "firefox": { - "$ref": "#/definitions/BrowserData" + "type": "string" }, "firefox_android": { - "$ref": "#/definitions/BrowserData" + "type": "string" }, "safari": { - "$ref": "#/definitions/BrowserData" + "type": "string" }, "safari_ios": { - "$ref": "#/definitions/BrowserData" + "type": "string" } }, - "required": [ - "chrome", - "chrome_android", - "edge", - "firefox", - "firefox_android", - "safari", - "safari_ios" - ], - "type": "object" + "additionalProperties": false + } + }, + "required": ["baseline", "support"] + }, + "StatusHeadline": { + "type": "object", + "allOf": [ + { "$ref": "#/definitions/Status" }, + { + "type": "object", + "properties": { + "by_compat_key": { + "description": "Statuses for each key in the feature's compat_features list, if applicable. Not available to the npm release of web-features.", + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/Status" + } + } + } + } + ] + }, + "Release": { + "description": "Browser release information", + "type": "object", + "properties": { + "version": { + "description": "The version string, as in \"10\" or \"17.1\"", + "type": "string" }, - "features": { - "additionalProperties": { - "$ref": "#/definitions/FeatureData" - }, - "description": "Feature identifiers and data", - "type": "object" + "date": { + "description": " The release date, as in \"2023-12-11\"", + "type": "string" + } + }, + "required": ["version", "date"], + "additionalProperties": false + }, + "SnapshotData": { + "type": "object", + "properties": { + "name": { + "description": "Short name", + "type": "string" }, - "groups": { - "additionalProperties": { - "$ref": "#/definitions/GroupData" - }, - "description": "Group identifiers and data", - "type": "object" + "spec": { + "description": "Specification", + "$ref": "#/definitions/URL" + } + } + }, + "StringOrStrings": { + "oneOf": [ + { + "type": "string" }, - "snapshots": { - "additionalProperties": { - "$ref": "#/definitions/SnapshotData" + { + "type": "array", + "items": { + "type": "string" }, - "description": "Snapshot identifiers and data", - "type": "object" + "minItems": 2 } + ] + }, + "Strings": { + "type": "array", + "items": { + "type": "string" }, - "required": [ - "browsers", - "features", - "groups", - "snapshots" - ], - "type": "object" + "minItems": 1 + }, + "URL": { + "type": "string" + }, + "URLs": { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "URLOrURLs": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + }, + "minItems": 2 + } + ] } } -} \ No newline at end of file +} diff --git a/schemas/new.schema.json b/schemas/new.schema.json deleted file mode 100644 index e2305ad2d62..00000000000 --- a/schemas/new.schema.json +++ /dev/null @@ -1,302 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema", - "description": "The top-level web-features data package", - "type": "object", - "properties": { - "browsers": { - "description": "Browsers and browser release data", - "type": "object", - "properties": { - "chrome": { - "$ref": "#/definitions/BrowserData" - }, - "chrome_android": { - "$ref": "#/definitions/BrowserData" - }, - "edge": { - "$ref": "#/definitions/BrowserData" - }, - "firefox": { - "$ref": "#/definitions/BrowserData" - }, - "firefox_android": { - "$ref": "#/definitions/BrowserData" - }, - "safari": { - "$ref": "#/definitions/BrowserData" - }, - "safari_ios": { - "$ref": "#/definitions/BrowserData" - } - }, - "required": [ - "chrome", - "chrome_android", - "edge", - "firefox", - "firefox_android", - "safari", - "safari_ios" - ], - "additionalProperties": false - }, - "features": { - "description": "Feature identifiers and data", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/FeatureData" - } - }, - "groups": { - "description": "Group identifiers and data", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/GroupData" - } - }, - "snapshots": { - "description": "Snapshot identifiers and data", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/SnapshotData" - } - } - }, - "required": ["browsers", "features", "groups"], - "additionalProperties": false, - "definitions": { - "Discouraged": { - "type": "object", - "properties": { - "according_to": { - "description": "Links to a formal discouragement notice, such as specification text, intent-to-unship, etc.", - "$ref": "#/definitions/URLs" - }, - "alternatives": { - "description": "IDs for features that substitute some or all of this feature's utility", - "$ref": "#/definitions/Strings" - } - }, - "required": ["according_to"], - "additionalProperties": false - }, - "BrowserData": { - "description": "Browser information", - "type": "object", - "properties": { - "name": { - "description": "The name of the browser, as in \"Edge\" or \"Safari on iOS\"", - "type": "string" - }, - "releases": { - "type": "array", - "items": { - "$ref": "#/definitions/Release" - } - } - }, - "required": ["name", "releases"], - "additionalProperties": false - }, - "FeatureData": { - "description": "A feature data entry", - "type": "object", - "properties": { - "name": { - "description": "Short name", - "type": "string" - }, - "description": { - "description": "Short description of the feature, as a plain text string", - "type": "string" - }, - "description_html": { - "description": "Short description of the feature, as an HTML string", - "type": "string" - }, - "spec": { - "description": "Specification URL(s)", - "$ref": "#/definitions/URLOrURLs" - }, - "group": { - "description": "Group identifier(s)", - "$ref": "#/definitions/StringOrStrings" - }, - "snapshot": { - "description": "Snapshot identifier(s)", - "$ref": "#/definitions/StringOrStrings" - }, - "caniuse": { - "description": "caniuse.com identifier(s)", - "$ref": "#/definitions/StringOrStrings" - }, - "compat_features": { - "description": "Sources of support data for this feature", - "$ref": "#/definitions/Strings" - }, - "status": { - "description": "Whether a feature is considered a \"Baseline\" web platform feature and when it achieved that status", - "$ref": "#/definitions/StatusHeadline" - }, - "discouraged": { - "description": "Whether developers are formally discouraged from using this feature", - "$ref": "#/definitions/Discouraged" - } - }, - "required": ["name", "description", "description_html", "spec"] - }, - "GroupData": { - "type": "object", - "properties": { - "name": { - "description": "Short name", - "type": "string" - }, - "parent": { - "description": "Identifier of parent group", - "type": "string" - } - }, - "required": ["name"], - "additionalProperties": false - }, - "Status": { - "type": "object", - "properties": { - "baseline": { - "description": "Whether the feature is Baseline (low substatus), Baseline (high substatus), or not (false)", - "enum": ["high", "low", false] - }, - "baseline_high_date": { - "description": "Date the feature achieved Baseline high status", - "type": "string" - }, - "baseline_low_date": { - "description": "Date the feature achieved Baseline low status", - "type": "string" - }, - "support": { - "description": "Browser versions that most-recently introduced the feature", - "type": "object", - "properties": { - "chrome": { - "type": "string" - }, - "chrome_android": { - "type": "string" - }, - "edge": { - "type": "string" - }, - "firefox": { - "type": "string" - }, - "firefox_android": { - "type": "string" - }, - "safari": { - "type": "string" - }, - "safari_ios": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "required": ["baseline", "support"] - }, - "StatusHeadline": { - "type": "object", - "allOf": [ - { "$ref": "#/definitions/Status" }, - { - "type": "object", - "properties": { - "by_compat_key": { - "description": "Statuses for each key in the feature's compat_features list, if applicable. Not available to the npm release of web-features.", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Status" - } - } - } - } - ] - }, - "Release": { - "description": "Browser release information", - "type": "object", - "properties": { - "version": { - "description": "The version string, as in \"10\" or \"17.1\"", - "type": "string" - }, - "date": { - "description": " The release date, as in \"2023-12-11\"", - "type": "string" - } - }, - "required": ["version", "date"], - "additionalProperties": false - }, - "SnapshotData": { - "type": "object", - "properties": { - "name": { - "description": "Short name", - "type": "string" - }, - "spec": { - "description": "Specification", - "$ref": "#/definitions/URL" - } - } - }, - "StringOrStrings": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 2 - } - ] - }, - "Strings": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "URL": { - "type": "string" - }, - "URLs": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "URLOrURLs": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 2 - } - ] - } - } -} diff --git a/scripts/schema.ts b/scripts/schema.ts index 357bcf85a21..24cc429c0b3 100644 --- a/scripts/schema.ts +++ b/scripts/schema.ts @@ -9,15 +9,15 @@ import { validate } from "./validate.js"; let status: 0 | 1 = 0; -function checkSchemaConsistency(): void { - const schemaPath: string = path.join(path.dirname(url.fileURLToPath(import.meta.url)), "../schemas/data.schema.json"); - const schemaOnDisk: string = fs.readFileSync(schemaPath, { encoding: "utf-8"}); - const schemaGenerated: string = child_process.execSync("npm run --silent schema", { encoding: "utf-8"}).trim(); +function checkGeneratedFileConsistency(): void { + const quicktypePath: string = path.join(path.dirname(url.fileURLToPath(import.meta.url)), "../types.quicktype.ts"); + const quicktypeOnDisk: string = fs.readFileSync(quicktypePath, { encoding: "utf-8"}); + const quicktypeGenerated: string = child_process.execSync("npm run --silent schematypes", { encoding: "utf-8"}); - if (schemaOnDisk !== schemaGenerated) { - console.error("There's a mismatch between the schema on disk and types in `index.ts`."); + if (quicktypeOnDisk !== quicktypeGenerated) { + console.error("There's a mismatch between the types generated by quicktype and the types in `types.quicktype.ts`."); console.error("This may produce misleading results for feature validation."); - console.error("To fix this, run `npm run schema:write`."); + console.error("To fix this, run `npm run schematypes:write`."); status = 1; } } @@ -35,6 +35,6 @@ function valid() { } } -checkSchemaConsistency(); +checkGeneratedFileConsistency(); valid(); process.exit(status); diff --git a/scripts/update-drafts.ts b/scripts/update-drafts.ts index 96b55c608c0..f3fac0babdf 100644 --- a/scripts/update-drafts.ts +++ b/scripts/update-drafts.ts @@ -11,7 +11,7 @@ import { Document, parse } from "yaml"; import yargs from "yargs"; import { features } from "../index.js"; -import { FeatureData } from "../newtypes.js"; +import { FeatureData } from "../types.js"; type WebSpecsSpec = (typeof webSpecs)[number]; diff --git a/scripts/validate.ts b/scripts/validate.ts index 2120c4e1913..8c88eecfef1 100644 --- a/scripts/validate.ts +++ b/scripts/validate.ts @@ -1,7 +1,7 @@ import Ajv from "ajv"; import assert from "node:assert/strict"; -import * as schema from "../schemas/new.schema.json" with { type: "json" }; +import * as schema from "../schemas/data.schema.json" with { type: "json" }; const ajv = new Ajv({ allErrors: true, allowUnionTypes: true }); // TODO: turn on strictNullChecks, fix all the errors, and replace this with: diff --git a/new.quicktype.ts b/types.quicktype.ts similarity index 100% rename from new.quicktype.ts rename to types.quicktype.ts diff --git a/types.ts b/types.ts index d1492262104..f988e1e5144 100644 --- a/types.ts +++ b/types.ts @@ -1,97 +1,65 @@ -export interface WebFeaturesData { - /** Browsers and browser release data */ - browsers: { [key in BrowserIdentifier]: BrowserData }; - /** Feature identifiers and data */ - features: { [key: string]: FeatureData }; - /** Group identifiers and data */ - groups: { [key: string]: GroupData }; - /** Snapshot identifiers and data */ - snapshots: { [key: string]: SnapshotData }; -} - +// Quicktype produces a definitions that are correct, but not as narrow or +// well-named as hand-written type definition might produce. This module takes +// the Quicktype-generated types as renames or modifies the types to be somewhat +// nicer to work with in TypeScript. -/** Browser information */ -export interface BrowserData { - /** The name of the browser, as in "Edge" or "Safari on iOS" */ - name: string; - /** The browser's releases */ - releases: Release[]; -} +import type { + BaselineEnum as BaselineHighLow, + BrowserData, + Browsers, + Discouraged, + GroupData, + FeatureData as QuicktypeMonolithicFeatureData, + WebFeaturesData as QuicktypeWebFeaturesData, + Release, + SnapshotData, + Status, + Support, + StatusHeadline as SupportStatus, +} from "./types.quicktype"; -/** Browser release information */ -export interface Release { - /** The version string, as in "10" or "17.1" */ - version: string; - /** The release date, as in "2023-12-11" */ - date: string; -} +// Passthrough types +export type { + BaselineHighLow, + BrowserData, + Browsers, + Discouraged, + GroupData, + Release, + SnapshotData, + Status, + Support, + SupportStatus +}; -export interface FeatureData { - /** Short name */ - name: string; - /** Short description of the feature, as a plain text string */ - description: string; - /** Short description of the feature, as an HTML string */ - description_html: string; - /** Specification URL(s) */ - spec: string | [string, string, ...string[]]; - /** Group identifier(s) */ - group?: string | [string, string, ...string[]]; - /** Snapshot identifier(s) */ - snapshot?: string | [string, string, ...string[]]; - /** caniuse.com identifier(s) */ - caniuse?: string | [string, string, ...string[]]; - /** Whether a feature is considered a "baseline" web platform feature and when it achieved that status */ - status: SupportStatus; - /** Sources of support data for this feature */ - compat_features?: string[]; - /** Whether developers are formally discouraged from using this feature */ - discouraged?: Discouraged; +export interface WebFeaturesData + extends Pick { + features: { [key: string]: FeatureData }; } -type BrowserIdentifier = "chrome" | "chrome_android" | "edge" | "firefox" | "firefox_android" | "safari" | "safari_ios"; +export type FeatureData = Required< + Pick< + QuicktypeMonolithicFeatureData, + "description_html" | "description" | "name" | "spec" | "status" + > +> & + Partial< + Pick< + QuicktypeMonolithicFeatureData, + "caniuse" | "compat_features" | "discouraged" + > + >; -type BaselineHighLow = "high" | "low"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const t1: FeatureData = { + name: "Test", + description: "Hi", + description_html: "Hi", + spec: "", + status: { + baseline: false, + support: {}, + }, +}; -interface Status { - /** Whether the feature is Baseline (low substatus), Baseline (high substatus), or not (false) */ - baseline: BaselineHighLow | false; - /** Date the feature achieved Baseline low status */ - baseline_low_date?: string; - /** Date the feature achieved Baseline high status */ - baseline_high_date?: string; - /** Browser versions that most-recently introduced the feature */ - support: { - [K in BrowserIdentifier]?: string; - }; -} - -interface SupportStatus extends Status { - /** Statuses for each key in the feature's compat_features list, if applicable. Not available to the npm release of web-features. */ - by_compat_key?: Record -} - -interface Discouraged { - /** Links to a formal discouragement notice, such as specification text, intent-to-unship, etc. */ - according_to: string[]; - /** IDs for features that substitute some or all of this feature's utility */ - alternatives?: string[]; - // TODO: alternatives ought to be `(keyof WebFeaturesData["features"])[]` - // but ts-json-schema-generator seems to have long-standing unresolved bugs - // around this. Remove this when - // https://github.com/web-platform-dx/web-features/issues/2722 is resolved. -} - -export interface GroupData { - /** Short name */ - name: string; - /** Identifier of parent group */ - parent?: string; -} - -export interface SnapshotData { - /** Short name */ - name: string; - /** Specification */ - spec: string; -} +export type BrowserIdentifier = keyof Browsers; From 77bf8f956943bd134c4bfa1dd1e3c6af2fcf5450 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Mon, 26 May 2025 19:28:42 +0200 Subject: [PATCH 03/21] Add new types to web-features package build --- .gitignore | 4 ++-- scripts/build.ts | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index d0e387936b6..68310b137ca 100644 --- a/.gitignore +++ b/.gitignore @@ -5,9 +5,9 @@ packages/web-features/index.d.ts packages/**/LICENSE.txt packages/web-features/data.json packages/web-features/data.schema.json -packages/web-features/types.ts +packages/web-features/types* +packages/web-features/index.js data.extended.json -index.js # Ignore files created & used by pages & 11ty /_site/** diff --git a/scripts/build.ts b/scripts/build.ts index 35f346481ce..3d1a883bbec 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -35,7 +35,12 @@ yargs(process.argv.slice(2)) function buildPackage() { const packageDir = new URL("./packages/web-features/", rootDir); - const filesToCopy = ["LICENSE.txt", "types.ts", "schemas/data.schema.json"]; + const filesToCopy = [ + "LICENSE.txt", + "types.quicktype.ts", + "types.ts", + "schemas/data.schema.json", + ]; if (!valid(data)) { logger.error("Data failed schema validation. No package built."); From 7742c646facf52d8bc8094da1477e64460020f11 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Tue, 27 May 2025 14:05:26 +0200 Subject: [PATCH 04/21] Format types.ts --- .prettierignore | 1 - types.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.prettierignore b/.prettierignore index 7df15e39874..eb97a6eba00 100644 --- a/.prettierignore +++ b/.prettierignore @@ -10,7 +10,6 @@ # TODO: Format all these files README.md index.ts -types.ts 2022-backgrounder.md towards-features.md /.github/** diff --git a/types.ts b/types.ts index f988e1e5144..aa925dec65f 100644 --- a/types.ts +++ b/types.ts @@ -29,7 +29,7 @@ export type { SnapshotData, Status, Support, - SupportStatus + SupportStatus, }; export interface WebFeaturesData From 7f7aa98cf58e762fcb778d402911b62abf09bc85 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Mon, 26 May 2025 19:34:38 +0200 Subject: [PATCH 05/21] Fix schema key sort order --- schemas/data.schema.json | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/schemas/data.schema.json b/schemas/data.schema.json index e2305ad2d62..a86a292f912 100644 --- a/schemas/data.schema.json +++ b/schemas/data.schema.json @@ -160,6 +160,22 @@ "required": ["name"], "additionalProperties": false }, + "Release": { + "description": "Browser release information", + "type": "object", + "properties": { + "version": { + "description": "The version string, as in \"10\" or \"17.1\"", + "type": "string" + }, + "date": { + "description": " The release date, as in \"2023-12-11\"", + "type": "string" + } + }, + "required": ["version", "date"], + "additionalProperties": false + }, "Status": { "type": "object", "properties": { @@ -224,22 +240,6 @@ } ] }, - "Release": { - "description": "Browser release information", - "type": "object", - "properties": { - "version": { - "description": "The version string, as in \"10\" or \"17.1\"", - "type": "string" - }, - "date": { - "description": " The release date, as in \"2023-12-11\"", - "type": "string" - } - }, - "required": ["version", "date"], - "additionalProperties": false - }, "SnapshotData": { "type": "object", "properties": { From 459ac43d2ca009900a82fef150d7f1c0b8eb7975 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Tue, 27 May 2025 14:04:19 +0200 Subject: [PATCH 06/21] Add redirect feature schema types --- schemas/data.schema.json | 56 +++++++++++++++++++++++++++++++++++++++- types.quicktype.ts | 27 ++++++++++++++++--- types.ts | 27 ++++++++++++++++++- 3 files changed, 104 insertions(+), 6 deletions(-) diff --git a/schemas/data.schema.json b/schemas/data.schema.json index a86a292f912..b7c7d964dcd 100644 --- a/schemas/data.schema.json +++ b/schemas/data.schema.json @@ -44,7 +44,17 @@ "description": "Feature identifiers and data", "type": "object", "additionalProperties": { - "$ref": "#/definitions/FeatureData" + "oneOf": [ + { + "$ref": "#/definitions/FeatureData" + }, + { + "$ref": "#/definitions/FeatureMovedData" + }, + { + "$ref": "#/definitions/FeatureSplitData" + } + ] } }, "groups": { @@ -176,6 +186,50 @@ "required": ["version", "date"], "additionalProperties": false }, + "FeatureMovedData": { + "description": "A feature has permanently moved to exactly one other ID", + "type": "object", + "properties": { + "redirect": { + "type": "object", + "properties": { + "reason": { + "const": "moved" + }, + "target": { + "description": "The new ID for this feature", + "type": "string" + } + }, + "required": ["reason", "target"], + "additionalProperties": false + } + }, + "required": ["redirect"], + "additionalProperties": false + }, + "FeatureSplitData": { + "description": "A feature has split into two or more other features", + "type": "object", + "properties": { + "redirect": { + "type": "object", + "properties": { + "reason": { + "const": "split" + }, + "targets": { + "description": "The new IDs for this feature", + "$ref": "#/definitions/Strings" + } + }, + "required": ["reason", "targets"], + "additionalProperties": false + } + }, + "required": ["redirect"], + "additionalProperties": false + }, "Status": { "type": "object", "properties": { diff --git a/types.quicktype.ts b/types.quicktype.ts index 8a0828f425f..40e07ea3fe2 100644 --- a/types.quicktype.ts +++ b/types.quicktype.ts @@ -60,6 +60,10 @@ export interface Release { /** * A feature data entry + * + * A feature has permanently moved to exactly one other ID + * + * A feature has split into two or more other features */ export interface FeatureData { /** @@ -73,11 +77,11 @@ export interface FeatureData { /** * Short description of the feature, as a plain text string */ - description: string; + description?: string; /** * Short description of the feature, as an HTML string */ - description_html: string; + description_html?: string; /** * Whether developers are formally discouraged from using this feature */ @@ -89,7 +93,7 @@ export interface FeatureData { /** * Short name */ - name: string; + name?: string; /** * Snapshot identifier(s) */ @@ -97,12 +101,13 @@ export interface FeatureData { /** * Specification URL(s) */ - spec: string[] | string; + spec?: string[] | string; /** * Whether a feature is considered a "Baseline" web platform feature and when it achieved * that status */ status?: StatusHeadline; + redirect?: Redirect; [property: string]: any; } @@ -121,6 +126,20 @@ export interface Discouraged { alternatives?: string[]; } +export interface Redirect { + reason: Reason; + /** + * The new ID for this feature + */ + target?: string; + /** + * The new IDs for this feature + */ + targets?: string[]; +} + +export type Reason = "moved" | "split"; + /** * Whether a feature is considered a "Baseline" web platform feature and when it achieved * that status diff --git a/types.ts b/types.ts index aa925dec65f..f9d80c75ffa 100644 --- a/types.ts +++ b/types.ts @@ -11,6 +11,7 @@ import type { GroupData, FeatureData as QuicktypeMonolithicFeatureData, WebFeaturesData as QuicktypeWebFeaturesData, + Redirect, Release, SnapshotData, Status, @@ -34,7 +35,9 @@ export type { export interface WebFeaturesData extends Pick { - features: { [key: string]: FeatureData }; + features: { + [key: string]: FeatureData | FeatureMovedData | FeatureSplitData; + }; } export type FeatureData = Required< @@ -50,6 +53,28 @@ export type FeatureData = Required< > >; +export type FeatureRedirectData = Required< + Pick +>; + +export interface Moved extends Exclude { + reason: "moved"; + target: Redirect["target"]; +} + +export interface Split extends Exclude { + reason: "split"; + targets: Redirect["targets"]; +} + +export interface FeatureMovedData extends FeatureRedirectData { + redirect: Moved; +} + +export interface FeatureSplitData extends FeatureRedirectData { + redirect: Split; +} + // eslint-disable-next-line @typescript-eslint/no-unused-vars const t1: FeatureData = { name: "Test", From 758e77a69db12428266f66662681dce3fa79a850 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Tue, 27 May 2025 18:02:16 +0200 Subject: [PATCH 07/21] Update scripts to use redirects --- index.ts | 36 +++++++++++++++++---- scripts/build.ts | 4 +++ scripts/dist.ts | 40 ++++++++++++++++++++++++ scripts/find-ranged-headline-statuses.ts | 10 ++++-- scripts/specs.ts | 5 +++ scripts/stats.ts | 15 +++++++-- scripts/update-drafts.ts | 12 ++++--- type-guards.ts | 9 ++++++ 8 files changed, 117 insertions(+), 14 deletions(-) create mode 100644 type-guards.ts diff --git a/index.ts b/index.ts index d7df26716bf..42ca1755f92 100644 --- a/index.ts +++ b/index.ts @@ -4,7 +4,7 @@ import path from 'path'; import { Temporal } from '@js-temporal/polyfill'; import { fdir } from 'fdir'; import YAML from 'yaml'; -import { FeatureData, GroupData, SnapshotData, WebFeaturesData } from './types'; +import { GroupData, SnapshotData, WebFeaturesData } from './types'; import { toString as hastTreeToString } from 'hast-util-to-string'; import rehypeStringify from 'rehype-stringify'; @@ -14,6 +14,7 @@ import { unified } from 'unified'; import { BASELINE_LOW_TO_HIGH_DURATION, coreBrowserSet, parseRangedDateString } from 'compute-baseline'; import { Compat } from 'compute-baseline/browser-compat-data'; +import { isOrdinaryFeatureData, isRedirectData } from './type-guards'; // The longest name allowed, to allow for compact display. const nameMaxLength = 80; @@ -117,7 +118,7 @@ function convertMarkdown(markdown: string) { // Map from BCD keys/paths to web-features identifiers. const bcdToFeatureId: Map = new Map(); -const features: { [key: string]: FeatureData } = {}; +const features: WebFeaturesData["features"] = {}; for (const [key, data] of yamlEntries('features')) { // Draft features reserve an identifier but aren't complete yet. Skip them. if (data[draft]) { @@ -182,11 +183,34 @@ for (const [key, data] of yamlEntries('features')) { features[key] = data; } -// Assert that discouraged feature's alternatives are valid +// Assert that feature references are valid for (const [id, feature] of Object.entries(features)) { - for (const alternative of feature.discouraged?.alternatives ?? []) { - if (!(alternative in features)) { - throw new Error(`${id}'s alternative "${alternative}" is not a valid feature ID`); + if (isOrdinaryFeatureData(feature)) { + for (const alternative of feature.discouraged?.alternatives ?? []) { + if (!(alternative in features)) { + throw new Error(`${id}'s alternative "${alternative}" is not a valid feature ID`); + } + } + } + + if (isRedirectData(feature)) { + const { reason } = feature.redirect; + switch (reason) { + case 'moved': + if (!(feature.redirect.target in features)) { + throw new Error(`${id}'s redirect target "${feature.redirect.target} is not a valid feature ID`); + } + break; + case 'split': + for (const target of feature.redirect.targets) { + if (!(target in features)) { + throw new Error(`${id}'s redirect target "${target}" is not a valid feature ID`); + } + } + break; + default: + reason satisfies never; + throw new Error(`Unhandled redirect reason ${reason}}`); } } } diff --git a/scripts/build.ts b/scripts/build.ts index 3d1a883bbec..c2cda0a5b30 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -68,6 +68,10 @@ function buildPackage() { function buildExtendedJSON() { for (const [id, featureData] of Object.entries(data.features)) { + if ("redirect" in featureData) { + continue; + } + if ( Array.isArray(featureData.compat_features) && featureData.compat_features.length && diff --git a/scripts/dist.ts b/scripts/dist.ts index 209aa1d154a..c04df563136 100644 --- a/scripts/dist.ts +++ b/scripts/dist.ts @@ -14,6 +14,12 @@ import { isDeepStrictEqual } from "node:util"; import winston from "winston"; import YAML, { Document, Scalar, YAMLSeq } from "yaml"; import yargs from "yargs"; +import { isRedirectData } from "../type-guards"; +import { + FeatureMovedData, + FeatureRedirectData, + FeatureSplitData, +} from "../types"; const compat = new Compat(); @@ -181,6 +187,36 @@ function compareStatus(a: SupportStatus, b: SupportStatus) { return 0; } +function toRedirectDist( + id: string, + source: FeatureMovedData | FeatureRedirectData, +): YAML.Document { + const dist = new Document({}); + + const comment = [ + `Generated from: ${id}.yml`, + `This file intentionally left blank.`, + `Do not edit this file.`, + ]; + + switch (source.redirect.reason) { + case "moved": + comment.push( + `The data for this feature has moved to ${(source as FeatureMovedData).redirect.target}.yml`, + ); + break; + case "split": + const targets = (source as FeatureSplitData).redirect.targets; + comment.push(`The data for this feature has moved to:`); + comment.push(...targets.map((to) => ` - ${to}.yml`)); + break; + } + + dist.commentBefore = comment.map((line) => ` ${line}`).join("\n"); + + return dist; +} + /** * Generate a dist YAML document from a feature definition YAML file path. * @@ -192,6 +228,10 @@ function toDist(sourcePath: string): YAML.Document { const source = YAML.parse(fs.readFileSync(sourcePath, { encoding: "utf-8" })); const { name: id } = path.parse(sourcePath); + if (isRedirectData(source)) { + return toRedirectDist(id, source); + } + // Collect tagged compat features. A `compat_features` list in the source // takes precedence, but can be removed if it matches the tagged features. const taggedCompatFeatures = (tagsToFeatures.get(`web-features:${id}`) ?? []) diff --git a/scripts/find-ranged-headline-statuses.ts b/scripts/find-ranged-headline-statuses.ts index a5dabd2f0cd..e60f21efd00 100644 --- a/scripts/find-ranged-headline-statuses.ts +++ b/scripts/find-ranged-headline-statuses.ts @@ -1,7 +1,13 @@ import { features } from "../index"; +import { isOrdinaryFeatureData } from "../type-guards"; for (const [key, data] of Object.entries(features)) { - if ((data.status.baseline_low_date ?? "").includes("≤")) { - console.log(key); + if (isOrdinaryFeatureData(data)) { + if ( + "status" in data && + (data.status.baseline_low_date ?? "").includes("≤") + ) { + console.log(key); + } } } diff --git a/scripts/specs.ts b/scripts/specs.ts index 7978e6e0069..ddf95c26228 100644 --- a/scripts/specs.ts +++ b/scripts/specs.ts @@ -3,6 +3,7 @@ import assert from "node:assert/strict"; import webSpecs from 'web-specs' with { type: 'json' }; import { features } from '../index.js'; +import { isRedirectData } from "../type-guards.js"; // Specs needs to be in "good standing". Nightly URLs are used if available, // otherwise the snapshot/versioned URL is used. See browser-specs/web-specs @@ -191,6 +192,10 @@ for (const [allowedUrl, message] of defaultAllowlist) { } for (const [id, data] of Object.entries(features)) { + if (isRedirectData(data)) { + continue; + } + const specs = Array.isArray(data.spec) ? data.spec : [data.spec]; for (const spec of specs) { let url: URL; diff --git a/scripts/stats.ts b/scripts/stats.ts index 1894c73ba96..404efe51ddf 100644 --- a/scripts/stats.ts +++ b/scripts/stats.ts @@ -2,6 +2,7 @@ import { Compat } from "compute-baseline/browser-compat-data"; import { fileURLToPath } from "node:url"; import yargs from "yargs"; import { features } from "../index.js"; +import { isOrdinaryFeatureData } from "../type-guards.js"; const argv = yargs(process.argv.slice(2)) .scriptName("stats") @@ -14,11 +15,20 @@ const argv = yargs(process.argv.slice(2)) }).argv; export function stats(detailed: boolean = false) { - const featureCount = Object.keys(features).length; + const featureCount = Object.values(features).filter( + isOrdinaryFeatureData, + ).length; const keys = []; const doneKeys = Array.from( - new Set(Object.values(features).flatMap((f) => f.compat_features ?? [])), + new Set( + Object.values(features).flatMap((f) => { + if (isOrdinaryFeatureData(f)) { + return f.compat_features ?? []; + } + return []; + }), + ), ); const toDoKeys = []; @@ -35,6 +45,7 @@ export function stats(detailed: boolean = false) { } const featureSizes = Object.values(features) + .filter(isOrdinaryFeatureData) .map((feature) => (feature.compat_features ?? []).length) .sort((a, b) => a - b); diff --git a/scripts/update-drafts.ts b/scripts/update-drafts.ts index f3fac0babdf..ab84d708596 100644 --- a/scripts/update-drafts.ts +++ b/scripts/update-drafts.ts @@ -11,6 +11,7 @@ import { Document, parse } from "yaml"; import yargs from "yargs"; import { features } from "../index.js"; +import { isOrdinaryFeatureData } from "../type-guards.js"; import { FeatureData } from "../types.js"; type WebSpecsSpec = (typeof webSpecs)[number]; @@ -84,7 +85,7 @@ async function main() { // Build a map of used BCD keys to feature. const webFeatures = new Map(); Object.values(features).map((data) => { - if (data.compat_features) { + if (isOrdinaryFeatureData(data) && data.compat_features) { for (const compatFeature of data.compat_features) { webFeatures.set(compatFeature, data.name); } @@ -261,9 +262,12 @@ async function main() { } // Clean up completed specs, even if they've been superseded - const assignedKeys = Object.values(features).flatMap( - (f) => f.compat_features ?? [], - ); + const assignedKeys = Object.values(features).flatMap((f) => { + if (isOrdinaryFeatureData(f)) { + return f.compat_features ?? []; + } + return []; + }); for (const spec of webSpecs) { const id = formatIdentifier(spec.shortname); const destination = `features/draft/spec/${id}.yml`; diff --git a/type-guards.ts b/type-guards.ts new file mode 100644 index 00000000000..d3d5bc0f66e --- /dev/null +++ b/type-guards.ts @@ -0,0 +1,9 @@ +import type { FeatureData, FeatureRedirectData } from "./types"; + +export function isOrdinaryFeatureData(x: unknown): x is FeatureData { + return typeof x === "object" && "name" in x; +} + +export function isRedirectData(x: unknown): x is FeatureRedirectData { + return typeof x === "object" && "redirect" in x; +} From 229469836b7d2aae849bcbdba6f0dc6e31690107 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Tue, 27 May 2025 18:12:09 +0200 Subject: [PATCH 08/21] Split webcodecs --- features/audio-codecs.yml | 53 ++++++++ features/audio-codecs.yml.dist | 90 +++++++++++++ features/video-codecs.yml | 80 ++++++++++++ features/video-codecs.yml.dist | 144 +++++++++++++++++++++ features/webcodecs.yml | 15 +-- features/webcodecs.yml.dist | 225 +-------------------------------- 6 files changed, 378 insertions(+), 229 deletions(-) create mode 100644 features/audio-codecs.yml create mode 100644 features/audio-codecs.yml.dist create mode 100644 features/video-codecs.yml create mode 100644 features/video-codecs.yml.dist diff --git a/features/audio-codecs.yml b/features/audio-codecs.yml new file mode 100644 index 00000000000..d638359ad7a --- /dev/null +++ b/features/audio-codecs.yml @@ -0,0 +1,53 @@ +name: Audio codecs +description: The `AudioEncoder` and `AudioDecoder` APIs (part of WebCodecs) provide low-level access to chunks of audio samples, for full control over the way audio is processed. +spec: https://w3c.github.io/webcodecs/ +status: + compute_from: + - api.AudioDecoder.AudioDecoder + - api.AudioEncoder.AudioEncoder +compat_features: + - api.AudioData + - api.AudioData.AudioData + - api.AudioData.allocationSize + - api.AudioData.clone + - api.AudioData.close + - api.AudioData.copyTo + - api.AudioData.duration + - api.AudioData.format + - api.AudioData.numberOfChannels + - api.AudioData.numberOfFrames + - api.AudioData.sampleRate + - api.AudioData.timestamp + - api.AudioDecoder + - api.AudioDecoder.AudioDecoder + - api.AudioDecoder.close + - api.AudioDecoder.configure + - api.AudioDecoder.decode + - api.AudioDecoder.decodeQueueSize + - api.AudioDecoder.dequeue_event + - api.AudioDecoder.flush + - api.AudioDecoder.isConfigSupported_static + - api.AudioDecoder.reset + - api.AudioDecoder.state + - api.AudioEncoder + - api.AudioEncoder.AudioEncoder + - api.AudioEncoder.close + - api.AudioEncoder.configure + - api.AudioEncoder.configure.bitrateMode + - api.AudioEncoder.configure.opus + - api.AudioEncoder.configure.opus.opus_application + - api.AudioEncoder.configure.opus.opus_signal + - api.AudioEncoder.dequeue_event + - api.AudioEncoder.encode + - api.AudioEncoder.encodeQueueSize + - api.AudioEncoder.flush + - api.AudioEncoder.isConfigSupported_static + - api.AudioEncoder.reset + - api.AudioEncoder.state + - api.EncodedAudioChunk + - api.EncodedAudioChunk.EncodedAudioChunk + - api.EncodedAudioChunk.byteLength + - api.EncodedAudioChunk.copyTo + - api.EncodedAudioChunk.duration + - api.EncodedAudioChunk.timestamp + - api.EncodedAudioChunk.type diff --git a/features/audio-codecs.yml.dist b/features/audio-codecs.yml.dist new file mode 100644 index 00000000000..e2245819f6f --- /dev/null +++ b/features/audio-codecs.yml.dist @@ -0,0 +1,90 @@ +# Generated from: audio-codecs.yml +# Do not edit this file by hand. Edit the source file instead! + +status: + baseline: false + support: + chrome: "94" + chrome_android: "94" + edge: "94" + firefox: "130" +compat_features: + # ⬇️ Same status as overall feature ⬇️ + # baseline: false + # support: + # chrome: "94" + # chrome_android: "94" + # edge: "94" + # firefox: "130" + - api.AudioData + - api.AudioData.AudioData + - api.AudioData.allocationSize + - api.AudioData.clone + - api.AudioData.close + - api.AudioData.copyTo + - api.AudioData.duration + - api.AudioData.format + - api.AudioData.numberOfChannels + - api.AudioData.numberOfFrames + - api.AudioData.sampleRate + - api.AudioData.timestamp + - api.AudioDecoder + - api.AudioDecoder.AudioDecoder + - api.AudioDecoder.close + - api.AudioDecoder.configure + - api.AudioDecoder.decode + - api.AudioDecoder.decodeQueueSize + - api.AudioDecoder.flush + - api.AudioDecoder.isConfigSupported_static + - api.AudioDecoder.reset + - api.AudioDecoder.state + - api.AudioEncoder + - api.AudioEncoder.AudioEncoder + - api.AudioEncoder.close + - api.AudioEncoder.configure + - api.AudioEncoder.encode + - api.AudioEncoder.encodeQueueSize + - api.AudioEncoder.flush + - api.AudioEncoder.isConfigSupported_static + - api.AudioEncoder.reset + - api.AudioEncoder.state + - api.EncodedAudioChunk + - api.EncodedAudioChunk.EncodedAudioChunk + - api.EncodedAudioChunk.byteLength + - api.EncodedAudioChunk.copyTo + - api.EncodedAudioChunk.duration + - api.EncodedAudioChunk.timestamp + - api.EncodedAudioChunk.type + + # baseline: false + # support: + # chrome: "106" + # chrome_android: "106" + # edge: "106" + # firefox: "130" + - api.AudioDecoder.dequeue_event + - api.AudioEncoder.dequeue_event + + # baseline: false + # support: + # chrome: "110" + # chrome_android: "110" + # edge: "110" + # firefox: "130" + - api.AudioEncoder.configure.opus + + # baseline: false + # support: + # chrome: "119" + # chrome_android: "119" + # edge: "119" + # firefox: "130" + - api.AudioEncoder.configure.bitrateMode + + # baseline: false + # support: + # chrome: "126" + # chrome_android: "126" + # edge: "126" + - api.AudioEncoder.configure.opus.opus_application + - api.AudioEncoder.configure.opus.opus_signal diff --git a/features/video-codecs.yml b/features/video-codecs.yml new file mode 100644 index 00000000000..2c81b6d9ac8 --- /dev/null +++ b/features/video-codecs.yml @@ -0,0 +1,80 @@ +name: Video codecs +description: The `VideoEncoder` and `VideoDecoder` APIs (part of WebCodecs) provide low-level access to individual video frames, for full control over the way video is processed. +spec: https://w3c.github.io/webcodecs/ +status: + compute_from: + - api.VideoDecoder.VideoDecoder + - api.VideoEncoder.VideoEncoder +compat_features: + - api.EncodedVideoChunk + - api.EncodedVideoChunk.EncodedVideoChunk + - api.EncodedVideoChunk.byteLength + - api.EncodedVideoChunk.copyTo + - api.EncodedVideoChunk.duration + - api.EncodedVideoChunk.timestamp + - api.EncodedVideoChunk.type + - api.ImageDecoder + - api.ImageDecoder.ImageDecoder + - api.ImageDecoder.close + - api.ImageDecoder.complete + - api.ImageDecoder.completed + - api.ImageDecoder.decode + - api.ImageDecoder.isTypeSupported_static + - api.ImageDecoder.reset + - api.ImageDecoder.tracks + - api.ImageDecoder.type + - api.ImageTrack + - api.ImageTrack.animated + - api.ImageTrack.frameCount + - api.ImageTrack.repetitionCount + - api.ImageTrack.selected + - api.ImageTrackList + - api.ImageTrackList.length + - api.ImageTrackList.ready + - api.ImageTrackList.selectedIndex + - api.ImageTrackList.selectedTrack + - api.VideoColorSpace + - api.VideoColorSpace.VideoColorSpace + - api.VideoColorSpace.fullRange + - api.VideoColorSpace.matrix + - api.VideoColorSpace.primaries + - api.VideoColorSpace.toJSON + - api.VideoColorSpace.transfer + - api.VideoDecoder + - api.VideoDecoder.VideoDecoder + - api.VideoDecoder.close + - api.VideoDecoder.configure + - api.VideoDecoder.decode + - api.VideoDecoder.decodeQueueSize + - api.VideoDecoder.dequeue_event + - api.VideoDecoder.flush + - api.VideoDecoder.isConfigSupported_static + - api.VideoDecoder.reset + - api.VideoDecoder.state + - api.VideoEncoder + - api.VideoEncoder.VideoEncoder + - api.VideoEncoder.close + - api.VideoEncoder.configure + - api.VideoEncoder.dequeue_event + - api.VideoEncoder.encode + - api.VideoEncoder.encodeQueueSize + - api.VideoEncoder.flush + - api.VideoEncoder.isConfigSupported_static + - api.VideoEncoder.reset + - api.VideoEncoder.state + - api.VideoFrame + - api.VideoFrame.VideoFrame + - api.VideoFrame.allocationSize + - api.VideoFrame.clone + - api.VideoFrame.close + - api.VideoFrame.codedHeight + - api.VideoFrame.codedRect + - api.VideoFrame.codedWidth + - api.VideoFrame.colorSpace + - api.VideoFrame.copyTo + - api.VideoFrame.displayHeight + - api.VideoFrame.displayWidth + - api.VideoFrame.duration + - api.VideoFrame.format + - api.VideoFrame.timestamp + - api.VideoFrame.visibleRect diff --git a/features/video-codecs.yml.dist b/features/video-codecs.yml.dist new file mode 100644 index 00000000000..dce7bf2b318 --- /dev/null +++ b/features/video-codecs.yml.dist @@ -0,0 +1,144 @@ +# Generated from: video-codecs.yml +# Do not edit this file by hand. Edit the source file instead! + +status: + baseline: false + support: + chrome: "94" + chrome_android: "94" + edge: "94" + firefox: "130" + safari: "16.4" + safari_ios: "16.4" +compat_features: + # baseline: low + # baseline_low_date: 2024-09-03 + # support: + # chrome: "94" + # chrome_android: "94" + # edge: "94" + # firefox: "130" + # firefox_android: "130" + # safari: "15.4" + # safari_ios: "15.4" + - api.VideoColorSpace + - api.VideoColorSpace.fullRange + - api.VideoColorSpace.matrix + - api.VideoColorSpace.primaries + - api.VideoColorSpace.toJSON + - api.VideoColorSpace.transfer + + # baseline: low + # baseline_low_date: 2024-09-03 + # support: + # chrome: "94" + # chrome_android: "94" + # edge: "94" + # firefox: "130" + # firefox_android: "130" + # safari: "16.4" + # safari_ios: "16.4" + - api.VideoFrame + - api.VideoFrame.VideoFrame + - api.VideoFrame.allocationSize + - api.VideoFrame.clone + - api.VideoFrame.close + - api.VideoFrame.codedHeight + - api.VideoFrame.codedRect + - api.VideoFrame.codedWidth + - api.VideoFrame.colorSpace + - api.VideoFrame.copyTo + - api.VideoFrame.displayHeight + - api.VideoFrame.displayWidth + - api.VideoFrame.duration + - api.VideoFrame.format + - api.VideoFrame.timestamp + - api.VideoFrame.visibleRect + + # baseline: low + # baseline_low_date: 2024-09-03 + # support: + # chrome: "94" + # chrome_android: "94" + # edge: "94" + # firefox: "130" + # firefox_android: "130" + # safari: "17" + # safari_ios: "17" + - api.VideoColorSpace.VideoColorSpace + + # ⬇️ Same status as overall feature ⬇️ + # baseline: false + # support: + # chrome: "94" + # chrome_android: "94" + # edge: "94" + # firefox: "130" + # safari: "16.4" + # safari_ios: "16.4" + - api.EncodedVideoChunk + - api.EncodedVideoChunk.EncodedVideoChunk + - api.EncodedVideoChunk.byteLength + - api.EncodedVideoChunk.copyTo + - api.EncodedVideoChunk.duration + - api.EncodedVideoChunk.timestamp + - api.EncodedVideoChunk.type + - api.VideoDecoder + - api.VideoDecoder.VideoDecoder + - api.VideoDecoder.close + - api.VideoDecoder.configure + - api.VideoDecoder.decode + - api.VideoDecoder.decodeQueueSize + - api.VideoDecoder.flush + - api.VideoDecoder.isConfigSupported_static + - api.VideoDecoder.reset + - api.VideoDecoder.state + - api.VideoEncoder + - api.VideoEncoder.VideoEncoder + - api.VideoEncoder.close + - api.VideoEncoder.configure + - api.VideoEncoder.encode + - api.VideoEncoder.encodeQueueSize + - api.VideoEncoder.flush + - api.VideoEncoder.isConfigSupported_static + - api.VideoEncoder.reset + - api.VideoEncoder.state + + # baseline: false + # support: + # chrome: "106" + # chrome_android: "106" + # edge: "106" + # firefox: "130" + # safari: "16.4" + # safari_ios: "16.4" + - api.VideoDecoder.dequeue_event + - api.VideoEncoder.dequeue_event + + # baseline: false + # support: + # chrome: "94" + # chrome_android: "94" + # edge: "94" + # firefox: "133" + # firefox_android: "133" + - api.ImageDecoder + - api.ImageDecoder.ImageDecoder + - api.ImageDecoder.close + - api.ImageDecoder.complete + - api.ImageDecoder.completed + - api.ImageDecoder.decode + - api.ImageDecoder.isTypeSupported_static + - api.ImageDecoder.reset + - api.ImageDecoder.tracks + - api.ImageDecoder.type + - api.ImageTrack + - api.ImageTrack.animated + - api.ImageTrack.frameCount + - api.ImageTrack.repetitionCount + - api.ImageTrack.selected + - api.ImageTrackList + - api.ImageTrackList.length + - api.ImageTrackList.ready + - api.ImageTrackList.selectedIndex + - api.ImageTrackList.selectedTrack diff --git a/features/webcodecs.yml b/features/webcodecs.yml index b28d885ea96..16cf158faf3 100644 --- a/features/webcodecs.yml +++ b/features/webcodecs.yml @@ -1,10 +1,5 @@ -name: WebCodecs -description: The WebCodecs API provides low-level access to individual video frames and chunks of audio samples, for full control over the way media is processed. -spec: https://w3c.github.io/webcodecs/ -caniuse: webcodecs -status: - compute_from: - - api.AudioDecoder.AudioDecoder - - api.AudioEncoder.AudioEncoder - - api.VideoDecoder.VideoDecoder - - api.VideoEncoder.VideoEncoder +redirect: + reason: split + targets: + - audio-codecs + - video-codecs diff --git a/features/webcodecs.yml.dist b/features/webcodecs.yml.dist index 32349ab36f7..948bdcbda98 100644 --- a/features/webcodecs.yml.dist +++ b/features/webcodecs.yml.dist @@ -1,221 +1,8 @@ # Generated from: webcodecs.yml -# Do not edit this file by hand. Edit the source file instead! +# This file intentionally left blank. +# Do not edit this file. +# The data for this feature has moved to: +# - audio-codecs.yml +# - video-codecs.yml -status: - baseline: false - support: - chrome: "94" - chrome_android: "94" - edge: "94" - firefox: "130" -compat_features: - # baseline: low - # baseline_low_date: 2024-09-03 - # support: - # chrome: "94" - # chrome_android: "94" - # edge: "94" - # firefox: "130" - # firefox_android: "130" - # safari: "15.4" - # safari_ios: "15.4" - - api.VideoColorSpace - - api.VideoColorSpace.fullRange - - api.VideoColorSpace.matrix - - api.VideoColorSpace.primaries - - api.VideoColorSpace.toJSON - - api.VideoColorSpace.transfer - - # baseline: low - # baseline_low_date: 2024-09-03 - # support: - # chrome: "94" - # chrome_android: "94" - # edge: "94" - # firefox: "130" - # firefox_android: "130" - # safari: "16.4" - # safari_ios: "16.4" - - api.VideoFrame - - api.VideoFrame.VideoFrame - - api.VideoFrame.allocationSize - - api.VideoFrame.clone - - api.VideoFrame.close - - api.VideoFrame.codedHeight - - api.VideoFrame.codedRect - - api.VideoFrame.codedWidth - - api.VideoFrame.colorSpace - - api.VideoFrame.copyTo - - api.VideoFrame.displayHeight - - api.VideoFrame.displayWidth - - api.VideoFrame.duration - - api.VideoFrame.format - - api.VideoFrame.timestamp - - api.VideoFrame.visibleRect - - # baseline: low - # baseline_low_date: 2024-09-03 - # support: - # chrome: "94" - # chrome_android: "94" - # edge: "94" - # firefox: "130" - # firefox_android: "130" - # safari: "17" - # safari_ios: "17" - - api.VideoColorSpace.VideoColorSpace - - # baseline: false - # support: - # chrome: "94" - # chrome_android: "94" - # edge: "94" - # firefox: "130" - # safari: "16.4" - # safari_ios: "16.4" - - api.EncodedVideoChunk - - api.EncodedVideoChunk.EncodedVideoChunk - - api.EncodedVideoChunk.byteLength - - api.EncodedVideoChunk.copyTo - - api.EncodedVideoChunk.duration - - api.EncodedVideoChunk.timestamp - - api.EncodedVideoChunk.type - - api.VideoDecoder - - api.VideoDecoder.VideoDecoder - - api.VideoDecoder.close - - api.VideoDecoder.configure - - api.VideoDecoder.decode - - api.VideoDecoder.decodeQueueSize - - api.VideoDecoder.flush - - api.VideoDecoder.isConfigSupported_static - - api.VideoDecoder.reset - - api.VideoDecoder.state - - api.VideoEncoder - - api.VideoEncoder.VideoEncoder - - api.VideoEncoder.close - - api.VideoEncoder.configure - - api.VideoEncoder.encode - - api.VideoEncoder.encodeQueueSize - - api.VideoEncoder.flush - - api.VideoEncoder.isConfigSupported_static - - api.VideoEncoder.reset - - api.VideoEncoder.state - - # baseline: false - # support: - # chrome: "106" - # chrome_android: "106" - # edge: "106" - # firefox: "130" - # safari: "16.4" - # safari_ios: "16.4" - - api.VideoDecoder.dequeue_event - - api.VideoEncoder.dequeue_event - - # baseline: false - # support: - # chrome: "94" - # chrome_android: "94" - # edge: "94" - # firefox: "133" - # firefox_android: "133" - - api.ImageDecoder - - api.ImageDecoder.ImageDecoder - - api.ImageDecoder.close - - api.ImageDecoder.complete - - api.ImageDecoder.completed - - api.ImageDecoder.decode - - api.ImageDecoder.isTypeSupported_static - - api.ImageDecoder.reset - - api.ImageDecoder.tracks - - api.ImageDecoder.type - - api.ImageTrack - - api.ImageTrack.animated - - api.ImageTrack.frameCount - - api.ImageTrack.repetitionCount - - api.ImageTrack.selected - - api.ImageTrackList - - api.ImageTrackList.length - - api.ImageTrackList.ready - - api.ImageTrackList.selectedIndex - - api.ImageTrackList.selectedTrack - - # ⬇️ Same status as overall feature ⬇️ - # baseline: false - # support: - # chrome: "94" - # chrome_android: "94" - # edge: "94" - # firefox: "130" - - api.AudioData - - api.AudioData.AudioData - - api.AudioData.allocationSize - - api.AudioData.clone - - api.AudioData.close - - api.AudioData.copyTo - - api.AudioData.duration - - api.AudioData.format - - api.AudioData.numberOfChannels - - api.AudioData.numberOfFrames - - api.AudioData.sampleRate - - api.AudioData.timestamp - - api.AudioDecoder - - api.AudioDecoder.AudioDecoder - - api.AudioDecoder.close - - api.AudioDecoder.configure - - api.AudioDecoder.decode - - api.AudioDecoder.decodeQueueSize - - api.AudioDecoder.flush - - api.AudioDecoder.isConfigSupported_static - - api.AudioDecoder.reset - - api.AudioDecoder.state - - api.AudioEncoder - - api.AudioEncoder.AudioEncoder - - api.AudioEncoder.close - - api.AudioEncoder.configure - - api.AudioEncoder.encode - - api.AudioEncoder.encodeQueueSize - - api.AudioEncoder.flush - - api.AudioEncoder.isConfigSupported_static - - api.AudioEncoder.reset - - api.AudioEncoder.state - - api.EncodedAudioChunk - - api.EncodedAudioChunk.EncodedAudioChunk - - api.EncodedAudioChunk.byteLength - - api.EncodedAudioChunk.copyTo - - api.EncodedAudioChunk.duration - - api.EncodedAudioChunk.timestamp - - api.EncodedAudioChunk.type - - # baseline: false - # support: - # chrome: "106" - # chrome_android: "106" - # edge: "106" - # firefox: "130" - - api.AudioDecoder.dequeue_event - - api.AudioEncoder.dequeue_event - - # baseline: false - # support: - # chrome: "110" - # chrome_android: "110" - # edge: "110" - # firefox: "130" - - api.AudioEncoder.configure.opus - - # baseline: false - # support: - # chrome: "119" - # chrome_android: "119" - # edge: "119" - # firefox: "130" - - api.AudioEncoder.configure.bitrateMode - - # baseline: false - # support: - # chrome: "126" - # chrome_android: "126" - # edge: "126" - - api.AudioEncoder.configure.opus.opus_application - - api.AudioEncoder.configure.opus.opus_signal +{} From eaf208dc3075839d6b50bf458c57324e5ea9f92b Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Tue, 27 May 2025 18:12:37 +0200 Subject: [PATCH 09/21] Rename `numeric-seperators` --- features/numeric-separators.yml | 6 ++++++ features/numeric-separators.yml.dist | 17 +++++++++++++++++ features/numeric-seperators.yml | 9 +++------ features/numeric-seperators.yml.dist | 19 ++++--------------- 4 files changed, 30 insertions(+), 21 deletions(-) create mode 100644 features/numeric-separators.yml create mode 100644 features/numeric-separators.yml.dist diff --git a/features/numeric-separators.yml b/features/numeric-separators.yml new file mode 100644 index 00000000000..85500442056 --- /dev/null +++ b/features/numeric-separators.yml @@ -0,0 +1,6 @@ +name: Numeric separators +description: To improve readability for numeric literals, underscores (`_`) can be used as separators. For example, `1_050.95` is equivalent to `1050.95`. +spec: https://tc39.es/ecma262/multipage/ecmascript-language-lexical-grammar.html#prod-NumericLiteralSeparator +group: javascript +compat_features: + - javascript.grammar.numeric_separators diff --git a/features/numeric-separators.yml.dist b/features/numeric-separators.yml.dist new file mode 100644 index 00000000000..996e1916346 --- /dev/null +++ b/features/numeric-separators.yml.dist @@ -0,0 +1,17 @@ +# Generated from: numeric-separators.yml +# Do not edit this file by hand. Edit the source file instead! + +status: + baseline: high + baseline_low_date: 2020-07-28 + baseline_high_date: 2023-01-28 + support: + chrome: "75" + chrome_android: "75" + edge: "79" + firefox: "70" + firefox_android: "79" + safari: "13" + safari_ios: "13" +compat_features: + - javascript.grammar.numeric_separators diff --git a/features/numeric-seperators.yml b/features/numeric-seperators.yml index 85500442056..d29ebacf327 100644 --- a/features/numeric-seperators.yml +++ b/features/numeric-seperators.yml @@ -1,6 +1,3 @@ -name: Numeric separators -description: To improve readability for numeric literals, underscores (`_`) can be used as separators. For example, `1_050.95` is equivalent to `1050.95`. -spec: https://tc39.es/ecma262/multipage/ecmascript-language-lexical-grammar.html#prod-NumericLiteralSeparator -group: javascript -compat_features: - - javascript.grammar.numeric_separators +redirect: + reason: moved + target: numeric-separators diff --git a/features/numeric-seperators.yml.dist b/features/numeric-seperators.yml.dist index eaa3c384275..9bda2b05ad1 100644 --- a/features/numeric-seperators.yml.dist +++ b/features/numeric-seperators.yml.dist @@ -1,17 +1,6 @@ # Generated from: numeric-seperators.yml -# Do not edit this file by hand. Edit the source file instead! +# This file intentionally left blank. +# Do not edit this file. +# The data for this feature has moved to numeric-separators.yml -status: - baseline: high - baseline_low_date: 2020-07-28 - baseline_high_date: 2023-01-28 - support: - chrome: "75" - chrome_android: "75" - edge: "79" - firefox: "70" - firefox_android: "79" - safari: "13" - safari_ios: "13" -compat_features: - - javascript.grammar.numeric_separators +{} From dc4974bf762fc375dce91a559c4ee8a5bfd233b1 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Tue, 27 May 2025 18:46:49 +0200 Subject: [PATCH 10/21] Prevent double and circular redirects --- index.ts | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/index.ts b/index.ts index 42ca1755f92..00b064edaef 100644 --- a/index.ts +++ b/index.ts @@ -184,12 +184,21 @@ for (const [key, data] of yamlEntries('features')) { } // Assert that feature references are valid + +function assertValidReference(sourceID: string, targetID: string): void { + if (targetID in features) { + if (isRedirectData(features[targetID])) { + throw new Error(`${sourceID} references a redirect "${targetID}" instead of an ordinary feature ID`); + } + return; + } + throw new Error(`${sourceID}'s reference to "${targetID}" is not a valid feature ID`); +} + for (const [id, feature] of Object.entries(features)) { if (isOrdinaryFeatureData(feature)) { for (const alternative of feature.discouraged?.alternatives ?? []) { - if (!(alternative in features)) { - throw new Error(`${id}'s alternative "${alternative}" is not a valid feature ID`); - } + assertValidReference(id, alternative); } } @@ -197,15 +206,11 @@ for (const [id, feature] of Object.entries(features)) { const { reason } = feature.redirect; switch (reason) { case 'moved': - if (!(feature.redirect.target in features)) { - throw new Error(`${id}'s redirect target "${feature.redirect.target} is not a valid feature ID`); - } + assertValidReference(id, feature.redirect.target); break; case 'split': for (const target of feature.redirect.targets) { - if (!(target in features)) { - throw new Error(`${id}'s redirect target "${target}" is not a valid feature ID`); - } + assertValidReference(id, target); } break; default: From b2b67621e0eee3c33e01f934095bb4afe05fd085 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Tue, 10 Jun 2025 15:48:07 +0200 Subject: [PATCH 11/21] Add unintentionally missing required fields Co-authored-by: James C Scott III <7788930+jcscottiii@users.noreply.github.com> --- schemas/data.schema.json | 8 +++++--- types.quicktype.ts | 14 +++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/schemas/data.schema.json b/schemas/data.schema.json index e2305ad2d62..f4b38984c4d 100644 --- a/schemas/data.schema.json +++ b/schemas/data.schema.json @@ -143,7 +143,7 @@ "$ref": "#/definitions/Discouraged" } }, - "required": ["name", "description", "description_html", "spec"] + "required": ["name", "description", "description_html", "spec", "status"] }, "GroupData": { "type": "object", @@ -222,7 +222,8 @@ } } } - ] + ], + "required": ["baseline", "support"] }, "Release": { "description": "Browser release information", @@ -251,7 +252,8 @@ "description": "Specification", "$ref": "#/definitions/URL" } - } + }, + "required": ["name", "spec"] }, "StringOrStrings": { "oneOf": [ diff --git a/types.quicktype.ts b/types.quicktype.ts index 8a0828f425f..7fb49c7882b 100644 --- a/types.quicktype.ts +++ b/types.quicktype.ts @@ -102,7 +102,7 @@ export interface FeatureData { * Whether a feature is considered a "Baseline" web platform feature and when it achieved * that status */ - status?: StatusHeadline; + status: StatusHeadline; [property: string]: any; } @@ -130,6 +130,10 @@ export interface StatusHeadline { * Whether the feature is Baseline (low substatus), Baseline (high substatus), or not (false) */ baseline: boolean | BaselineEnum; + /** + * Browser versions that most-recently introduced the feature + */ + support: Support; /** * Date the feature achieved Baseline high status */ @@ -138,10 +142,6 @@ export interface StatusHeadline { * Date the feature achieved Baseline low status */ baseline_low_date?: string; - /** - * Browser versions that most-recently introduced the feature - */ - support: Support; /** * Statuses for each key in the feature's compat_features list, if applicable. Not available * to the npm release of web-features. @@ -200,10 +200,10 @@ export interface SnapshotData { /** * Short name */ - name?: string; + name: string; /** * Specification */ - spec?: string; + spec: string; [property: string]: any; } From dd23730dea48021ebd4ed05f90d9a1505efa0946 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Tue, 10 Jun 2025 16:19:06 +0200 Subject: [PATCH 12/21] Add more property constraints to reduce any in TS --- schemas/data.schema.json | 32 +++++++++++++++++--------------- types.quicktype.ts | 11 ++++------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/schemas/data.schema.json b/schemas/data.schema.json index f4b38984c4d..7611cf4192d 100644 --- a/schemas/data.schema.json +++ b/schemas/data.schema.json @@ -143,7 +143,8 @@ "$ref": "#/definitions/Discouraged" } }, - "required": ["name", "description", "description_html", "spec", "status"] + "required": ["name", "description", "description_html", "spec", "status"], + "additionalProperties": false }, "GroupData": { "type": "object", @@ -208,22 +209,22 @@ }, "StatusHeadline": { "type": "object", - "allOf": [ - { "$ref": "#/definitions/Status" }, - { + "allOf": [{ "$ref": "#/definitions/Status" }], + "properties": { + "baseline": {}, + "baseline_high_date": {}, + "baseline_low_date": {}, + "support": {}, + "by_compat_key": { + "description": "Statuses for each key in the feature's compat_features list, if applicable. Not available to the npm release of web-features.", "type": "object", - "properties": { - "by_compat_key": { - "description": "Statuses for each key in the feature's compat_features list, if applicable. Not available to the npm release of web-features.", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Status" - } - } + "additionalProperties": { + "$ref": "#/definitions/Status" } } - ], - "required": ["baseline", "support"] + }, + "required": ["baseline", "support"], + "additionalProperties": false }, "Release": { "description": "Browser release information", @@ -253,7 +254,8 @@ "$ref": "#/definitions/URL" } }, - "required": ["name", "spec"] + "required": ["name", "spec"], + "additionalProperties": false }, "StringOrStrings": { "oneOf": [ diff --git a/types.quicktype.ts b/types.quicktype.ts index 7fb49c7882b..ed29d838117 100644 --- a/types.quicktype.ts +++ b/types.quicktype.ts @@ -103,7 +103,6 @@ export interface FeatureData { * that status */ status: StatusHeadline; - [property: string]: any; } /** @@ -130,10 +129,6 @@ export interface StatusHeadline { * Whether the feature is Baseline (low substatus), Baseline (high substatus), or not (false) */ baseline: boolean | BaselineEnum; - /** - * Browser versions that most-recently introduced the feature - */ - support: Support; /** * Date the feature achieved Baseline high status */ @@ -147,7 +142,10 @@ export interface StatusHeadline { * to the npm release of web-features. */ by_compat_key?: { [key: string]: Status }; - [property: string]: any; + /** + * Browser versions that most-recently introduced the feature + */ + support: Support; } export type BaselineEnum = "high" | "low"; @@ -205,5 +203,4 @@ export interface SnapshotData { * Specification */ spec: string; - [property: string]: any; } From 6e383fff9f5555317054ec3555063a449ffda117 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Mon, 30 Jun 2025 19:26:39 +0200 Subject: [PATCH 13/21] Drop tsup and export our types directly --- packages/web-features/package-lock.json | 1852 +---------------------- packages/web-features/package.json | 5 +- packages/web-features/tsconfig.json | 3 +- 3 files changed, 10 insertions(+), 1850 deletions(-) diff --git a/packages/web-features/package-lock.json b/packages/web-features/package-lock.json index f4cf2440b7b..a5dd61135f2 100644 --- a/packages/web-features/package-lock.json +++ b/packages/web-features/package-lock.json @@ -10,1728 +10,16 @@ "license": "Apache-2.0", "devDependencies": { "@types/node": "^24.0.7", - "tsup": "^8.5.0", "typescript": "^5.8.3" } }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", - "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", - "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", - "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", - "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", - "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", - "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", - "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", - "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", - "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", - "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", - "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", - "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", - "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", - "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", - "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", - "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", - "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", - "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", - "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", - "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", - "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", - "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", - "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", - "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", - "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz", - "integrity": "sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.8.tgz", - "integrity": "sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.8.tgz", - "integrity": "sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.8.tgz", - "integrity": "sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.8.tgz", - "integrity": "sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.8.tgz", - "integrity": "sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.8.tgz", - "integrity": "sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.8.tgz", - "integrity": "sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.8.tgz", - "integrity": "sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.8.tgz", - "integrity": "sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.8.tgz", - "integrity": "sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.8.tgz", - "integrity": "sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.8.tgz", - "integrity": "sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.8.tgz", - "integrity": "sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.8.tgz", - "integrity": "sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.8.tgz", - "integrity": "sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.8.tgz", - "integrity": "sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.8.tgz", - "integrity": "sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.8.tgz", - "integrity": "sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.8.tgz", - "integrity": "sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "24.0.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.7.tgz", - "integrity": "sha512-YIEUUr4yf8q8oQoXPpSlnvKNVKDQlPMWrmOcgzoduo7kvA2UF0/BwJ/eMKFTiTtkNL17I0M6Xe2tvwFU7be6iw==", - "dev": true, - "dependencies": { - "undici-types": "~7.8.0" - } - }, - "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/bundle-require": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/bundle-require/-/bundle-require-5.1.0.tgz", - "integrity": "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "load-tsconfig": "^0.2.3" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "peerDependencies": { - "esbuild": ">=0.18" - } - }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/confbox": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", - "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/consola": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.0.tgz", - "integrity": "sha512-EiPU8G6dQG0GFHNR8ljnZFki/8a+cQwEQ+7wpxdChl02Q8HXlwEZWD5lqAF8vC2sEC3Tehr8hy7vErz88LHyUA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/esbuild": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", - "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.0", - "@esbuild/android-arm": "0.25.0", - "@esbuild/android-arm64": "0.25.0", - "@esbuild/android-x64": "0.25.0", - "@esbuild/darwin-arm64": "0.25.0", - "@esbuild/darwin-x64": "0.25.0", - "@esbuild/freebsd-arm64": "0.25.0", - "@esbuild/freebsd-x64": "0.25.0", - "@esbuild/linux-arm": "0.25.0", - "@esbuild/linux-arm64": "0.25.0", - "@esbuild/linux-ia32": "0.25.0", - "@esbuild/linux-loong64": "0.25.0", - "@esbuild/linux-mips64el": "0.25.0", - "@esbuild/linux-ppc64": "0.25.0", - "@esbuild/linux-riscv64": "0.25.0", - "@esbuild/linux-s390x": "0.25.0", - "@esbuild/linux-x64": "0.25.0", - "@esbuild/netbsd-arm64": "0.25.0", - "@esbuild/netbsd-x64": "0.25.0", - "@esbuild/openbsd-arm64": "0.25.0", - "@esbuild/openbsd-x64": "0.25.0", - "@esbuild/sunos-x64": "0.25.0", - "@esbuild/win32-arm64": "0.25.0", - "@esbuild/win32-ia32": "0.25.0", - "@esbuild/win32-x64": "0.25.0" - } - }, - "node_modules/fdir": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz", - "integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fix-dts-default-cjs-exports": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fix-dts-default-cjs-exports/-/fix-dts-default-cjs-exports-1.0.1.tgz", - "integrity": "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "magic-string": "^0.30.17", - "mlly": "^1.7.4", - "rollup": "^4.34.8" - } - }, - "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/glob": { - "version": "10.3.10", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", - "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", - "dev": true, - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^2.3.5", - "minimatch": "^9.0.1", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", - "path-scurry": "^1.10.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/joycon": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", - "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/load-tsconfig": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/load-tsconfig/-/load-tsconfig-0.2.5.tgz", - "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", - "dev": true - }, - "node_modules/lru-cache": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", - "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", - "dev": true, - "engines": { - "node": "14 || >=16.14" - } - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mlly": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz", - "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.14.0", - "pathe": "^2.0.1", - "pkg-types": "^1.3.0", - "ufo": "^1.5.4" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-scurry": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", - "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", - "dev": true, - "dependencies": { - "lru-cache": "^9.1.1 || ^10.0.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true - }, - "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-types": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz", - "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "confbox": "^0.1.8", - "mlly": "^1.7.4", - "pathe": "^2.0.1" - } - }, - "node_modules/postcss-load-config": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", - "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "lilconfig": "^3.1.1" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "jiti": ">=1.21.0", - "postcss": ">=8.0.9", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - }, - "postcss": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/rollup": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.8.tgz", - "integrity": "sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.6" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.34.8", - "@rollup/rollup-android-arm64": "4.34.8", - "@rollup/rollup-darwin-arm64": "4.34.8", - "@rollup/rollup-darwin-x64": "4.34.8", - "@rollup/rollup-freebsd-arm64": "4.34.8", - "@rollup/rollup-freebsd-x64": "4.34.8", - "@rollup/rollup-linux-arm-gnueabihf": "4.34.8", - "@rollup/rollup-linux-arm-musleabihf": "4.34.8", - "@rollup/rollup-linux-arm64-gnu": "4.34.8", - "@rollup/rollup-linux-arm64-musl": "4.34.8", - "@rollup/rollup-linux-loongarch64-gnu": "4.34.8", - "@rollup/rollup-linux-powerpc64le-gnu": "4.34.8", - "@rollup/rollup-linux-riscv64-gnu": "4.34.8", - "@rollup/rollup-linux-s390x-gnu": "4.34.8", - "@rollup/rollup-linux-x64-gnu": "4.34.8", - "@rollup/rollup-linux-x64-musl": "4.34.8", - "@rollup/rollup-win32-arm64-msvc": "4.34.8", - "@rollup/rollup-win32-ia32-msvc": "4.34.8", - "@rollup/rollup-win32-x64-msvc": "4.34.8", - "fsevents": "~2.3.2" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.8.0-beta.0", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", - "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", - "dev": true, - "dependencies": { - "whatwg-url": "^7.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.12", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz", - "integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.4.3", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tr46": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", - "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, - "bin": { - "tree-kill": "cli.js" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true - }, - "node_modules/tsup": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.5.0.tgz", - "integrity": "sha512-VmBp77lWNQq6PfuMqCHD3xWl22vEoWsKajkF8t+yMBawlUS8JzEI+vOVMeuNZIuMML8qXRizFKi9oD5glKQVcQ==", + "node_modules/@types/node": { + "version": "24.0.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.7.tgz", + "integrity": "sha512-YIEUUr4yf8q8oQoXPpSlnvKNVKDQlPMWrmOcgzoduo7kvA2UF0/BwJ/eMKFTiTtkNL17I0M6Xe2tvwFU7be6iw==", "dev": true, - "license": "MIT", "dependencies": { - "bundle-require": "^5.1.0", - "cac": "^6.7.14", - "chokidar": "^4.0.3", - "consola": "^3.4.0", - "debug": "^4.4.0", - "esbuild": "^0.25.0", - "fix-dts-default-cjs-exports": "^1.0.0", - "joycon": "^3.1.1", - "picocolors": "^1.1.1", - "postcss-load-config": "^6.0.1", - "resolve-from": "^5.0.0", - "rollup": "^4.34.8", - "source-map": "0.8.0-beta.0", - "sucrase": "^3.35.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.11", - "tree-kill": "^1.2.2" - }, - "bin": { - "tsup": "dist/cli-default.js", - "tsup-node": "dist/cli-node.js" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "@microsoft/api-extractor": "^7.36.0", - "@swc/core": "^1", - "postcss": "^8.4.12", - "typescript": ">=4.5.0" - }, - "peerDependenciesMeta": { - "@microsoft/api-extractor": { - "optional": true - }, - "@swc/core": { - "optional": true - }, - "postcss": { - "optional": true - }, - "typescript": { - "optional": true - } + "undici-types": "~7.8.0" } }, "node_modules/typescript": { @@ -1748,142 +36,12 @@ "node": ">=14.17" } }, - "node_modules/ufo": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", - "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", - "dev": true, - "license": "MIT" - }, "node_modules/undici-types": { "version": "7.8.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", "dev": true, "license": "MIT" - }, - "node_modules/webidl-conversions": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true - }, - "node_modules/whatwg-url": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", - "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", - "dev": true, - "dependencies": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } } } } diff --git a/packages/web-features/package.json b/packages/web-features/package.json index e4363935343..60ecb77e106 100644 --- a/packages/web-features/package.json +++ b/packages/web-features/package.json @@ -18,16 +18,17 @@ "types": "./index.d.ts", "files": [ "index.d.ts", + "types.d.ts", + "types.quicktype.d.ts", "index.js", "data.json", "data.schema.json" ], "scripts": { - "prepare": "tsc && rm types.js && tsup ./index.ts --dts-only --format=esm --out-dir=." + "prepare": "tsc && rm types.js && rm types.quicktype.js" }, "devDependencies": { "@types/node": "^24.0.7", - "tsup": "^8.5.0", "typescript": "^5.8.3" } } diff --git a/packages/web-features/tsconfig.json b/packages/web-features/tsconfig.json index 216634c4164..e483fa95943 100644 --- a/packages/web-features/tsconfig.json +++ b/packages/web-features/tsconfig.json @@ -3,6 +3,7 @@ "target": "ES2016", "module": "ESNext", "moduleResolution": "Bundler", - "typeRoots": ["./node_modules/@types"] + "typeRoots": ["./node_modules/@types"], + "declaration": true } } From 0d54e0676ac87a1161e5068399dd8760d4fdbfca Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Wed, 16 Jul 2025 20:00:09 +0200 Subject: [PATCH 14/21] Fix bad Quicktype command parsing --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 32fd17d84d5..e0052bd9ee4 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "undist": "tsx scripts/undist.ts", "feature-init": "tsx scripts/feature-init.ts", "format": "npx prettier --write .", - "schematypes": "sh -c \"npx quicktype --lang='typescript' --src-lang schema --src ./schemas/data.schema.json --just-types='yes' --prefer-unions='yes' --prefer-const-values='yes' --top-level=WebFeaturesData | prettier --stdin-filepath ./types.quicktype.ts\"", + "schematypes": "sh -c \"npx quicktype --lang='typescript' --src-lang schema --src ./schemas/data.schema.json --just-types --prefer-unions --prefer-const-values --top-level=WebFeaturesData | prettier --stdin-filepath ./types.quicktype.ts\"", "schematypes:write": "npm run --silent schematypes > ./types.quicktype.ts", "test:caniuse": "tsx scripts/caniuse.ts", "test:coverage": "npm run --workspaces test:coverage", From 7f7c96f20de06a90207f0727b3ed41d5eefcb2f0 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Tue, 22 Jul 2025 17:16:39 +0200 Subject: [PATCH 15/21] Provide discriminator property on feature data --- features/numeric-separators.yml.dist | 1 + features/numeric-seperators.yml | 5 +-- features/webcodecs.yml | 9 ++--- index.ts | 43 ++++++++++----------- schemas/data.schema.json | 57 +++++++++++++--------------- scripts/build.ts | 3 +- scripts/dist.ts | 25 ++++++------ scripts/specs.ts | 4 +- type-guards.ts | 12 ++++-- types.quicktype.ts | 24 +++++------- types.ts | 43 +++++++++++---------- 11 files changed, 112 insertions(+), 114 deletions(-) diff --git a/features/numeric-separators.yml.dist b/features/numeric-separators.yml.dist index 996e1916346..6f529f71b66 100644 --- a/features/numeric-separators.yml.dist +++ b/features/numeric-separators.yml.dist @@ -1,6 +1,7 @@ # Generated from: numeric-separators.yml # Do not edit this file by hand. Edit the source file instead! +kind: feature status: baseline: high baseline_low_date: 2020-07-28 diff --git a/features/numeric-seperators.yml b/features/numeric-seperators.yml index d29ebacf327..04c6806c7fd 100644 --- a/features/numeric-seperators.yml +++ b/features/numeric-seperators.yml @@ -1,3 +1,2 @@ -redirect: - reason: moved - target: numeric-separators +kind: moved +redirect_target: numeric-separators diff --git a/features/webcodecs.yml b/features/webcodecs.yml index 16cf158faf3..7da83637dbd 100644 --- a/features/webcodecs.yml +++ b/features/webcodecs.yml @@ -1,5 +1,4 @@ -redirect: - reason: split - targets: - - audio-codecs - - video-codecs +kind: split +redirect_targets: + - audio-codecs + - video-codecs diff --git a/index.ts b/index.ts index a5e138af8db..527d51622c3 100644 --- a/index.ts +++ b/index.ts @@ -14,7 +14,7 @@ import { unified } from 'unified'; import { BASELINE_LOW_TO_HIGH_DURATION, coreBrowserSet, parseRangedDateString } from 'compute-baseline'; import { Compat } from 'compute-baseline/browser-compat-data'; -import { isOrdinaryFeatureData, isRedirectData } from './type-guards'; +import { isMoved, isSplit } from './type-guards'; // The longest name allowed, to allow for compact display. const nameMaxLength = 80; @@ -188,7 +188,7 @@ for (const [key, data] of yamlEntries('features')) { function assertValidReference(sourceID: string, targetID: string): void { if (targetID in features) { - if (isRedirectData(features[targetID])) { + if (isMoved(features[targetID]) || isSplit(features[targetID])) { throw new Error(`${sourceID} references a redirect "${targetID}" instead of an ordinary feature ID`); } return; @@ -197,27 +197,24 @@ function assertValidReference(sourceID: string, targetID: string): void { } for (const [id, feature] of Object.entries(features)) { - if (isOrdinaryFeatureData(feature)) { - for (const alternative of feature.discouraged?.alternatives ?? []) { - assertValidReference(id, alternative); - } - } - - if (isRedirectData(feature)) { - const { reason } = feature.redirect; - switch (reason) { - case 'moved': - assertValidReference(id, feature.redirect.target); - break; - case 'split': - for (const target of feature.redirect.targets) { - assertValidReference(id, target); - } - break; - default: - reason satisfies never; - throw new Error(`Unhandled redirect reason ${reason}}`); - } + const { kind } = feature; + switch (kind) { + case "feature": + for (const alternative of feature.discouraged?.alternatives ?? []) { + assertValidReference(id, alternative); + } + break; + case "moved": + assertValidReference(id, feature.redirect_target); + break; + case "split": + for (const target of feature.redirect_targets) { + assertValidReference(id, target); + } + break; + default: + kind satisfies never; + throw new Error(`Unhandled feature kind ${kind}}`); } } diff --git a/schemas/data.schema.json b/schemas/data.schema.json index 45b3cec308f..a75219bb06b 100644 --- a/schemas/data.schema.json +++ b/schemas/data.schema.json @@ -54,7 +54,8 @@ { "$ref": "#/definitions/FeatureSplitData" } - ] + ], + "$comment": "Use the `kind` property as a discriminator." } }, "groups": { @@ -112,6 +113,9 @@ "description": "A feature data entry", "type": "object", "properties": { + "kind": { + "const": "feature" + }, "name": { "description": "Short name", "type": "string" @@ -153,7 +157,14 @@ "$ref": "#/definitions/Discouraged" } }, - "required": ["name", "description", "description_html", "spec", "status"], + "required": [ + "kind", + "name", + "description", + "description_html", + "spec", + "status" + ], "additionalProperties": false }, "GroupData": { @@ -191,44 +202,30 @@ "description": "A feature has permanently moved to exactly one other ID", "type": "object", "properties": { - "redirect": { - "type": "object", - "properties": { - "reason": { - "const": "moved" - }, - "target": { - "description": "The new ID for this feature", - "type": "string" - } - }, - "required": ["reason", "target"], - "additionalProperties": false + "kind": { + "const": "moved" + }, + "redirect_target": { + "description": "The new ID for this feature", + "type": "string" } }, - "required": ["redirect"], + "required": ["kind", "redirect_target"], "additionalProperties": false }, "FeatureSplitData": { "description": "A feature has split into two or more other features", "type": "object", "properties": { - "redirect": { - "type": "object", - "properties": { - "reason": { - "const": "split" - }, - "targets": { - "description": "The new IDs for this feature", - "$ref": "#/definitions/Strings" - } - }, - "required": ["reason", "targets"], - "additionalProperties": false + "kind": { + "const": "split" + }, + "redirect_targets": { + "description": "The new IDs for this feature", + "$ref": "#/definitions/Strings" } }, - "required": ["redirect"], + "required": ["kind", "redirect_targets"], "additionalProperties": false }, "Status": { diff --git a/scripts/build.ts b/scripts/build.ts index c2cda0a5b30..04c4aa2a14e 100644 --- a/scripts/build.ts +++ b/scripts/build.ts @@ -7,6 +7,7 @@ import { basename } from "node:path"; import winston from "winston"; import yargs from "yargs"; import * as data from "../index.js"; +import { isOrdinaryFeatureData } from "../type-guards.js"; import { validate } from "./validate.js"; const logger = winston.createLogger({ @@ -68,7 +69,7 @@ function buildPackage() { function buildExtendedJSON() { for (const [id, featureData] of Object.entries(data.features)) { - if ("redirect" in featureData) { + if (!isOrdinaryFeatureData(featureData)) { continue; } diff --git a/scripts/dist.ts b/scripts/dist.ts index c04df563136..9d163c63f2f 100644 --- a/scripts/dist.ts +++ b/scripts/dist.ts @@ -14,12 +14,7 @@ import { isDeepStrictEqual } from "node:util"; import winston from "winston"; import YAML, { Document, Scalar, YAMLSeq } from "yaml"; import yargs from "yargs"; -import { isRedirectData } from "../type-guards"; -import { - FeatureMovedData, - FeatureRedirectData, - FeatureSplitData, -} from "../types"; +import type { FeatureData, FeatureMovedData, FeatureSplitData } from "../types"; const compat = new Compat(); @@ -189,7 +184,7 @@ function compareStatus(a: SupportStatus, b: SupportStatus) { function toRedirectDist( id: string, - source: FeatureMovedData | FeatureRedirectData, + source: FeatureMovedData | FeatureSplitData, ): YAML.Document { const dist = new Document({}); @@ -199,17 +194,20 @@ function toRedirectDist( `Do not edit this file.`, ]; - switch (source.redirect.reason) { + const { kind } = source; + switch (kind) { case "moved": comment.push( - `The data for this feature has moved to ${(source as FeatureMovedData).redirect.target}.yml`, + `The data for this feature has moved to ${source.redirect_target}.yml`, ); break; case "split": - const targets = (source as FeatureSplitData).redirect.targets; comment.push(`The data for this feature has moved to:`); - comment.push(...targets.map((to) => ` - ${to}.yml`)); + comment.push(...source.redirect_targets.map((dest) => ` - ${dest}.yml`)); break; + default: + source satisfies never; + throw new Error(`Unhandled feature kind ${kind}}`); } dist.commentBefore = comment.map((line) => ` ${line}`).join("\n"); @@ -228,9 +226,10 @@ function toDist(sourcePath: string): YAML.Document { const source = YAML.parse(fs.readFileSync(sourcePath, { encoding: "utf-8" })); const { name: id } = path.parse(sourcePath); - if (isRedirectData(source)) { + if ("redirect_target" in source || "redirect_targets" in source) { return toRedirectDist(id, source); } + source as Partial; // Collect tagged compat features. A `compat_features` list in the source // takes precedence, but can be removed if it matches the tagged features. @@ -340,7 +339,7 @@ function toDist(sourcePath: string): YAML.Document { } // Assemble and return the dist YAML. - const dist = new Document({}); + const dist = new Document({ kind: "feature" }); dist.commentBefore = [ `Generated from: ${id}.yml`, diff --git a/scripts/specs.ts b/scripts/specs.ts index ddf95c26228..c597d21761b 100644 --- a/scripts/specs.ts +++ b/scripts/specs.ts @@ -3,7 +3,7 @@ import assert from "node:assert/strict"; import webSpecs from 'web-specs' with { type: 'json' }; import { features } from '../index.js'; -import { isRedirectData } from "../type-guards.js"; +import { isOrdinaryFeatureData } from "../type-guards.js"; // Specs needs to be in "good standing". Nightly URLs are used if available, // otherwise the snapshot/versioned URL is used. See browser-specs/web-specs @@ -192,7 +192,7 @@ for (const [allowedUrl, message] of defaultAllowlist) { } for (const [id, data] of Object.entries(features)) { - if (isRedirectData(data)) { + if (!isOrdinaryFeatureData(data)) { continue; } diff --git a/type-guards.ts b/type-guards.ts index d3d5bc0f66e..d01544d1ba4 100644 --- a/type-guards.ts +++ b/type-guards.ts @@ -1,9 +1,13 @@ -import type { FeatureData, FeatureRedirectData } from "./types"; +import type { FeatureData, FeatureMovedData, FeatureSplitData } from "./types"; export function isOrdinaryFeatureData(x: unknown): x is FeatureData { - return typeof x === "object" && "name" in x; + return typeof x === "object" && "kind" in x && x.kind === "feature"; } -export function isRedirectData(x: unknown): x is FeatureRedirectData { - return typeof x === "object" && "redirect" in x; +export function isSplit(x: unknown): x is FeatureSplitData { + return typeof x === "object" && "kind" in x && x.kind === "moved"; +} + +export function isMoved(x: unknown): x is FeatureMovedData { + return typeof x === "object" && "kind" in x && x.kind === "moved"; } diff --git a/types.quicktype.ts b/types.quicktype.ts index 481b99fa5b9..f76a24fabef 100644 --- a/types.quicktype.ts +++ b/types.quicktype.ts @@ -90,6 +90,7 @@ export interface FeatureData { * Group identifier(s) */ group?: string[] | string; + kind: Kind; /** * Short name */ @@ -107,7 +108,14 @@ export interface FeatureData { * that status */ status?: StatusHeadline; - redirect?: Redirect; + /** + * The new ID for this feature + */ + redirect_target?: string; + /** + * The new IDs for this feature + */ + redirect_targets?: string[]; } /** @@ -125,19 +133,7 @@ export interface Discouraged { alternatives?: string[]; } -export interface Redirect { - reason: Reason; - /** - * The new ID for this feature - */ - target?: string; - /** - * The new IDs for this feature - */ - targets?: string[]; -} - -export type Reason = "moved" | "split"; +export type Kind = "feature" | "moved" | "split"; /** * Whether a feature is considered a "Baseline" web platform feature and when it achieved diff --git a/types.ts b/types.ts index f9d80c75ffa..273bb0ac37d 100644 --- a/types.ts +++ b/types.ts @@ -9,9 +9,9 @@ import type { Browsers, Discouraged, GroupData, + Kind, FeatureData as QuicktypeMonolithicFeatureData, WebFeaturesData as QuicktypeWebFeaturesData, - Redirect, Release, SnapshotData, Status, @@ -40,10 +40,10 @@ export interface WebFeaturesData }; } -export type FeatureData = Required< +export type FeatureData = { kind: "feature" } & Required< Pick< QuicktypeMonolithicFeatureData, - "description_html" | "description" | "name" | "spec" | "status" + "kind" | "description_html" | "description" | "name" | "spec" | "status" > > & Partial< @@ -53,30 +53,23 @@ export type FeatureData = Required< > >; -export type FeatureRedirectData = Required< - Pick +export type FeatureRedirectData = { kind: Exclude } & Required< + Pick >; -export interface Moved extends Exclude { - reason: "moved"; - target: Redirect["target"]; +export interface FeatureMovedData + extends Omit { + kind: "moved"; } -export interface Split extends Exclude { - reason: "split"; - targets: Redirect["targets"]; -} - -export interface FeatureMovedData extends FeatureRedirectData { - redirect: Moved; -} - -export interface FeatureSplitData extends FeatureRedirectData { - redirect: Split; +export interface FeatureSplitData + extends Omit { + kind: "split"; } // eslint-disable-next-line @typescript-eslint/no-unused-vars const t1: FeatureData = { + kind: "feature", name: "Test", description: "Hi", description_html: "Hi", @@ -87,4 +80,16 @@ const t1: FeatureData = { }, }; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const t2: FeatureMovedData = { + kind: "moved", + redirect_target: "", +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const t3: FeatureSplitData = { + kind: "split", + redirect_targets: ["", ""], +}; + export type BrowserIdentifier = keyof Browsers; From 9b276f04076f7afc187c0a847b0bdedd5f5fdee7 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Tue, 22 Jul 2025 17:29:43 +0200 Subject: [PATCH 16/21] Use single-color-gradients as split demonstrator feature --- features/conic-gradients.yml | 1 + features/conic-gradients.yml.dist | 13 ++ features/gradients.yml | 4 + features/gradients.yml.dist | 16 ++ features/single-color-gradients.yml | 15 +- features/single-color-gradients.yml.dist | 25 +-- features/webcodecs.yml | 14 +- features/webcodecs.yml.dist | 239 ++++++++++++++++++++++- 8 files changed, 287 insertions(+), 40 deletions(-) diff --git a/features/conic-gradients.yml b/features/conic-gradients.yml index 296df0f44ff..bbe65dfd98e 100644 --- a/features/conic-gradients.yml +++ b/features/conic-gradients.yml @@ -8,4 +8,5 @@ status: compat_features: - css.types.gradient.conic-gradient - css.types.gradient.conic-gradient.doubleposition + - css.types.gradient.conic-gradient.single_color_stop - css.types.gradient.repeating-conic-gradient diff --git a/features/conic-gradients.yml.dist b/features/conic-gradients.yml.dist index 9b55cfcd57f..013eacbe2d3 100644 --- a/features/conic-gradients.yml.dist +++ b/features/conic-gradients.yml.dist @@ -1,6 +1,7 @@ # Generated from: conic-gradients.yml # Do not edit this file by hand. Edit the source file instead! +kind: feature status: baseline: high baseline_low_date: 2020-11-17 @@ -41,3 +42,15 @@ compat_features: # safari: "12.1" # safari_ios: "12.2" - css.types.gradient.conic-gradient.doubleposition + + # baseline: low + # baseline_low_date: 2025-04-04 + # support: + # chrome: "135" + # chrome_android: "135" + # edge: "135" + # firefox: "136" + # firefox_android: "136" + # safari: "18.4" + # safari_ios: "18.4" + - css.types.gradient.conic-gradient.single_color_stop diff --git a/features/gradients.yml b/features/gradients.yml index fe513c4390c..e95ec8fb792 100644 --- a/features/gradients.yml +++ b/features/gradients.yml @@ -16,18 +16,22 @@ compat_features: - css.types.gradient.linear-gradient.interpolation_hints - css.types.gradient.linear-gradient.premultiplied_gradients - css.types.gradient.linear-gradient.to + - css.types.gradient.linear-gradient.single_color_stop - css.types.gradient.linear-gradient.unitless_0_angle - css.types.gradient.repeating-linear-gradient - css.types.gradient.repeating-linear-gradient.doubleposition - css.types.gradient.repeating-linear-gradient.interpolation_hints - css.types.gradient.repeating-linear-gradient.to + - css.types.gradient.repeating-linear-gradient.single_color_stop - css.types.gradient.repeating-linear-gradient.unitless_0_angle - css.types.gradient.radial-gradient - css.types.gradient.radial-gradient.at - css.types.gradient.radial-gradient.doubleposition - css.types.gradient.radial-gradient.interpolation_hints - css.types.gradient.radial-gradient.premultiplied_gradients + - css.types.gradient.radial-gradient.single_color_stop - css.types.gradient.repeating-radial-gradient - css.types.gradient.repeating-radial-gradient.at - css.types.gradient.repeating-radial-gradient.doubleposition - css.types.gradient.repeating-radial-gradient.interpolation_hints + - css.types.gradient.repeating-radial-gradient.single_color_stop diff --git a/features/gradients.yml.dist b/features/gradients.yml.dist index f5ac728b631..62d12d59d57 100644 --- a/features/gradients.yml.dist +++ b/features/gradients.yml.dist @@ -1,6 +1,7 @@ # Generated from: gradients.yml # Do not edit this file by hand. Edit the source file instead! +kind: feature status: baseline: high baseline_low_date: 2015-07-29 @@ -145,3 +146,18 @@ compat_features: # safari_ios: "15" - css.types.gradient.linear-gradient.premultiplied_gradients - css.types.gradient.radial-gradient.premultiplied_gradients + + # baseline: low + # baseline_low_date: 2025-04-04 + # support: + # chrome: "135" + # chrome_android: "135" + # edge: "135" + # firefox: "136" + # firefox_android: "136" + # safari: "18.4" + # safari_ios: "18.4" + - css.types.gradient.linear-gradient.single_color_stop + - css.types.gradient.radial-gradient.single_color_stop + - css.types.gradient.repeating-linear-gradient.single_color_stop + - css.types.gradient.repeating-radial-gradient.single_color_stop diff --git a/features/single-color-gradients.yml b/features/single-color-gradients.yml index b8e1a4789ba..5d1e93a8ee5 100644 --- a/features/single-color-gradients.yml +++ b/features/single-color-gradients.yml @@ -1,11 +1,4 @@ -name: Single color stop gradients -description: A single color stop can be provided to the `linear-gradient()`, `radial-gradient()`, and `conic-gradient()` CSS functions, and their repeating counterparts, to create a solid color background. -spec: https://drafts.csswg.org/css-images-4/#color-stop-syntax -group: gradients -compat_features: - - css.types.gradient.conic-gradient.single_color_stop - - css.types.gradient.linear-gradient.single_color_stop - - css.types.gradient.radial-gradient.single_color_stop - - css.types.gradient.repeating-conic-gradient.single_color_stop - - css.types.gradient.repeating-linear-gradient.single_color_stop - - css.types.gradient.repeating-radial-gradient.single_color_stop +kind: split +redirect_targets: + - gradients + - conic-gradients diff --git a/features/single-color-gradients.yml.dist b/features/single-color-gradients.yml.dist index 38d174ab005..e7dfa57a3c0 100644 --- a/features/single-color-gradients.yml.dist +++ b/features/single-color-gradients.yml.dist @@ -1,21 +1,8 @@ # Generated from: single-color-gradients.yml -# Do not edit this file by hand. Edit the source file instead! +# This file intentionally left blank. +# Do not edit this file. +# The data for this feature has moved to: +# - gradients.yml +# - conic-gradients.yml -status: - baseline: low - baseline_low_date: 2025-04-04 - support: - chrome: "135" - chrome_android: "135" - edge: "135" - firefox: "136" - firefox_android: "136" - safari: "18.4" - safari_ios: "18.4" -compat_features: - - css.types.gradient.conic-gradient.single_color_stop - - css.types.gradient.linear-gradient.single_color_stop - - css.types.gradient.radial-gradient.single_color_stop - - css.types.gradient.repeating-conic-gradient.single_color_stop - - css.types.gradient.repeating-linear-gradient.single_color_stop - - css.types.gradient.repeating-radial-gradient.single_color_stop +{} diff --git a/features/webcodecs.yml b/features/webcodecs.yml index 7da83637dbd..b28d885ea96 100644 --- a/features/webcodecs.yml +++ b/features/webcodecs.yml @@ -1,4 +1,10 @@ -kind: split -redirect_targets: - - audio-codecs - - video-codecs +name: WebCodecs +description: The WebCodecs API provides low-level access to individual video frames and chunks of audio samples, for full control over the way media is processed. +spec: https://w3c.github.io/webcodecs/ +caniuse: webcodecs +status: + compute_from: + - api.AudioDecoder.AudioDecoder + - api.AudioEncoder.AudioEncoder + - api.VideoDecoder.VideoDecoder + - api.VideoEncoder.VideoEncoder diff --git a/features/webcodecs.yml.dist b/features/webcodecs.yml.dist index 948bdcbda98..de9493647e0 100644 --- a/features/webcodecs.yml.dist +++ b/features/webcodecs.yml.dist @@ -1,8 +1,235 @@ # Generated from: webcodecs.yml -# This file intentionally left blank. -# Do not edit this file. -# The data for this feature has moved to: -# - audio-codecs.yml -# - video-codecs.yml +# Do not edit this file by hand. Edit the source file instead! -{} +status: + baseline: false + support: + chrome: "94" + chrome_android: "94" + edge: "94" + firefox: "130" +compat_features: + # baseline: low + # baseline_low_date: 2024-09-03 + # support: + # chrome: "94" + # chrome_android: "94" + # edge: "94" + # firefox: "130" + # firefox_android: "130" + # safari: "15.4" + # safari_ios: "15.4" + - api.VideoColorSpace + - api.VideoColorSpace.fullRange + - api.VideoColorSpace.matrix + - api.VideoColorSpace.primaries + - api.VideoColorSpace.toJSON + - api.VideoColorSpace.transfer + + # baseline: low + # baseline_low_date: 2024-09-03 + # support: + # chrome: "94" + # chrome_android: "94" + # edge: "94" + # firefox: "130" + # firefox_android: "130" + # safari: "16.4" + # safari_ios: "16.4" + - api.VideoFrame + - api.VideoFrame.VideoFrame + - api.VideoFrame.allocationSize + - api.VideoFrame.clone + - api.VideoFrame.close + - api.VideoFrame.codedHeight + - api.VideoFrame.codedRect + - api.VideoFrame.codedWidth + - api.VideoFrame.colorSpace + - api.VideoFrame.copyTo + - api.VideoFrame.displayHeight + - api.VideoFrame.displayWidth + - api.VideoFrame.duration + - api.VideoFrame.format + - api.VideoFrame.timestamp + - api.VideoFrame.visibleRect + + # baseline: low + # baseline_low_date: 2024-09-03 + # support: + # chrome: "94" + # chrome_android: "94" + # edge: "94" + # firefox: "130" + # firefox_android: "130" + # safari: "17" + # safari_ios: "17" + - api.VideoColorSpace.VideoColorSpace + + # baseline: false + # support: + # chrome: "94" + # chrome_android: "94" + # edge: "94" + # firefox: "130" + # safari: "16.4" + # safari_ios: "16.4" + - api.EncodedVideoChunk + - api.EncodedVideoChunk.EncodedVideoChunk + - api.EncodedVideoChunk.byteLength + - api.EncodedVideoChunk.copyTo + - api.EncodedVideoChunk.duration + - api.EncodedVideoChunk.timestamp + - api.EncodedVideoChunk.type + - api.VideoDecoder + - api.VideoDecoder.VideoDecoder + - api.VideoDecoder.close + - api.VideoDecoder.configure + - api.VideoDecoder.decode + - api.VideoDecoder.decodeQueueSize + - api.VideoDecoder.flush + - api.VideoDecoder.isConfigSupported_static + - api.VideoDecoder.reset + - api.VideoDecoder.state + - api.VideoEncoder + - api.VideoEncoder.VideoEncoder + - api.VideoEncoder.close + - api.VideoEncoder.configure + - api.VideoEncoder.encode + - api.VideoEncoder.encodeQueueSize + - api.VideoEncoder.flush + - api.VideoEncoder.isConfigSupported_static + - api.VideoEncoder.reset + - api.VideoEncoder.state + + # baseline: false + # support: + # chrome: "106" + # chrome_android: "106" + # edge: "106" + # firefox: "130" + # safari: "16.4" + # safari_ios: "16.4" + - api.VideoDecoder.dequeue_event + - api.VideoEncoder.dequeue_event + + # baseline: false + # support: + # chrome: "94" + # chrome_android: "94" + # edge: "94" + # firefox: "133" + # firefox_android: "133" + - api.ImageDecoder + - api.ImageDecoder.ImageDecoder + - api.ImageDecoder.close + - api.ImageDecoder.complete + - api.ImageDecoder.completed + - api.ImageDecoder.decode + - api.ImageDecoder.isTypeSupported_static + - api.ImageDecoder.reset + - api.ImageDecoder.tracks + - api.ImageDecoder.type + - api.ImageTrack + - api.ImageTrack.animated + - api.ImageTrack.frameCount + - api.ImageTrack.repetitionCount + - api.ImageTrack.selected + - api.ImageTrackList + - api.ImageTrackList.length + - api.ImageTrackList.ready + - api.ImageTrackList.selectedIndex + - api.ImageTrackList.selectedTrack + + # ⬇️ Same status as overall feature ⬇️ + # baseline: false + # support: + # chrome: "94" + # chrome_android: "94" + # edge: "94" + # firefox: "130" + - api.AudioData + - api.AudioData.AudioData + - api.AudioData.allocationSize + - api.AudioData.clone + - api.AudioData.close + - api.AudioData.copyTo + - api.AudioData.duration + - api.AudioData.format + - api.AudioData.numberOfChannels + - api.AudioData.numberOfFrames + - api.AudioData.sampleRate + - api.AudioData.timestamp + - api.AudioDecoder + - api.AudioDecoder.AudioDecoder + - api.AudioDecoder.close + - api.AudioDecoder.configure + - api.AudioDecoder.decode + - api.AudioDecoder.decodeQueueSize + - api.AudioDecoder.flush + - api.AudioDecoder.isConfigSupported_static + - api.AudioDecoder.reset + - api.AudioDecoder.state + - api.AudioEncoder + - api.AudioEncoder.AudioEncoder + - api.AudioEncoder.close + - api.AudioEncoder.configure + - api.AudioEncoder.encode + - api.AudioEncoder.encodeQueueSize + - api.AudioEncoder.flush + - api.AudioEncoder.isConfigSupported_static + - api.AudioEncoder.reset + - api.AudioEncoder.state + - api.EncodedAudioChunk + - api.EncodedAudioChunk.EncodedAudioChunk + - api.EncodedAudioChunk.byteLength + - api.EncodedAudioChunk.copyTo + - api.EncodedAudioChunk.duration + - api.EncodedAudioChunk.timestamp + - api.EncodedAudioChunk.type + + # baseline: false + # support: + # chrome: "106" + # chrome_android: "106" + # edge: "106" + # firefox: "130" + - api.AudioDecoder.dequeue_event + - api.AudioEncoder.dequeue_event + + # baseline: false + # support: + # chrome: "110" + # chrome_android: "110" + # edge: "110" + # firefox: "130" + - api.AudioEncoder.configure.opus + + # baseline: false + # support: + # chrome: "119" + # chrome_android: "119" + # edge: "119" + # firefox: "130" + - api.AudioEncoder.configure.bitrateMode + + # baseline: false + # support: + # chrome: "126" + # chrome_android: "126" + # edge: "126" + - api.AudioEncoder.configure.opus.opus_application + - api.AudioEncoder.configure.opus.opus_signal + + # baseline: false + # support: + # chrome: "138" + # chrome_android: "138" + # edge: "138" + - api.VideoDecoder.configure.flip_option + - api.VideoDecoder.configure.rotation_option + - api.VideoDecoder.isConfigSupported_static.flip_option + - api.VideoDecoder.isConfigSupported_static.rotation_option + - api.VideoFrame.VideoFrame.flip_option + - api.VideoFrame.VideoFrame.rotation_option + - api.VideoFrame.flip + - api.VideoFrame.rotation From 66adbd434527c5d767c59de78bb5465d2d82c4f3 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Tue, 22 Jul 2025 18:47:17 +0200 Subject: [PATCH 17/21] fixup! Use single-color-gradients as split demonstrator feature --- features/audio-codecs.yml | 53 ------------ features/audio-codecs.yml.dist | 90 --------------------- features/video-codecs.yml | 80 ------------------ features/video-codecs.yml.dist | 144 --------------------------------- 4 files changed, 367 deletions(-) delete mode 100644 features/audio-codecs.yml delete mode 100644 features/audio-codecs.yml.dist delete mode 100644 features/video-codecs.yml delete mode 100644 features/video-codecs.yml.dist diff --git a/features/audio-codecs.yml b/features/audio-codecs.yml deleted file mode 100644 index d638359ad7a..00000000000 --- a/features/audio-codecs.yml +++ /dev/null @@ -1,53 +0,0 @@ -name: Audio codecs -description: The `AudioEncoder` and `AudioDecoder` APIs (part of WebCodecs) provide low-level access to chunks of audio samples, for full control over the way audio is processed. -spec: https://w3c.github.io/webcodecs/ -status: - compute_from: - - api.AudioDecoder.AudioDecoder - - api.AudioEncoder.AudioEncoder -compat_features: - - api.AudioData - - api.AudioData.AudioData - - api.AudioData.allocationSize - - api.AudioData.clone - - api.AudioData.close - - api.AudioData.copyTo - - api.AudioData.duration - - api.AudioData.format - - api.AudioData.numberOfChannels - - api.AudioData.numberOfFrames - - api.AudioData.sampleRate - - api.AudioData.timestamp - - api.AudioDecoder - - api.AudioDecoder.AudioDecoder - - api.AudioDecoder.close - - api.AudioDecoder.configure - - api.AudioDecoder.decode - - api.AudioDecoder.decodeQueueSize - - api.AudioDecoder.dequeue_event - - api.AudioDecoder.flush - - api.AudioDecoder.isConfigSupported_static - - api.AudioDecoder.reset - - api.AudioDecoder.state - - api.AudioEncoder - - api.AudioEncoder.AudioEncoder - - api.AudioEncoder.close - - api.AudioEncoder.configure - - api.AudioEncoder.configure.bitrateMode - - api.AudioEncoder.configure.opus - - api.AudioEncoder.configure.opus.opus_application - - api.AudioEncoder.configure.opus.opus_signal - - api.AudioEncoder.dequeue_event - - api.AudioEncoder.encode - - api.AudioEncoder.encodeQueueSize - - api.AudioEncoder.flush - - api.AudioEncoder.isConfigSupported_static - - api.AudioEncoder.reset - - api.AudioEncoder.state - - api.EncodedAudioChunk - - api.EncodedAudioChunk.EncodedAudioChunk - - api.EncodedAudioChunk.byteLength - - api.EncodedAudioChunk.copyTo - - api.EncodedAudioChunk.duration - - api.EncodedAudioChunk.timestamp - - api.EncodedAudioChunk.type diff --git a/features/audio-codecs.yml.dist b/features/audio-codecs.yml.dist deleted file mode 100644 index e2245819f6f..00000000000 --- a/features/audio-codecs.yml.dist +++ /dev/null @@ -1,90 +0,0 @@ -# Generated from: audio-codecs.yml -# Do not edit this file by hand. Edit the source file instead! - -status: - baseline: false - support: - chrome: "94" - chrome_android: "94" - edge: "94" - firefox: "130" -compat_features: - # ⬇️ Same status as overall feature ⬇️ - # baseline: false - # support: - # chrome: "94" - # chrome_android: "94" - # edge: "94" - # firefox: "130" - - api.AudioData - - api.AudioData.AudioData - - api.AudioData.allocationSize - - api.AudioData.clone - - api.AudioData.close - - api.AudioData.copyTo - - api.AudioData.duration - - api.AudioData.format - - api.AudioData.numberOfChannels - - api.AudioData.numberOfFrames - - api.AudioData.sampleRate - - api.AudioData.timestamp - - api.AudioDecoder - - api.AudioDecoder.AudioDecoder - - api.AudioDecoder.close - - api.AudioDecoder.configure - - api.AudioDecoder.decode - - api.AudioDecoder.decodeQueueSize - - api.AudioDecoder.flush - - api.AudioDecoder.isConfigSupported_static - - api.AudioDecoder.reset - - api.AudioDecoder.state - - api.AudioEncoder - - api.AudioEncoder.AudioEncoder - - api.AudioEncoder.close - - api.AudioEncoder.configure - - api.AudioEncoder.encode - - api.AudioEncoder.encodeQueueSize - - api.AudioEncoder.flush - - api.AudioEncoder.isConfigSupported_static - - api.AudioEncoder.reset - - api.AudioEncoder.state - - api.EncodedAudioChunk - - api.EncodedAudioChunk.EncodedAudioChunk - - api.EncodedAudioChunk.byteLength - - api.EncodedAudioChunk.copyTo - - api.EncodedAudioChunk.duration - - api.EncodedAudioChunk.timestamp - - api.EncodedAudioChunk.type - - # baseline: false - # support: - # chrome: "106" - # chrome_android: "106" - # edge: "106" - # firefox: "130" - - api.AudioDecoder.dequeue_event - - api.AudioEncoder.dequeue_event - - # baseline: false - # support: - # chrome: "110" - # chrome_android: "110" - # edge: "110" - # firefox: "130" - - api.AudioEncoder.configure.opus - - # baseline: false - # support: - # chrome: "119" - # chrome_android: "119" - # edge: "119" - # firefox: "130" - - api.AudioEncoder.configure.bitrateMode - - # baseline: false - # support: - # chrome: "126" - # chrome_android: "126" - # edge: "126" - - api.AudioEncoder.configure.opus.opus_application - - api.AudioEncoder.configure.opus.opus_signal diff --git a/features/video-codecs.yml b/features/video-codecs.yml deleted file mode 100644 index 2c81b6d9ac8..00000000000 --- a/features/video-codecs.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: Video codecs -description: The `VideoEncoder` and `VideoDecoder` APIs (part of WebCodecs) provide low-level access to individual video frames, for full control over the way video is processed. -spec: https://w3c.github.io/webcodecs/ -status: - compute_from: - - api.VideoDecoder.VideoDecoder - - api.VideoEncoder.VideoEncoder -compat_features: - - api.EncodedVideoChunk - - api.EncodedVideoChunk.EncodedVideoChunk - - api.EncodedVideoChunk.byteLength - - api.EncodedVideoChunk.copyTo - - api.EncodedVideoChunk.duration - - api.EncodedVideoChunk.timestamp - - api.EncodedVideoChunk.type - - api.ImageDecoder - - api.ImageDecoder.ImageDecoder - - api.ImageDecoder.close - - api.ImageDecoder.complete - - api.ImageDecoder.completed - - api.ImageDecoder.decode - - api.ImageDecoder.isTypeSupported_static - - api.ImageDecoder.reset - - api.ImageDecoder.tracks - - api.ImageDecoder.type - - api.ImageTrack - - api.ImageTrack.animated - - api.ImageTrack.frameCount - - api.ImageTrack.repetitionCount - - api.ImageTrack.selected - - api.ImageTrackList - - api.ImageTrackList.length - - api.ImageTrackList.ready - - api.ImageTrackList.selectedIndex - - api.ImageTrackList.selectedTrack - - api.VideoColorSpace - - api.VideoColorSpace.VideoColorSpace - - api.VideoColorSpace.fullRange - - api.VideoColorSpace.matrix - - api.VideoColorSpace.primaries - - api.VideoColorSpace.toJSON - - api.VideoColorSpace.transfer - - api.VideoDecoder - - api.VideoDecoder.VideoDecoder - - api.VideoDecoder.close - - api.VideoDecoder.configure - - api.VideoDecoder.decode - - api.VideoDecoder.decodeQueueSize - - api.VideoDecoder.dequeue_event - - api.VideoDecoder.flush - - api.VideoDecoder.isConfigSupported_static - - api.VideoDecoder.reset - - api.VideoDecoder.state - - api.VideoEncoder - - api.VideoEncoder.VideoEncoder - - api.VideoEncoder.close - - api.VideoEncoder.configure - - api.VideoEncoder.dequeue_event - - api.VideoEncoder.encode - - api.VideoEncoder.encodeQueueSize - - api.VideoEncoder.flush - - api.VideoEncoder.isConfigSupported_static - - api.VideoEncoder.reset - - api.VideoEncoder.state - - api.VideoFrame - - api.VideoFrame.VideoFrame - - api.VideoFrame.allocationSize - - api.VideoFrame.clone - - api.VideoFrame.close - - api.VideoFrame.codedHeight - - api.VideoFrame.codedRect - - api.VideoFrame.codedWidth - - api.VideoFrame.colorSpace - - api.VideoFrame.copyTo - - api.VideoFrame.displayHeight - - api.VideoFrame.displayWidth - - api.VideoFrame.duration - - api.VideoFrame.format - - api.VideoFrame.timestamp - - api.VideoFrame.visibleRect diff --git a/features/video-codecs.yml.dist b/features/video-codecs.yml.dist deleted file mode 100644 index dce7bf2b318..00000000000 --- a/features/video-codecs.yml.dist +++ /dev/null @@ -1,144 +0,0 @@ -# Generated from: video-codecs.yml -# Do not edit this file by hand. Edit the source file instead! - -status: - baseline: false - support: - chrome: "94" - chrome_android: "94" - edge: "94" - firefox: "130" - safari: "16.4" - safari_ios: "16.4" -compat_features: - # baseline: low - # baseline_low_date: 2024-09-03 - # support: - # chrome: "94" - # chrome_android: "94" - # edge: "94" - # firefox: "130" - # firefox_android: "130" - # safari: "15.4" - # safari_ios: "15.4" - - api.VideoColorSpace - - api.VideoColorSpace.fullRange - - api.VideoColorSpace.matrix - - api.VideoColorSpace.primaries - - api.VideoColorSpace.toJSON - - api.VideoColorSpace.transfer - - # baseline: low - # baseline_low_date: 2024-09-03 - # support: - # chrome: "94" - # chrome_android: "94" - # edge: "94" - # firefox: "130" - # firefox_android: "130" - # safari: "16.4" - # safari_ios: "16.4" - - api.VideoFrame - - api.VideoFrame.VideoFrame - - api.VideoFrame.allocationSize - - api.VideoFrame.clone - - api.VideoFrame.close - - api.VideoFrame.codedHeight - - api.VideoFrame.codedRect - - api.VideoFrame.codedWidth - - api.VideoFrame.colorSpace - - api.VideoFrame.copyTo - - api.VideoFrame.displayHeight - - api.VideoFrame.displayWidth - - api.VideoFrame.duration - - api.VideoFrame.format - - api.VideoFrame.timestamp - - api.VideoFrame.visibleRect - - # baseline: low - # baseline_low_date: 2024-09-03 - # support: - # chrome: "94" - # chrome_android: "94" - # edge: "94" - # firefox: "130" - # firefox_android: "130" - # safari: "17" - # safari_ios: "17" - - api.VideoColorSpace.VideoColorSpace - - # ⬇️ Same status as overall feature ⬇️ - # baseline: false - # support: - # chrome: "94" - # chrome_android: "94" - # edge: "94" - # firefox: "130" - # safari: "16.4" - # safari_ios: "16.4" - - api.EncodedVideoChunk - - api.EncodedVideoChunk.EncodedVideoChunk - - api.EncodedVideoChunk.byteLength - - api.EncodedVideoChunk.copyTo - - api.EncodedVideoChunk.duration - - api.EncodedVideoChunk.timestamp - - api.EncodedVideoChunk.type - - api.VideoDecoder - - api.VideoDecoder.VideoDecoder - - api.VideoDecoder.close - - api.VideoDecoder.configure - - api.VideoDecoder.decode - - api.VideoDecoder.decodeQueueSize - - api.VideoDecoder.flush - - api.VideoDecoder.isConfigSupported_static - - api.VideoDecoder.reset - - api.VideoDecoder.state - - api.VideoEncoder - - api.VideoEncoder.VideoEncoder - - api.VideoEncoder.close - - api.VideoEncoder.configure - - api.VideoEncoder.encode - - api.VideoEncoder.encodeQueueSize - - api.VideoEncoder.flush - - api.VideoEncoder.isConfigSupported_static - - api.VideoEncoder.reset - - api.VideoEncoder.state - - # baseline: false - # support: - # chrome: "106" - # chrome_android: "106" - # edge: "106" - # firefox: "130" - # safari: "16.4" - # safari_ios: "16.4" - - api.VideoDecoder.dequeue_event - - api.VideoEncoder.dequeue_event - - # baseline: false - # support: - # chrome: "94" - # chrome_android: "94" - # edge: "94" - # firefox: "133" - # firefox_android: "133" - - api.ImageDecoder - - api.ImageDecoder.ImageDecoder - - api.ImageDecoder.close - - api.ImageDecoder.complete - - api.ImageDecoder.completed - - api.ImageDecoder.decode - - api.ImageDecoder.isTypeSupported_static - - api.ImageDecoder.reset - - api.ImageDecoder.tracks - - api.ImageDecoder.type - - api.ImageTrack - - api.ImageTrack.animated - - api.ImageTrack.frameCount - - api.ImageTrack.repetitionCount - - api.ImageTrack.selected - - api.ImageTrackList - - api.ImageTrackList.length - - api.ImageTrackList.ready - - api.ImageTrackList.selectedIndex - - api.ImageTrackList.selectedTrack From 6ad4e80820b705584d025cb1fdc08aa6b315afbe Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Tue, 22 Jul 2025 20:15:10 +0200 Subject: [PATCH 18/21] Set `kind: feature` at runtime, for author convenience --- features/conic-gradients.yml.dist | 1 - features/gradients.yml.dist | 1 - features/numeric-separators.yml.dist | 1 - index.ts | 5 +++++ scripts/dist.ts | 2 +- type-guards.ts | 2 +- 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/features/conic-gradients.yml.dist b/features/conic-gradients.yml.dist index 013eacbe2d3..ff1a7cb8638 100644 --- a/features/conic-gradients.yml.dist +++ b/features/conic-gradients.yml.dist @@ -1,7 +1,6 @@ # Generated from: conic-gradients.yml # Do not edit this file by hand. Edit the source file instead! -kind: feature status: baseline: high baseline_low_date: 2020-11-17 diff --git a/features/gradients.yml.dist b/features/gradients.yml.dist index 62d12d59d57..625d60b9b77 100644 --- a/features/gradients.yml.dist +++ b/features/gradients.yml.dist @@ -1,7 +1,6 @@ # Generated from: gradients.yml # Do not edit this file by hand. Edit the source file instead! -kind: feature status: baseline: high baseline_low_date: 2015-07-29 diff --git a/features/numeric-separators.yml.dist b/features/numeric-separators.yml.dist index 6f529f71b66..996e1916346 100644 --- a/features/numeric-separators.yml.dist +++ b/features/numeric-separators.yml.dist @@ -1,7 +1,6 @@ # Generated from: numeric-separators.yml # Do not edit this file by hand. Edit the source file instead! -kind: feature status: baseline: high baseline_low_date: 2020-07-28 diff --git a/index.ts b/index.ts index 527d51622c3..dd832d3c890 100644 --- a/index.ts +++ b/index.ts @@ -129,6 +129,11 @@ for (const [key, data] of yamlEntries('features')) { continue; } + // Attach `kind: feature` to ordinary features + if (!isMoved(data) && !isSplit(data)) { + data.kind = "feature"; + } + // Convert markdown to text+HTML. if (data.description) { const { text, html } = convertMarkdown(data.description); diff --git a/scripts/dist.ts b/scripts/dist.ts index 9d163c63f2f..7525b422dd0 100644 --- a/scripts/dist.ts +++ b/scripts/dist.ts @@ -339,7 +339,7 @@ function toDist(sourcePath: string): YAML.Document { } // Assemble and return the dist YAML. - const dist = new Document({ kind: "feature" }); + const dist = new Document({}); dist.commentBefore = [ `Generated from: ${id}.yml`, diff --git a/type-guards.ts b/type-guards.ts index d01544d1ba4..806a88139d4 100644 --- a/type-guards.ts +++ b/type-guards.ts @@ -5,7 +5,7 @@ export function isOrdinaryFeatureData(x: unknown): x is FeatureData { } export function isSplit(x: unknown): x is FeatureSplitData { - return typeof x === "object" && "kind" in x && x.kind === "moved"; + return typeof x === "object" && "kind" in x && x.kind === "split"; } export function isMoved(x: unknown): x is FeatureMovedData { From 7845c1a6a59aa1053a6c36f13fbb7f6d05ae9a4a Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Tue, 22 Jul 2025 20:21:07 +0200 Subject: [PATCH 19/21] Fix switch statement exhaustiveness check --- scripts/dist.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/dist.ts b/scripts/dist.ts index 7525b422dd0..1832f6a4b89 100644 --- a/scripts/dist.ts +++ b/scripts/dist.ts @@ -206,7 +206,7 @@ function toRedirectDist( comment.push(...source.redirect_targets.map((dest) => ` - ${dest}.yml`)); break; default: - source satisfies never; + kind satisfies never; throw new Error(`Unhandled feature kind ${kind}}`); } From d457d4d78d77236d81413dc012313de6f4d09fd7 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Thu, 7 Aug 2025 18:07:41 +0200 Subject: [PATCH 20/21] Add `redirect_created_date` field on redirects --- features/numeric-seperators.yml | 1 + features/single-color-gradients.yml | 1 + schemas/data.schema.json | 12 ++++++++++-- types.quicktype.ts | 4 ++++ types.ts | 7 ++++++- 5 files changed, 22 insertions(+), 3 deletions(-) diff --git a/features/numeric-seperators.yml b/features/numeric-seperators.yml index 04c6806c7fd..9b5c384ca6e 100644 --- a/features/numeric-seperators.yml +++ b/features/numeric-seperators.yml @@ -1,2 +1,3 @@ kind: moved +redirect_created_date: 2025-09-01 redirect_target: numeric-separators diff --git a/features/single-color-gradients.yml b/features/single-color-gradients.yml index 5d1e93a8ee5..adfe08421b2 100644 --- a/features/single-color-gradients.yml +++ b/features/single-color-gradients.yml @@ -1,4 +1,5 @@ kind: split +redirect_created_date: 2025-09-01 redirect_targets: - gradients - conic-gradients diff --git a/schemas/data.schema.json b/schemas/data.schema.json index a75219bb06b..1e25bc7e0d2 100644 --- a/schemas/data.schema.json +++ b/schemas/data.schema.json @@ -205,12 +205,16 @@ "kind": { "const": "moved" }, + "redirect_created_date": { + "description": "The date this redirect was created", + "type": "string" + }, "redirect_target": { "description": "The new ID for this feature", "type": "string" } }, - "required": ["kind", "redirect_target"], + "required": ["kind", "redirect_created_date", "redirect_target"], "additionalProperties": false }, "FeatureSplitData": { @@ -220,12 +224,16 @@ "kind": { "const": "split" }, + "redirect_created_date": { + "description": "The date this redirect was created", + "type": "string" + }, "redirect_targets": { "description": "The new IDs for this feature", "$ref": "#/definitions/Strings" } }, - "required": ["kind", "redirect_targets"], + "required": ["kind", "redirect_created_date", "redirect_targets"], "additionalProperties": false }, "Status": { diff --git a/types.quicktype.ts b/types.quicktype.ts index f76a24fabef..fedc4dfedec 100644 --- a/types.quicktype.ts +++ b/types.quicktype.ts @@ -108,6 +108,10 @@ export interface FeatureData { * that status */ status?: StatusHeadline; + /** + * The date this redirect was created + */ + redirect_created_date?: string; /** * The new ID for this feature */ diff --git a/types.ts b/types.ts index 273bb0ac37d..8948a895940 100644 --- a/types.ts +++ b/types.ts @@ -54,7 +54,10 @@ export type FeatureData = { kind: "feature" } & Required< >; export type FeatureRedirectData = { kind: Exclude } & Required< - Pick + Pick< + QuicktypeMonolithicFeatureData, + "redirect_created_date" | "redirect_target" | "redirect_targets" + > >; export interface FeatureMovedData @@ -83,12 +86,14 @@ const t1: FeatureData = { // eslint-disable-next-line @typescript-eslint/no-unused-vars const t2: FeatureMovedData = { kind: "moved", + redirect_created_date: "2025-09-01", redirect_target: "", }; // eslint-disable-next-line @typescript-eslint/no-unused-vars const t3: FeatureSplitData = { kind: "split", + redirect_created_date: "2025-09-01", redirect_targets: ["", ""], }; From f88369b0c99bde3ebcc5ba38183bee3c5230b400 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Mon, 18 Aug 2025 13:38:33 +0200 Subject: [PATCH 21/21] Fix out of date package-lock.json --- package-lock.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/package-lock.json b/package-lock.json index 823fba2bf51..bb31c77e230 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5504,6 +5504,20 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/quicktype/node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",