From aca9beaa64a3b174ded9df8140e0876820689524 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Mon, 8 Sep 2025 14:36:03 +0200 Subject: [PATCH 1/6] Demonstrate JSON Schema as source of truth for web-features data (#2990) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: James C Scott III <7788930+jcscottiii@users.noreply.github.com> Co-authored-by: Philip Jägenstedt --- .gitignore | 4 +- .prettierignore | 3 - package-lock.json | 1450 +++++++++++++++++++-------- package.json | 9 +- packages/web-features/package.json | 4 +- packages/web-features/tsconfig.json | 3 +- schemas/data.schema.json | 537 +++++----- scripts/build.ts | 7 +- scripts/schema.ts | 16 +- types.quicktype.ts | 206 ++++ types.ts | 167 ++- 11 files changed, 1576 insertions(+), 830 deletions(-) create mode 100644 types.quicktype.ts 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/.prettierignore b/.prettierignore index 743a54d17e2..eb97a6eba00 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/** @@ -11,14 +10,12 @@ # TODO: Format all these files README.md index.ts -types.ts 2022-backgrounder.md towards-features.md /.github/** /docs !/docs/publishing.md /features/draft -/schemas /scripts/caniuse.ts /scripts/schema.ts /scripts/specs.ts diff --git a/package-lock.json b/package-lock.json index 89a3a5da8cd..7ee560b4c10 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "fdir": "^6.5.0", "hast-util-to-string": "^3.0.1", "prettier": "^3.6.2", + "quicktype": "^23.2.6", "rehype-parse": "^9.0.1", "rehype-stringify": "^10.0.1", "remark-parse": "^11.0.0", @@ -63,79 +64,39 @@ "node": ">=0.1.90" } }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "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": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", - "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", - "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], + "@jridgewell/trace-mapping": "0.3.9" + }, "engines": { - "node": ">=18" + "node": ">=12" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", - "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", - "cpu": [ - "arm64" - ], + "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, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", - "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", - "cpu": [ - "x64" - ], + "node_modules/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", "dev": true, - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" } }, "node_modules/@esbuild/darwin-arm64": { @@ -154,326 +115,6 @@ "node": ">=18" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", - "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", - "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", - "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", - "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", - "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", - "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", - "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", - "cpu": [ - "loong64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", - "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", - "cpu": [ - "mips64el" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", - "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", - "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", - "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", - "cpu": [ - "s390x" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", - "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", - "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", - "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", - "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", - "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", - "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", - "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", - "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", - "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", @@ -649,6 +290,13 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "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/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -750,54 +398,224 @@ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" }, "engines": { - "node": ">=12" + "node": ">=12" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "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 + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@js-temporal/polyfill": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@js-temporal/polyfill/-/polyfill-0.5.1.tgz", + "integrity": "sha512-hloP58zRVCRSpgDxmqCWJNlizAlUgJFqG2ypq79DCvyv9tHjRYMDOcPFjzfl/A1/YxDvRCZz8wvZvmapQnKwFQ==", + "dependencies": { + "jsbi": "^4.3.0" + }, + "engines": { + "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/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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@mark.probst/typescript-json-schema/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@mark.probst/typescript-json-schema/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, + "license": "MIT" + }, + "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/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, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "node_modules/@mark.probst/typescript-json-schema/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, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, "engines": { "node": ">=8" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "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": ">=6.0.0" + "node": ">=4.2.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 - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "node_modules/@mark.probst/typescript-json-schema/node_modules/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, + "license": "MIT", "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "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/@js-temporal/polyfill": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@js-temporal/polyfill/-/polyfill-0.5.1.tgz", - "integrity": "sha512-hloP58zRVCRSpgDxmqCWJNlizAlUgJFqG2ypq79DCvyv9tHjRYMDOcPFjzfl/A1/YxDvRCZz8wvZvmapQnKwFQ==", + "node_modules/@mark.probst/typescript-json-schema/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", "dependencies": { - "jsbi": "^4.3.0" + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" }, "engines": { "node": ">=12" } }, + "node_modules/@mark.probst/typescript-json-schema/node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/@mdn/browser-compat-data": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-7.1.2.tgz", @@ -1006,6 +824,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", "resolved": "https://registry.npmjs.org/@types/caniuse-lite/-/caniuse-lite-1.0.5.tgz", @@ -1381,12 +1227,24 @@ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true }, + "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.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1404,6 +1262,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", @@ -1447,12 +1318,29 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "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", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "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/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", @@ -1475,6 +1363,27 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "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/before-after-hook": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", @@ -1492,7 +1401,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1511,12 +1419,44 @@ "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", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "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", @@ -1741,6 +1681,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "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/character-entities": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", @@ -1895,6 +1851,13 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "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", "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", @@ -1968,6 +1931,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", @@ -1990,8 +2005,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", @@ -1999,6 +2013,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.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2487,6 +2518,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", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -2634,6 +2685,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", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -2702,6 +2766,13 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2820,6 +2891,17 @@ "dev": true, "license": "MIT" }, + "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": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -3018,6 +3100,27 @@ "node": ">=0.10.0" } }, + "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.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3055,6 +3158,18 @@ "node": ">=0.8.19" } }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -3143,6 +3258,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", @@ -3185,6 +3307,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": "4.1.1", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", @@ -3465,6 +3594,13 @@ "node": ">=4" } }, + "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", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3559,6 +3695,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", @@ -3629,6 +3779,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.2", "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", @@ -4171,7 +4328,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -4449,6 +4605,16 @@ "node": ">=12" } }, + "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", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -4461,6 +4627,27 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "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", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -4482,6 +4669,16 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/one-time": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", @@ -4545,6 +4742,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", @@ -4607,6 +4811,13 @@ "url": "https://github.com/fb55/entities?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", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4616,6 +4827,16 @@ "node": ">=8" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "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", @@ -4661,6 +4882,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", @@ -4732,6 +4963,16 @@ "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, + "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": "7.1.0", "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", @@ -4773,6 +5014,159 @@ ], "license": "MIT" }, + "node_modules/quicktype": { + "version": "23.2.6", + "resolved": "https://registry.npmjs.org/quicktype/-/quicktype-23.2.6.tgz", + "integrity": "sha512-rlD1jF71bOmDn6SQ/ToLuuRkMQ7maxo5oVTn5dPCl11ymqoJCFCvl7FzRfh+fkDFmWt2etl+JiIEdWImLxferA==", + "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.6", + "quicktype-graphql-input": "23.2.6", + "quicktype-typescript-input": "23.2.6", + "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.6", + "resolved": "https://registry.npmjs.org/quicktype-core/-/quicktype-core-23.2.6.tgz", + "integrity": "sha512-asfeSv7BKBNVb9WiYhFRBvBZHcRutPRBwJMxW0pefluK4kkKu4lv0IvZBwFKvw2XygLcL1Rl90zxWDHYgkwCmA==", + "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.6", + "resolved": "https://registry.npmjs.org/quicktype-graphql-input/-/quicktype-graphql-input-23.2.6.tgz", + "integrity": "sha512-jHQ8XrEaccZnWA7h/xqUQhfl+0mR5o91T6k3I4QhlnZSLdVnbycrMq4FHa9EaIFcai783JKwSUl1+koAdJq4pg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "collection-utils": "^1.0.1", + "graphql": "^0.11.7", + "quicktype-core": "23.2.6" + } + }, + "node_modules/quicktype-typescript-input": { + "version": "23.2.6", + "resolved": "https://registry.npmjs.org/quicktype-typescript-input/-/quicktype-typescript-input-23.2.6.tgz", + "integrity": "sha512-dCNMxR+7PGs9/9Tsth9H6LOQV1G+Tv4sUGT8ZUfDRJ5Hq371qOYLma5BnLX6VxkPu8JT7mAMpQ9VFlxstX6Qaw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@mark.probst/typescript-json-schema": "0.55.0", + "quicktype-core": "23.2.6", + "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/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/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", @@ -5062,6 +5456,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", @@ -5071,6 +5482,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": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -5205,6 +5626,30 @@ "node": ">=8" } }, + "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", @@ -5306,6 +5751,13 @@ "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", "dev": 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", @@ -5319,6 +5771,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", "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", @@ -5383,6 +5842,60 @@ "node": ">=18.0.0" } }, + "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", @@ -5460,6 +5973,16 @@ "typescript": ">=4.8.4 <6.0.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": { "version": "7.12.0", "resolved": "https://registry.npmjs.org/undici/-/undici-7.12.0.tgz", @@ -5476,6 +5999,35 @@ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true }, + "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", @@ -5579,12 +6131,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.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", @@ -5660,6 +6226,13 @@ "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-encoding": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", @@ -5681,6 +6254,17 @@ "node": ">=18" } }, + "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", @@ -5742,6 +6326,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": "9.3.2", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.2.tgz", @@ -5836,6 +6437,13 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -5931,6 +6539,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index dbe4dfd14a8..468d4557698 100644 --- a/package.json +++ b/package.json @@ -23,17 +23,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/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", "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" }, @@ -54,6 +54,7 @@ "fdir": "^6.5.0", "hast-util-to-string": "^3.0.1", "prettier": "^3.6.2", + "quicktype": "^23.2.6", "rehype-parse": "^9.0.1", "rehype-stringify": "^10.0.1", "remark-parse": "^11.0.0", diff --git a/packages/web-features/package.json b/packages/web-features/package.json index 6c3407c218e..f33ffafe519 100644 --- a/packages/web-features/package.json +++ b/packages/web-features/package.json @@ -18,12 +18,14 @@ "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": "^20.19.11", 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 } } diff --git a/schemas/data.schema.json b/schemas/data.schema.json index 2d34661eed7..1d4f5f899d1 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", "snapshots"], + "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,248 +114,136 @@ "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)" + "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", "status"], + "additionalProperties": false + }, + "GroupData": { + "type": "object", + "properties": { "name": { "description": "Short name", "type": "string" }, - "snapshot": { - "anyOf": [ - { + "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" }, - { - "items": { - "type": "string" - }, - "minItems": 2, - "type": "array" - } - ], - "description": "Snapshot identifier(s)" - }, - "spec": { - "anyOf": [ - { + "chrome_android": { "type": "string" }, - { - "items": { - "type": "string" - }, - "minItems": 2, - "type": "array" - } - ], - "description": "Specification URL(s)" - }, - "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" - ] + "edge": { + "type": "string" }, - "baseline_high_date": { - "description": "Date the feature achieved Baseline high status", + "firefox": { "type": "string" }, - "baseline_low_date": { - "description": "Date the feature achieved Baseline low status", + "firefox_android": { "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" + "safari": { + "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" + "safari_ios": { + "type": "string" } }, - "required": [ - "baseline", - "support" - ], - "type": "object" + "additionalProperties": false } }, - "required": [ - "name", - "description", - "description_html", - "spec", - "status" - ], - "type": "object" + "required": ["baseline", "support"] }, - "GroupData": { - "additionalProperties": false, + "StatusHeadline": { + "type": "object", + "allOf": [{ "$ref": "#/definitions/Status" }], "properties": { - "name": { - "description": "Short name", - "type": "string" - }, - "parent": { - "description": "Identifier of parent group", - "type": "string" + "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", + "additionalProperties": { + "$ref": "#/definitions/Status" + } } }, - "required": [ - "name" - ], - "type": "object" + "required": ["baseline", "support"], + "additionalProperties": false }, "Release": { - "additionalProperties": false, "description": "Browser release information", + "type": "object", "properties": { - "date": { - "description": "The release date, as in \"2023-12-11\"", - "type": "string" - }, "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" - ], - "type": "object" + "required": ["version", "date"], + "additionalProperties": false }, "SnapshotData": { - "additionalProperties": false, + "type": "object", "properties": { "name": { "description": "Short name", @@ -306,84 +251,56 @@ }, "spec": { "description": "Specification", - "type": "string" + "$ref": "#/definitions/URL" } }, - "required": [ - "name", - "spec" - ], - "type": "object" + "required": ["name", "spec"], + "additionalProperties": false }, - "WebFeaturesData": { - "additionalProperties": false, - "properties": { - "browsers": { - "additionalProperties": false, - "description": "Browsers and browser release data", - "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" - ], - "type": "object" - }, - "features": { - "additionalProperties": { - "$ref": "#/definitions/FeatureData" - }, - "description": "Feature identifiers and data", - "type": "object" + "StringOrStrings": { + "oneOf": [ + { + "type": "string" }, - "groups": { - "additionalProperties": { - "$ref": "#/definitions/GroupData" + { + "type": "array", + "items": { + "type": "string" }, - "description": "Group identifiers and data", - "type": "object" + "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" }, - "snapshots": { - "additionalProperties": { - "$ref": "#/definitions/SnapshotData" + { + "type": "array", + "items": { + "type": "string" }, - "description": "Snapshot identifiers and data", - "type": "object" + "minItems": 2 } - }, - "required": [ - "browsers", - "features", - "groups", - "snapshots" - ], - "type": "object" + ] } } -} \ No newline at end of file +} 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."); 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/types.quicktype.ts b/types.quicktype.ts new file mode 100644 index 00000000000..a62d87701e3 --- /dev/null +++ b/types.quicktype.ts @@ -0,0 +1,206 @@ +/** + * 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; +} + +/** + * 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; + /** + * 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 }; + /** + * Browser versions that most-recently introduced the feature + */ + support: Support; +} + +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; +} diff --git a/types.ts b/types.ts index d1492262104..a0cb96cb7d8 100644 --- a/types.ts +++ b/types.ts @@ -1,97 +1,96 @@ -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 }; -} +/* eslint-disable @typescript-eslint/no-unused-vars */ +// Quicktype produces 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, + Discouraged, + GroupData, + FeatureData as QuicktypeMonolithicFeatureData, + Status as QuicktypeStatus, + StatusHeadline as QuicktypeStatusHeadline, + WebFeaturesData as QuicktypeWebFeaturesData, + Release, + SnapshotData, + Support, +} from "./types.quicktype"; -/** 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[]; -} +// Passthrough types +export type { + BaselineHighLow, + BrowserData, + Browsers, + Discouraged, + GroupData, + Release, + SnapshotData, + Support, +}; -/** 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; +export interface Status extends QuicktypeStatus { + baseline: false | BaselineHighLow; } -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 SupportStatus extends QuicktypeStatusHeadline { + baseline: false | BaselineHighLow; } -type BrowserIdentifier = "chrome" | "chrome_android" | "edge" | "firefox" | "firefox_android" | "safari" | "safari_ios"; +// These are "tests" for our type definitions. +const badQuicktypeStatusHeadline: QuicktypeStatusHeadline = { + baseline: true, // This is an improper value in our actual published data + support: {}, +}; +const badQuicktypeStatus: QuicktypeStatus = badQuicktypeStatusHeadline; -type BaselineHighLow = "high" | "low"; +const badSupportStatus: SupportStatus = { + // This validates that we're actually overriding Quicktype (and correctly). If + // `baseline: true` ever becomes possible in the `SupportStatus`, then + // TypeScript will complain about the next line. + // @ts-expect-error + baseline: true, + support: {}, +}; +const badStatus: Status = { + // @ts-expect-error + baseline: true, + support: {}, +}; +const goodSupportStatus: QuicktypeStatusHeadline | SupportStatus = { + 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; - }; +export interface WebFeaturesData + extends Pick { + features: { [key: string]: FeatureData }; } -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 -} +export type FeatureData = Required< + Pick< + QuicktypeMonolithicFeatureData, + "description_html" | "description" | "name" | "spec" | "status" + > +> & + Partial< + Pick< + QuicktypeMonolithicFeatureData, + "caniuse" | "compat_features" | "discouraged" + > + >; -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. -} +const goodFeatureData: FeatureData = { + name: "Test", + description: "Hi", + description_html: "Hi", + spec: "", + status: { + baseline: false, + support: {}, + }, +}; -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 0d40182666c5741e59b06ed18f11d82496c83a7f Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Fri, 12 Sep 2025 12:48:18 +0200 Subject: [PATCH 2/6] Add move and split redirects to the schema (#3000) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: James C Scott III <7788930+jcscottiii@users.noreply.github.com> Co-authored-by: Philip Jägenstedt --- assertions.test.ts | 36 +++++++ assertions.ts | 29 ++++++ features/conic-gradients.yml | 1 + features/conic-gradients.yml.dist | 12 +++ features/gradients.yml | 4 + features/gradients.yml.dist | 15 +++ features/numeric-separators.yml | 6 ++ features/numeric-separators.yml.dist | 17 ++++ features/numeric-seperators.yml | 8 +- features/numeric-seperators.yml.dist | 19 +--- features/single-color-gradients.yml | 15 +-- features/single-color-gradients.yml.dist | 25 ++--- index.ts | 34 +++++-- package-lock.json | 5 +- package.json | 4 +- packages/compute-baseline/package.json | 4 +- schemas/data.schema.json | 117 ++++++++++++++++------- scripts/build.ts | 5 + scripts/dist.ts | 39 ++++++++ scripts/find-ranged-headline-statuses.ts | 10 +- scripts/inspect-feature.ts | 16 ++++ scripts/specs.ts | 5 + scripts/stats.ts | 15 ++- scripts/update-drafts.ts | 12 ++- type-guards.ts | 13 +++ types.quicktype.ts | 25 ++++- types.ts | 42 +++++++- 27 files changed, 420 insertions(+), 113 deletions(-) create mode 100644 assertions.test.ts create mode 100644 assertions.ts create mode 100644 features/numeric-separators.yml create mode 100644 features/numeric-separators.yml.dist create mode 100644 type-guards.ts diff --git a/assertions.test.ts b/assertions.test.ts new file mode 100644 index 00000000000..2ff81193779 --- /dev/null +++ b/assertions.test.ts @@ -0,0 +1,36 @@ +import assert from "node:assert/strict"; +import { assertValidFeatureReference } from "./assertions"; + +describe("assertValidReference()", function () { + it("throws if target ID is a move", function () { + assert.throws(() => { + assertValidFeatureReference("a", "some-moving-feature", { + "some-moving-feature": { kind: "moved" }, + }); + }); + }); + + it("throws if target ID is a split", function () { + assert.throws(() => { + assertValidFeatureReference("a", "some-split-feature", { + "some-split-feature": { kind: "split" }, + }); + }); + }); + + it("throws if target ID is not defined", function () { + assert.throws(() => { + assertValidFeatureReference( + "a", + "this-is-a-completely-invalid-feature", + {}, + ); + }); + }); + + it("does not throw if target ID is a feature", function () { + assert.doesNotThrow(() => { + assertValidFeatureReference("a", "dom", { dom: { kind: "feature" } }); + }); + }); +}); diff --git a/assertions.ts b/assertions.ts new file mode 100644 index 00000000000..90d7a546f16 --- /dev/null +++ b/assertions.ts @@ -0,0 +1,29 @@ +import { isOrdinaryFeatureData } from "./type-guards"; +import { WebFeaturesData } from "./types.quicktype"; + +/** + * Assert that a reference from one feature to another is an ordinary feature + * reference (i.e., it's defined and not some kind of redirect). + * + * @export + * @param {string} sourceId The feature that is referencing another feature + * @param {string} targetId The feature being referenced + * @param {WebFeaturesData["features"]} features Feature data + */ +export function assertValidFeatureReference( + sourceId: string, + targetId: string, + features: WebFeaturesData["features"], +) { + const target: unknown = features[targetId]; + if (target === undefined) { + throw new Error(`${sourceId} references a non-existent feature`); + } + if (!isOrdinaryFeatureData(target)) { + throw new Error( + `${sourceId} references a redirect "${targetId} instead of an ordinary feature ID`, + ); + } +} + +// TODO: assertValidSnapshotReference 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..ff1a7cb8638 100644 --- a/features/conic-gradients.yml.dist +++ b/features/conic-gradients.yml.dist @@ -41,3 +41,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..625d60b9b77 100644 --- a/features/gradients.yml.dist +++ b/features/gradients.yml.dist @@ -145,3 +145,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/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..04c6806c7fd 100644 --- a/features/numeric-seperators.yml +++ b/features/numeric-seperators.yml @@ -1,6 +1,2 @@ -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 +kind: moved +redirect_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 +{} 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/index.ts b/index.ts index 3cefc981622..c7f7789bd5d 100644 --- a/index.ts +++ b/index.ts @@ -5,10 +5,12 @@ import { Temporal } from '@js-temporal/polyfill'; import { fdir } from 'fdir'; import YAML from 'yaml'; import { convertMarkdown } from "./text"; -import { FeatureData, GroupData, SnapshotData, WebFeaturesData } from './types'; +import { GroupData, SnapshotData, WebFeaturesData } from './types'; import { BASELINE_LOW_TO_HIGH_DURATION, coreBrowserSet, parseRangedDateString } from 'compute-baseline'; import { Compat } from 'compute-baseline/browser-compat-data'; +import { assertValidFeatureReference } from './assertions'; +import { isMoved, isSplit } from './type-guards'; // The longest name allowed, to allow for compact display. const nameMaxLength = 80; @@ -120,7 +122,7 @@ function* identifiers(value) { // 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]) { @@ -130,6 +132,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); @@ -185,12 +192,25 @@ for (const [key, data] of yamlEntries('features')) { features[key] = data; } -// Assert that discouraged feature's alternatives 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`); - } + const { kind } = feature; + switch (kind) { + case "feature": + for (const alternative of feature.discouraged?.alternatives ?? []) { + assertValidFeatureReference(id, alternative, features) + } + break; + case "moved": + assertValidFeatureReference(id, feature.redirect_target, features); + break; + case "split": + for (const target of feature.redirect_targets) { + assertValidFeatureReference(id, target, features); + } + break; + default: + kind satisfies never; + throw new Error(`Unhandled feature kind ${kind}}`); } } diff --git a/package-lock.json b/package-lock.json index 2615f0a7583..9b34e8ce5c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "fast-json-stable-stringify": "^2.1.0", "fdir": "^6.5.0", "hast-util-to-string": "^3.0.1", + "mocha": "^11.7.2", "prettier": "^3.6.2", "quicktype": "^23.2.6", "rehype-parse": "^9.0.1", @@ -6581,11 +6582,9 @@ "devDependencies": { "@types/chai": "^5.2.2", "@types/chai-jest-snapshot": "^1.3.8", - "@types/mocha": "^10.0.10", "c8": "^10.1.3", "chai": "^6.0.1", - "chai-jest-snapshot": "^2.0.0", - "mocha": "^11.7.2" + "chai-jest-snapshot": "^2.0.0" }, "peerDependencies": { "@mdn/browser-compat-data": "^7.0.0" diff --git a/package.json b/package.json index c12007cac7b..db2f6a30b92 100644 --- a/package.json +++ b/package.json @@ -29,11 +29,12 @@ "test:coverage": "npm run --workspaces test:coverage", "test:dist": "tsx scripts/dist.ts --check", "test:format": "prettier --check .", + "test:index": "mocha -r tsx './*.test.ts'", "test:lint": "npx eslint .", "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: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", + "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:index && npm run test:dist && npm 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" }, @@ -53,6 +54,7 @@ "fast-json-stable-stringify": "^2.1.0", "fdir": "^6.5.0", "hast-util-to-string": "^3.0.1", + "mocha": "^11.7.2", "prettier": "^3.6.2", "quicktype": "^23.2.6", "rehype-parse": "^9.0.1", diff --git a/packages/compute-baseline/package.json b/packages/compute-baseline/package.json index 8fcf340d2fb..fa1abe85965 100644 --- a/packages/compute-baseline/package.json +++ b/packages/compute-baseline/package.json @@ -28,11 +28,9 @@ "devDependencies": { "@types/chai": "^5.2.2", "@types/chai-jest-snapshot": "^1.3.8", - "@types/mocha": "^10.0.10", "c8": "^10.1.3", "chai": "^6.0.1", - "chai-jest-snapshot": "^2.0.0", - "mocha": "^11.7.2" + "chai-jest-snapshot": "^2.0.0" }, "peerDependencies": { "@mdn/browser-compat-data": "^7.0.0" diff --git a/schemas/data.schema.json b/schemas/data.schema.json index 1d4f5f899d1..7ee7b28da67 100644 --- a/schemas/data.schema.json +++ b/schemas/data.schema.json @@ -44,7 +44,18 @@ "description": "Feature identifiers and data", "type": "object", "additionalProperties": { - "$ref": "#/definitions/FeatureData" + "oneOf": [ + { + "$ref": "#/definitions/FeatureData" + }, + { + "$ref": "#/definitions/FeatureMovedData" + }, + { + "$ref": "#/definitions/FeatureSplitData" + } + ], + "$comment": "Use the `kind` property as a discriminator." } }, "groups": { @@ -102,6 +113,9 @@ "description": "A feature data entry", "type": "object", "properties": { + "kind": { + "const": "feature" + }, "name": { "description": "Short name", "type": "string" @@ -143,7 +157,44 @@ "$ref": "#/definitions/Discouraged" } }, - "required": ["name", "description", "description_html", "spec", "status"], + "required": [ + "kind", + "name", + "description", + "description_html", + "spec", + "status" + ], + "additionalProperties": false + }, + "FeatureMovedData": { + "description": "A feature has permanently moved to exactly one other ID", + "type": "object", + "properties": { + "kind": { + "const": "moved" + }, + "redirect_target": { + "description": "The new ID for this feature", + "type": "string" + } + }, + "required": ["kind", "redirect_target"], + "additionalProperties": false + }, + "FeatureSplitData": { + "description": "A feature has split into two or more other features", + "type": "object", + "properties": { + "kind": { + "const": "split" + }, + "redirect_targets": { + "description": "The new IDs for this feature", + "$ref": "#/definitions/Strings" + } + }, + "required": ["kind", "redirect_targets"], "additionalProperties": false }, "GroupData": { @@ -161,6 +212,37 @@ "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 + }, + "SnapshotData": { + "type": "object", + "properties": { + "name": { + "description": "Short name", + "type": "string" + }, + "spec": { + "description": "Specification", + "type": "string" + } + }, + "required": ["name", "spec"], + "additionalProperties": false + }, "Status": { "type": "object", "properties": { @@ -226,37 +308,6 @@ "required": ["baseline", "support"], "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 - }, - "SnapshotData": { - "type": "object", - "properties": { - "name": { - "description": "Short name", - "type": "string" - }, - "spec": { - "description": "Specification", - "$ref": "#/definitions/URL" - } - }, - "required": ["name", "spec"], - "additionalProperties": false - }, "StringOrStrings": { "oneOf": [ { diff --git a/scripts/build.ts b/scripts/build.ts index 3d1a883bbec..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,6 +69,10 @@ function buildPackage() { function buildExtendedJSON() { for (const [id, featureData] of Object.entries(data.features)) { + if (!isOrdinaryFeatureData(featureData)) { + continue; + } + if ( Array.isArray(featureData.compat_features) && featureData.compat_features.length && diff --git a/scripts/dist.ts b/scripts/dist.ts index 209aa1d154a..1832f6a4b89 100644 --- a/scripts/dist.ts +++ b/scripts/dist.ts @@ -14,6 +14,7 @@ import { isDeepStrictEqual } from "node:util"; import winston from "winston"; import YAML, { Document, Scalar, YAMLSeq } from "yaml"; import yargs from "yargs"; +import type { FeatureData, FeatureMovedData, FeatureSplitData } from "../types"; const compat = new Compat(); @@ -181,6 +182,39 @@ function compareStatus(a: SupportStatus, b: SupportStatus) { return 0; } +function toRedirectDist( + id: string, + source: FeatureMovedData | FeatureSplitData, +): YAML.Document { + const dist = new Document({}); + + const comment = [ + `Generated from: ${id}.yml`, + `This file intentionally left blank.`, + `Do not edit this file.`, + ]; + + const { kind } = source; + switch (kind) { + case "moved": + comment.push( + `The data for this feature has moved to ${source.redirect_target}.yml`, + ); + break; + case "split": + comment.push(`The data for this feature has moved to:`); + comment.push(...source.redirect_targets.map((dest) => ` - ${dest}.yml`)); + break; + default: + kind satisfies never; + throw new Error(`Unhandled feature kind ${kind}}`); + } + + dist.commentBefore = comment.map((line) => ` ${line}`).join("\n"); + + return dist; +} + /** * Generate a dist YAML document from a feature definition YAML file path. * @@ -192,6 +226,11 @@ function toDist(sourcePath: string): YAML.Document { const source = YAML.parse(fs.readFileSync(sourcePath, { encoding: "utf-8" })); const { name: id } = path.parse(sourcePath); + 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. 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/inspect-feature.ts b/scripts/inspect-feature.ts index 7c7c9e97f07..121dca044cf 100644 --- a/scripts/inspect-feature.ts +++ b/scripts/inspect-feature.ts @@ -3,6 +3,7 @@ import escapeHtml from "escape-html"; import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; +import winston from "winston"; import YAML from "yaml"; import yargs from "yargs"; import { features } from ".."; @@ -33,6 +34,15 @@ const argv = yargs(process.argv.slice(2)) defaultDescription: "warn", }).argv as Args; +const logger = winston.createLogger({ + level: argv.verbose > 0 ? "debug" : "warn", + format: winston.format.combine( + winston.format.colorize(), + winston.format.simple(), + ), + transports: new winston.transports.Console(), +}); + function main(): void { for (const filePath of argv.paths) { const { name, ext, dir } = path.parse(filePath); @@ -45,6 +55,12 @@ function main(): void { if (!feature) { throw new Error(`No feature found for ID ${id}`); } + if (feature.kind === "moved" || feature.kind === "split") { + logger.warn( + `${id} is a ${feature.kind} feature. Did you mean to inspect this?`, + ); + continue; + } const { compat_features } = feature; diff --git a/scripts/specs.ts b/scripts/specs.ts index a2372fc5e9e..c1e7284ff28 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 { 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 @@ -195,6 +196,10 @@ for (const [allowedUrl, message] of defaultAllowlist) { } for (const [id, data] of Object.entries(features)) { + if (!isOrdinaryFeatureData(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..806a88139d4 --- /dev/null +++ b/type-guards.ts @@ -0,0 +1,13 @@ +import type { FeatureData, FeatureMovedData, FeatureSplitData } from "./types"; + +export function isOrdinaryFeatureData(x: unknown): x is FeatureData { + return typeof x === "object" && "kind" in x && x.kind === "feature"; +} + +export function isSplit(x: unknown): x is FeatureSplitData { + return typeof x === "object" && "kind" in x && x.kind === "split"; +} + +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 a62d87701e3..abf8e3474c5 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 */ @@ -86,10 +90,11 @@ export interface FeatureData { * Group identifier(s) */ group?: string[] | string; + kind: Kind; /** * Short name */ - name: string; + name?: string; /** * Snapshot identifier(s) */ @@ -97,12 +102,20 @@ 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; + status?: StatusHeadline; + /** + * The new ID for this feature + */ + redirect_target?: string; + /** + * The new IDs for this feature + */ + redirect_targets?: string[]; } /** @@ -120,6 +133,8 @@ export interface Discouraged { alternatives?: string[]; } +export type Kind = "feature" | "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 a0cb96cb7d8..de0c7ad2fbe 100644 --- a/types.ts +++ b/types.ts @@ -10,6 +10,7 @@ import type { Browsers, Discouraged, GroupData, + Kind, FeatureData as QuicktypeMonolithicFeatureData, Status as QuicktypeStatus, StatusHeadline as QuicktypeStatusHeadline, @@ -66,10 +67,12 @@ const goodSupportStatus: QuicktypeStatusHeadline | SupportStatus = { export interface WebFeaturesData extends Pick { - features: { [key: string]: FeatureData }; + features: { + [key: string]: FeatureData | FeatureMovedData | FeatureSplitData; + }; } -export type FeatureData = Required< +export type FeatureData = { kind: "feature" } & Required< Pick< QuicktypeMonolithicFeatureData, "description_html" | "description" | "name" | "spec" | "status" @@ -83,6 +86,7 @@ export type FeatureData = Required< >; const goodFeatureData: FeatureData = { + kind: "feature", name: "Test", description: "Hi", description_html: "Hi", @@ -93,4 +97,38 @@ const goodFeatureData: FeatureData = { }, }; +type FeatureRedirectData = { kind: Exclude } & Required< + Pick +>; + +export interface FeatureMovedData + extends Omit { + kind: "moved"; +} + +const goodFeatureMovedData: FeatureMovedData = { + kind: "moved", + redirect_target: "", +}; +const badFeatureMovedData: FeatureMovedData = { + kind: "moved", + // @ts-expect-error + redirect_targets: ["", ""], +}; + +export interface FeatureSplitData + extends Omit { + kind: "split"; +} + +const goodFeatureSplitData: FeatureSplitData = { + kind: "split", + redirect_targets: ["", ""], +}; +const badFeatureSplitData: FeatureSplitData = { + kind: "split", + // @ts-expect-error + redirect_target: "", +}; + export type BrowserIdentifier = keyof Browsers; From 15dfc9af981eb9cb0f76d54765747f4e44943427 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Tue, 16 Sep 2025 13:43:15 +0200 Subject: [PATCH 3/6] Add guidelines for moving and splitting features (#3180) Co-authored-by: Patrick Brosset --- docs/guidelines.md | 77 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/docs/guidelines.md b/docs/guidelines.md index 181feb12705..fae24d70be0 100644 --- a/docs/guidelines.md +++ b/docs/guidelines.md @@ -77,6 +77,81 @@ The identifier should match the name, with these additional guidelines: - 👍 Recommended: `user-pseudos` - 👎 Not recommended: `user-valid-and-user-invalid` +### Move a feature to a new ID + +It’s possible to change or substitute a feature’s ID by creating a redirect from the original ID pointing to a new ID. +You can do this when: + +* The original feature ID is misspelled. + For example, `numeric-seperators` (note spelling) data can be moved to `numeric-separators`. + +* The original feature ID breaks the identifier guidelines. + For example, `drones-initial-support` data can be moved to `drones`. + +* The original feature should not have existed as an independent feature. + For example, `single-color-gradients` was a poorly-conceived feature, where [a specification change](https://github.com/w3c/csswg-drafts/issues/10092) simplified the specification, implementation, and tools, but produced no novel browser behavior that developers could use in an application. + Instead, all the compatibility keys for the feature were reassigned to `gradients`. + +* Data consumers report that the original feature ID is confusing or misleading. + +You must not do this when the feature has been superseded, such that the feature's name has changed and the exposed behaviors or API surface have changed (in shipping browsers, up to and including unshipping). +Instead, use [`discouraged` data](#discouraged) with one or more `alternatives`. + +To move the feature: + +1. If applicable, move the existing YAML files for the feature to the target ID filename. + For example, rename `features/numeric-seperators.yml` to `features/numeric-separators.yml`. + + If the original feature is being replaced by another feature, then move on to the next step. + +2. Create a new YAML file for the original target ID filename. + For example, create an empty file `features/numeric-seperators.yml`. + +3. Populate the following data in the new YAML file: + + ```yaml + kind: moved + redirect_target: TARGET-ID + ``` + + where `TARGET-ID` is the target ID. + +4. Regenerate the dist files. + Run `npm run dist`. + +5. Commit your work and open a pull request. + +### Split a feature into two or more other features + +Some features may need to be split in two or more parts. +You can do this when the original feature should not have existed as an independent feature in the first place. +For example, similarly-named compat keys that ought to have been additions to existing features were erroneously combined and assigned to a new feature. + +To split the feature: + +1. If the feature to be split has any keys listed in `compat_features`, then reassign the keys to the target features. + + To get the list of keys, you may need to first run `npm run undist -- $feature` where `$feature` is the path to the YAML file of the feature to be split. + +2. Replace the contents of the original feature YAML file with the following data: + + ```yaml + kind: split + redirect_targets: + - target-id1 + - target-id2 + ``` + + Replace the `target-id` values with two or more target ID strings. + Order `redirect_targets` by the most widely-relevant features first. + For example, if the feature is split to separate a Baseline subset of a feature from a non-Baseline subset, then put the Baseline feature first. + If you must break a tie, use alphabetical order. + +3. Regenerate the dist files. + Run `npm run dist`. + +4. Commit your work and open a pull request. + ## Descriptions The `description` field contains a short description of the feature in Markdown-formatted text, which is converted to HTML in the published package. @@ -345,6 +420,6 @@ When you set a `discouraged` block in a feature file, do: If possible, use the single most broadly applicable reference, such as specification text. If a feature is removed from a specification, link to an issue, pull request, or commit showing the removal. -- Set one or more (optional) `alternative` feature IDs that are whole or partial substitutes for the discouraged feature. +- Set one or more (optional) `alternatives` feature IDs that are whole or partial substitutes for the discouraged feature. An alternative doesn't have to be a narrow drop-in replacement for the discouraged feature but it must handle some use case of the discouraged feature. Guide developers to the most relevant features that would help them stop using the discouraged feature. From b3b99ea7f7411370ec0c890b5031381e32ded402 Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Tue, 16 Sep 2025 13:43:56 +0200 Subject: [PATCH 4/6] Add consumer docs for moved and split features (#3181) Co-authored-by: Patrick Brosset --- packages/web-features/README.md | 75 +++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/packages/web-features/README.md b/packages/web-features/README.md index 929a6b82790..af40a7bda27 100644 --- a/packages/web-features/README.md +++ b/packages/web-features/README.md @@ -52,3 +52,78 @@ For browser support iconography (that is, browser logos and checkmarks and Xs), If you wish to report browser version numbers, avoid showing version numbers alone. Developers and users often do not know whether a version number refers to a very recent or old release. If you must show a version number, consider contextualizing that number by showing a release date, a relative date (such as "Released … years ago"), an offset (such as "… releases ago"), or usage statistics relevant to your audience (such as "…% of your visitors in the last 90 days"). + +## Schema reference + +This part of the README summarizes the schema for feature data. +See `data.schema.json` for a canonical reference. + +## `features` + +The `features` object contains data for features. +Each key is a feature ID string and values describe the feature. +Most values are ordinary feature objects with names, descriptions, and other data. +Some features contain redirects to other features. +You can distinguish between ordinary feature objects and redirects by using the `kind` property: + +* `"feature"` — ordinary features +* `"moved"` — the feature has a redirect to a new key +* `"split"` — the feature has a redirect to two or more keys + +### Feature objects + +A feature with the `kind` set to `"feature"` is an ordinary feature. +It has the following properties: + +- `kind` (value: `"feature"`): A type discriminator +- `name` (type: `string`): A plain-text human-readable name for the feature +- `description` (type: `string`): A short plain-text description of the feature +- `description_html` (type: `string`): A short HTML-formatted description of the feature +- `spec` (type: `string | string[]`): A specification URL or an array of them +- `status`: Support status data. + It has the following properties: + + - `baseline` (type: `"high" | "low" | false`): Whether the feature Baseline widely available, Baseline newly available, or not Baseline + - `baseline_low_date` (optional, type: `string`): When the feature reached Baseline newly available status + - `baseline_high_date` (optional, type: `string`): When the feature reached Baseline widely available status + - `support`: An object representing per-browser support information, showing the version number where each browser first started to support that feature. + All keys are optional. + Keys are one of: `"chrome"`, `"chrome_android"`, `"edge"`, `"firefox"`, `"firefox"`, `"firefox_android"`, `"safari"`, `"safari_ios"`. + Each value is a `string` containing the version number. + +- `group` (optional, type: `string | string[]`): A `groups` key or an array of them +- `snapshot` (optional, type: `string | string[]`): A `snapshots` key or an array of them +- `caniuse` (optional, type: `string | string[]`): A caniuse feature ID that corresponds to the current feature, or an array of them. + Use it to look up caniuse data from a package like [`caniuse-lite`](https://www.npmjs.com/package/caniuse-lite) or construct a URL to a page on caniuse.com. +- `compat_features` (optional, type: `string[]`): An array of `@mdn/browser-compat-data` feature key strings. +- `discouraged` (optional): An object indicating that web developers should avoid using the feature. + It has the following properties: + + - `according_to` (type: `string[]`): One or more links to a formal discouragement notice, such as specification text or an intent-to-unship + - `alternatives` (optional, type: `string[]`): One or more feature IDs (as in `features[alternatives[0]]`) that substitute some or all of this feature's utility + +### Moved objects + +A feature with the `kind` set to `"moved"` is a redirect to another feature. +It says that this feature ID is actually best represented by the data given by another ID. +If you’re showing web-features data to developers, then treat this like an HTTP 301 redirect and go directly to the feature it points to. + +A moved feature has the following properties: + +- `kind` (value: `"moved"`): A type discriminator +- `redirect_target` (type: `string`): The ID of a feature (as in `features[redirect_target]`). + The ID is guaranteed to be an ordinary, non-redirecting feature. + Double redirects and cycles are forbidden. + +### Split objects + +A feature with the `kind` set to `"split"` is a redirect to multiple other features. +It says that this feature ID is actually best represented by the data given by multiple other features and you (or your users) will have to make a choice about what to do. +You can think of this kind of feature like a [Wikipedia disambiguation page](https://en.wikipedia.org/wiki/Joker). + +A split feature has the following properties: + +- `kind` (value: `"split"`): A type discriminator +- `redirect_targets` (type: `string[]`): An array of two or more feature IDs, in order of greatest relevance or importance (as judged by the web-features maintainers). + The IDs are guaranteed to be ordinary, non-redirecting features. + Double redirects and cycles are forbidden. From 7b71997794067f64616a94864a8d74ab9fb2a23a Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Tue, 16 Sep 2025 13:52:53 +0200 Subject: [PATCH 5/6] Change schema `string | string[]` properties to `string[]` (#3184) --- index.ts | 22 +++++++++++++ packages/web-features/README.md | 8 ++--- schemas/data.schema.json | 56 ++++++--------------------------- types.quicktype.ts | 16 +++++----- types.ts | 2 +- 5 files changed, 44 insertions(+), 60 deletions(-) diff --git a/index.ts b/index.ts index c7f7789bd5d..c9dfc28ca5a 100644 --- a/index.ts +++ b/index.ts @@ -137,6 +137,28 @@ for (const [key, data] of yamlEntries('features')) { data.kind = "feature"; } + // Upgrade authored strings to arrays of 1 + const optionalArrays = [ + "spec", + "group", + "snapshot", + "caniuse", + "foo" + ]; + const stringToStringArray = (value: string | string[]) => typeof value === "string" ? [value] : value; + for (const optionalArray of optionalArrays) { + const value = data[optionalArray]; + if (value) { + data[optionalArray] = stringToStringArray(value); + } + } + if (data.discouraged) { + const value = data.discouraged.according_to; + if (value) { + data.discouraged.according_to = stringToStringArray(value); + } + } + // Convert markdown to text+HTML. if (data.description) { const { text, html } = convertMarkdown(data.description); diff --git a/packages/web-features/README.md b/packages/web-features/README.md index af40a7bda27..b62037daf97 100644 --- a/packages/web-features/README.md +++ b/packages/web-features/README.md @@ -79,7 +79,7 @@ It has the following properties: - `name` (type: `string`): A plain-text human-readable name for the feature - `description` (type: `string`): A short plain-text description of the feature - `description_html` (type: `string`): A short HTML-formatted description of the feature -- `spec` (type: `string | string[]`): A specification URL or an array of them +- `spec` (type: `string[]`): A specification URL or an array of them - `status`: Support status data. It has the following properties: @@ -91,9 +91,9 @@ It has the following properties: Keys are one of: `"chrome"`, `"chrome_android"`, `"edge"`, `"firefox"`, `"firefox"`, `"firefox_android"`, `"safari"`, `"safari_ios"`. Each value is a `string` containing the version number. -- `group` (optional, type: `string | string[]`): A `groups` key or an array of them -- `snapshot` (optional, type: `string | string[]`): A `snapshots` key or an array of them -- `caniuse` (optional, type: `string | string[]`): A caniuse feature ID that corresponds to the current feature, or an array of them. +- `group` (optional, type: `string[]`): A `groups` key or an array of them +- `snapshot` (optional, type: `string[]`): A `snapshots` key or an array of them +- `caniuse` (optional, type: `string[]`): A caniuse feature ID that corresponds to the current feature, or an array of them. Use it to look up caniuse data from a package like [`caniuse-lite`](https://www.npmjs.com/package/caniuse-lite) or construct a URL to a page on caniuse.com. - `compat_features` (optional, type: `string[]`): An array of `@mdn/browser-compat-data` feature key strings. - `discouraged` (optional): An object indicating that web developers should avoid using the feature. diff --git a/schemas/data.schema.json b/schemas/data.schema.json index 7ee7b28da67..dcf66bcf8cc 100644 --- a/schemas/data.schema.json +++ b/schemas/data.schema.json @@ -81,7 +81,7 @@ "properties": { "according_to": { "description": "Links to a formal discouragement notice, such as specification text, intent-to-unship, etc.", - "$ref": "#/definitions/URLs" + "$ref": "#/definitions/Strings" }, "alternatives": { "description": "IDs for features that substitute some or all of this feature's utility", @@ -129,20 +129,20 @@ "type": "string" }, "spec": { - "description": "Specification URL(s)", - "$ref": "#/definitions/URLOrURLs" + "description": "Specification URLs", + "$ref": "#/definitions/Strings" }, "group": { - "description": "Group identifier(s)", - "$ref": "#/definitions/StringOrStrings" + "description": "Group identifiers", + "$ref": "#/definitions/Strings" }, "snapshot": { - "description": "Snapshot identifier(s)", - "$ref": "#/definitions/StringOrStrings" + "description": "Snapshot identifiers", + "$ref": "#/definitions/Strings" }, "caniuse": { - "description": "caniuse.com identifier(s)", - "$ref": "#/definitions/StringOrStrings" + "description": "caniuse.com identifiers", + "$ref": "#/definitions/Strings" }, "compat_features": { "description": "Sources of support data for this feature", @@ -308,50 +308,12 @@ "required": ["baseline", "support"], "additionalProperties": false }, - "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/types.quicktype.ts b/types.quicktype.ts index abf8e3474c5..9ee64a5766f 100644 --- a/types.quicktype.ts +++ b/types.quicktype.ts @@ -67,9 +67,9 @@ export interface Release { */ export interface FeatureData { /** - * caniuse.com identifier(s) + * caniuse.com identifiers */ - caniuse?: string[] | string; + caniuse?: string[]; /** * Sources of support data for this feature */ @@ -87,22 +87,22 @@ export interface FeatureData { */ discouraged?: Discouraged; /** - * Group identifier(s) + * Group identifiers */ - group?: string[] | string; + group?: string[]; kind: Kind; /** * Short name */ name?: string; /** - * Snapshot identifier(s) + * Snapshot identifiers */ - snapshot?: string[] | string; + snapshot?: string[]; /** - * Specification URL(s) + * Specification URLs */ - spec?: string[] | string; + spec?: string[]; /** * 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 de0c7ad2fbe..be0b028c99b 100644 --- a/types.ts +++ b/types.ts @@ -90,7 +90,7 @@ const goodFeatureData: FeatureData = { name: "Test", description: "Hi", description_html: "Hi", - spec: "", + spec: [""], status: { baseline: false, support: {}, From 30bf162f23cc2a494cb3a09208c037fc113286be Mon Sep 17 00:00:00 2001 From: "Daniel D. Beck" Date: Tue, 16 Sep 2025 14:02:18 +0200 Subject: [PATCH 6/6] Fix missing `group` and `snapshot` keys from convenience types --- types.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/types.ts b/types.ts index be0b028c99b..c14c124ac4a 100644 --- a/types.ts +++ b/types.ts @@ -81,7 +81,7 @@ export type FeatureData = { kind: "feature" } & Required< Partial< Pick< QuicktypeMonolithicFeatureData, - "caniuse" | "compat_features" | "discouraged" + "caniuse" | "compat_features" | "discouraged" | "group" | "snapshot" > >; @@ -91,6 +91,13 @@ const goodFeatureData: FeatureData = { description: "Hi", description_html: "Hi", spec: [""], + snapshot: [""], + group: [""], + caniuse: [""], + discouraged: { + according_to: [""], + alternatives: [""], + }, status: { baseline: false, support: {},