From 6f1be0081986c3ab6b400c80dfb52e07e5062360 Mon Sep 17 00:00:00 2001 From: Sam Macbeth Date: Thu, 12 Sep 2024 11:33:02 +0200 Subject: [PATCH 01/20] Define config output schema in TS and validate with JSONSchema --- package-lock.json | 373 +++++++++++++++++++++++++++++++++++++------ package.json | 3 +- schema/config.d.ts | 34 ++++ schema/cookie.d.ts | 24 +++ schema/feature.d.ts | 34 ++++ scripts/validate.mjs | 29 ++++ 6 files changed, 451 insertions(+), 46 deletions(-) create mode 100644 schema/config.d.ts create mode 100644 schema/cookie.d.ts create mode 100644 schema/feature.d.ts create mode 100644 scripts/validate.mjs diff --git a/package-lock.json b/package-lock.json index 10275a05d..82a87edc2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -203,7 +203,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -392,8 +391,7 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/binary-extensions": { "version": "2.2.0", @@ -621,6 +619,14 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "engines": { + "node": ">=18" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -631,7 +637,6 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -802,11 +807,15 @@ "node": ">=6.0.0" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, "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 + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/enquirer": { "version": "2.3.6", @@ -2004,7 +2013,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -2175,8 +2183,21 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } }, "node_modules/js-tokens": { "version": "4.0.0", @@ -2316,6 +2337,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/mocha": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", @@ -2494,7 +2523,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -2677,7 +2705,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -2970,7 +2997,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -2982,7 +3008,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } @@ -3005,6 +3030,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/slice-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", @@ -3065,7 +3101,6 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -3125,12 +3160,23 @@ } }, "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dependencies": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" @@ -3232,6 +3278,79 @@ "node": ">=8.0" } }, + "node_modules/ts-json-schema-generator": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-2.3.0.tgz", + "integrity": "sha512-t4lBQAwZc0sOJq9LJt3NgbznIcslVnm0JeEMFq8qIRklpMRY8jlYD0YmnRWbqBKANxkby91P1XanSSlSOFpUmg==", + "dependencies": { + "@types/json-schema": "^7.0.15", + "commander": "^12.0.0", + "glob": "^10.3.12", + "json5": "^2.2.3", + "normalize-path": "^3.0.0", + "safe-stable-stringify": "^2.4.3", + "tslib": "^2.6.2", + "typescript": "^5.4.5" + }, + "bin": { + "ts-json-schema-generator": "bin/ts-json-schema-generator.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/ts-json-schema-generator/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/ts-json-schema-generator/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ts-json-schema-generator/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/ts-json-schema-generator/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -3244,6 +3363,11 @@ "strip-bom": "^3.0.0" } }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -3392,7 +3516,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -3470,6 +3593,53 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -3725,8 +3895,7 @@ "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 + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, "ansi-styles": { "version": "3.2.1", @@ -3858,8 +4027,7 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "binary-extensions": { "version": "2.2.0", @@ -4030,6 +4198,11 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4040,7 +4213,6 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4152,11 +4324,15 @@ "esutils": "^2.0.2" } }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + }, "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 + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "enquirer": { "version": "2.3.6", @@ -5023,8 +5199,7 @@ "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, "is-glob": { "version": "4.0.3", @@ -5132,8 +5307,16 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } }, "js-tokens": { "version": "4.0.0", @@ -5243,6 +5426,11 @@ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true }, + "minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==" + }, "mocha": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", @@ -5370,8 +5558,7 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, "object-inspect": { "version": "1.13.2", @@ -5496,8 +5683,7 @@ "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" }, "path-parse": { "version": "1.0.7", @@ -5692,7 +5878,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "requires": { "shebang-regex": "^3.0.0" } @@ -5700,8 +5885,7 @@ "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, "side-channel": { "version": "1.0.6", @@ -5715,6 +5899,11 @@ "object-inspect": "^1.13.1" } }, + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" + }, "slice-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", @@ -5762,7 +5951,6 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -5804,12 +5992,19 @@ } }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" + } + }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" } }, "strip-bom": { @@ -5881,6 +6076,57 @@ "is-number": "^7.0.0" } }, + "ts-json-schema-generator": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-2.3.0.tgz", + "integrity": "sha512-t4lBQAwZc0sOJq9LJt3NgbznIcslVnm0JeEMFq8qIRklpMRY8jlYD0YmnRWbqBKANxkby91P1XanSSlSOFpUmg==", + "requires": { + "@types/json-schema": "^7.0.15", + "commander": "^12.0.0", + "glob": "^10.3.12", + "json5": "^2.2.3", + "normalize-path": "^3.0.0", + "safe-stable-stringify": "^2.4.3", + "tslib": "^2.6.2", + "typescript": "^5.4.5" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + } + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -5893,6 +6139,11 @@ "strip-bom": "^3.0.0" } }, + "tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -6002,7 +6253,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "requires": { "isexe": "^2.0.0" } @@ -6082,6 +6332,39 @@ } } }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + } + } + }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", diff --git a/package.json b/package.json index 735a5c65d..a308cbce1 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "lint": "eslint .", "unit-tests": "mocha ./tests/**/*.js", "build": "node index.js", - "format": "node format.js" + "format": "node format.js", + "generate-schema": "ts-json-schema-generator --path 'schema/config.d.ts' --type 'GenericV4Config'" }, "author": "DuckDuckGo", "license": "Apache 2.0", diff --git a/schema/config.d.ts b/schema/config.d.ts new file mode 100644 index 000000000..cc75233b3 --- /dev/null +++ b/schema/config.d.ts @@ -0,0 +1,34 @@ +import { CookieFeature } from "./cookie"; +import { Feature, SiteException } from "./feature"; + +export type V4Config = { + readme: string; + version: number + features: Record> & { + // These features have typed settings + cookie: CookieFeature + }, + unprotectedTemporary: SiteException[], + +} + +/** + * Android: + * - Uses integer version numbers for minSupportedVersion + * - Adds 'experimentalVariants' top level property + */ +export type AndroidV4Config = V4Config & { + experimentalVariants: { + variants: { + desc: string; + variantKey: string; + weight: number; + }[] + } +} + +/** + * Generic spec: covers mac, iOS, windows and extension configs + * - Use string version numbers for minSupportedVersion + */ +export type GenericV4Config = V4Config; diff --git a/schema/cookie.d.ts b/schema/cookie.d.ts new file mode 100644 index 000000000..eb7878040 --- /dev/null +++ b/schema/cookie.d.ts @@ -0,0 +1,24 @@ +import { Feature, FeatureState, SiteException } from "./feature"; + +type CookieSettings = { + trackerCookie: FeatureState; + nonTrackerCookie: FeatureState; + excludedCookieDomains: SiteException[]; + firstPartyTrackerCookiePolicy: { + threshold: number; + maxAge: number; + }; + firstPartyCookiePolicy: { + threshold: number; + maxAge: number; + }; + thirdPartyCookieNames?: string[]; + // Windows-specific key + excludedCookieNames?: { + domain: string; + name: string; + reason: string; + }[]; +}; + +export type CookieFeature = Feature; diff --git a/schema/feature.d.ts b/schema/feature.d.ts new file mode 100644 index 000000000..f1d645982 --- /dev/null +++ b/schema/feature.d.ts @@ -0,0 +1,34 @@ + +export type SiteException = { + domain: string; + reason?: string; +} + +export type FeatureState = 'enabled' | 'disabled' | 'internal' + +type FeatureMeta = { + description: string; + sampleExcludeRecords?: any +} + +type SubFeature = { + state: FeatureState; + rollout?: { + steps: { percent: number }[] + }; + targets?: { + variantKey?: string + }[]; + minSupportedVersion?: VersionType; +} + +export type Feature = { + readme?: string; + _meta?: FeatureMeta; + state: FeatureState; + exceptions: SiteException[]; + settings?: SettingsType; + features?: Record>; + hash: string; + minSupportedVersion?: VersionType; +} diff --git a/scripts/validate.mjs b/scripts/validate.mjs new file mode 100644 index 000000000..35e83292f --- /dev/null +++ b/scripts/validate.mjs @@ -0,0 +1,29 @@ +import Ajv from 'ajv' +import { createGenerator } from 'ts-json-schema-generator' +import path from 'path' +import fs from 'fs' + +const repoRoot = process.cwd() +const ajv = new Ajv() + +const platformSpecificSchemas = { + 'android-config.json': 'AndroidV4Config' +} + +const v4configRoot = path.join(repoRoot, 'generated', 'v4') +for (const platformConfig of fs.readdirSync(v4configRoot)) { + // create schema and validator for this config + const schema = createGenerator({ + path: path.join(repoRoot, 'schema', 'config.d.ts') + }).createSchema(platformSpecificSchemas[platformConfig] || 'GenericV4Config') + const validate = ajv.compile(schema) + const config = JSON.parse(fs.readFileSync(path.join(v4configRoot, platformConfig))) + console.log('Checking', platformConfig) + const result = validate(config) + if (!result) { + console.log(validate.errors) + break + } else { + console.log('OK') + } +} From 3eda11d150ec98f76173c9720a72c54364596918 Mon Sep 17 00:00:00 2001 From: Sam Macbeth Date: Mon, 7 Oct 2024 16:29:09 +0200 Subject: [PATCH 02/20] Add more feature types and validate against full spec in tests --- schema/config.d.ts | 17 ++++++++++++----- schema/feature.d.ts | 2 ++ schema/features/autoconsent.d.ts | 5 +++++ schema/{ => features}/cookie.d.ts | 2 +- schema/features/tracker-allowlist.d.ts | 16 ++++++++++++++++ tests/config-tests.js | 15 +++++++++++++++ 6 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 schema/features/autoconsent.d.ts rename schema/{ => features}/cookie.d.ts (88%) create mode 100644 schema/features/tracker-allowlist.d.ts diff --git a/schema/config.d.ts b/schema/config.d.ts index cc75233b3..1064e743f 100644 --- a/schema/config.d.ts +++ b/schema/config.d.ts @@ -1,15 +1,22 @@ -import { CookieFeature } from "./cookie"; import { Feature, SiteException } from "./feature"; +import { AutoconsentFeature } from "./features/autoconsent"; +import { CookieFeature } from "./features/cookie"; +import { TrackerAllowlistFeature } from "./features/tracker-allowlist"; -export type V4Config = { +/** + * Defines the structure of the built V4 config output as downloaded by clients. + */ +export type ConfigV4 = { readme: string; version: number features: Record> & { // These features have typed settings + autoconsent: AutoconsentFeature cookie: CookieFeature + trackerAllowlist: TrackerAllowlistFeature }, unprotectedTemporary: SiteException[], - + } /** @@ -17,7 +24,7 @@ export type V4Config = { * - Uses integer version numbers for minSupportedVersion * - Adds 'experimentalVariants' top level property */ -export type AndroidV4Config = V4Config & { +export type AndroidV4Config = ConfigV4 & { experimentalVariants: { variants: { desc: string; @@ -31,4 +38,4 @@ export type AndroidV4Config = V4Config & { * Generic spec: covers mac, iOS, windows and extension configs * - Use string version numbers for minSupportedVersion */ -export type GenericV4Config = V4Config; +export type GenericV4Config = ConfigV4; diff --git a/schema/feature.d.ts b/schema/feature.d.ts index f1d645982..d2cf1a504 100644 --- a/schema/feature.d.ts +++ b/schema/feature.d.ts @@ -32,3 +32,5 @@ export type Feature = { hash: string; minSupportedVersion?: VersionType; } + +export type GenericFeature = Feature; diff --git a/schema/features/autoconsent.d.ts b/schema/features/autoconsent.d.ts new file mode 100644 index 000000000..258f8c12e --- /dev/null +++ b/schema/features/autoconsent.d.ts @@ -0,0 +1,5 @@ +import { Feature } from "../feature"; + +export type AutoconsentFeature = Feature<{ + disabledCMPs: string[] +}, VersionType>; diff --git a/schema/cookie.d.ts b/schema/features/cookie.d.ts similarity index 88% rename from schema/cookie.d.ts rename to schema/features/cookie.d.ts index eb7878040..e24fb8fda 100644 --- a/schema/cookie.d.ts +++ b/schema/features/cookie.d.ts @@ -1,4 +1,4 @@ -import { Feature, FeatureState, SiteException } from "./feature"; +import { Feature, FeatureState, SiteException } from "../feature"; type CookieSettings = { trackerCookie: FeatureState; diff --git a/schema/features/tracker-allowlist.d.ts b/schema/features/tracker-allowlist.d.ts new file mode 100644 index 000000000..de9f450e2 --- /dev/null +++ b/schema/features/tracker-allowlist.d.ts @@ -0,0 +1,16 @@ +import { Feature } from "../feature"; + +type AllowlistRule = { + rule: string + domains: string[] +} + +type TrackerAllowlist = { + allowlistedTrackers: { + [key: string]: { + rules: AllowlistRule[] + } + } +} + +export type TrackerAllowlistFeature = Feature; diff --git a/tests/config-tests.js b/tests/config-tests.js index 25afb2e48..4f29f5df4 100644 --- a/tests/config-tests.js +++ b/tests/config-tests.js @@ -2,6 +2,7 @@ const expect = require('chai').expect const Ajv = require('ajv').default const ajv = new Ajv() const fs = require('fs') +const createGenerator = require('ts-json-schema-generator').createGenerator const platforms = require('./../platforms').map(item => item.replace('browsers/', 'extension-')) function formatErrors (errors) { @@ -12,6 +13,10 @@ function formatErrors (errors) { return errors.map(item => `${item.instancePath}: ${item.message}`).join(', ') } +const platformSpecificSchemas = { + 'v4/android-config.json': 'AndroidV4Config' +} + // Test the latest 2 versions of each platform const latestConfigs = platforms.map((plat) => { return { @@ -37,6 +42,10 @@ describe('Config schema tests', () => { const exceptionSchemav4 = JSON.parse(fs.readFileSync('./tests/schemas/exception-v4.json')) const validateExceptionv4 = ajv.compile(exceptionSchemav4) + const fullSchemaGenerator = createGenerator({ + path: './schema/config.d.ts' + }) + for (const config of latestConfigs) { describe(`${config.name}`, () => { it('should have a valid root schema', () => { @@ -66,6 +75,12 @@ describe('Config schema tests', () => { it('should contain appTrackerProtection or not', () => { expect('appTrackerProtection' in config.body.features).to.be.equal(shouldContainAppTP, `appTrackerProtection expected: ${shouldContainAppTP}`) }) + + it('should validate against the full configV4 schema', () => { + const schema = fullSchemaGenerator.createSchema(platformSpecificSchemas[config.name] || 'GenericV4Config') + const validate = ajv.compile(schema) + expect(validate(config.body)).to.be.equal(true, formatErrors(validate.errors)) + }) }) } From 003bcde74edb920660b69f667a7066ed34954499 Mon Sep 17 00:00:00 2001 From: Sam Macbeth Date: Mon, 7 Oct 2024 16:30:23 +0200 Subject: [PATCH 03/20] Remove schema generator npm command --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index a308cbce1..735a5c65d 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,7 @@ "lint": "eslint .", "unit-tests": "mocha ./tests/**/*.js", "build": "node index.js", - "format": "node format.js", - "generate-schema": "ts-json-schema-generator --path 'schema/config.d.ts' --type 'GenericV4Config'" + "format": "node format.js" }, "author": "DuckDuckGo", "license": "Apache 2.0", From 47b6d5d94dcb931c446bfd5b375f127bf8bec99b Mon Sep 17 00:00:00 2001 From: Sam Macbeth Date: Mon, 7 Oct 2024 16:31:37 +0200 Subject: [PATCH 04/20] Better key name --- schema/features/tracker-allowlist.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schema/features/tracker-allowlist.d.ts b/schema/features/tracker-allowlist.d.ts index de9f450e2..d193d4603 100644 --- a/schema/features/tracker-allowlist.d.ts +++ b/schema/features/tracker-allowlist.d.ts @@ -7,7 +7,7 @@ type AllowlistRule = { type TrackerAllowlist = { allowlistedTrackers: { - [key: string]: { + [domain: string]: { rules: AllowlistRule[] } } From cc765d79860e1540b73b8c795e3ebb2342aec7a4 Mon Sep 17 00:00:00 2001 From: Sam Macbeth Date: Mon, 7 Oct 2024 16:32:11 +0200 Subject: [PATCH 05/20] Remove validation script - superseded by test --- scripts/validate.mjs | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 scripts/validate.mjs diff --git a/scripts/validate.mjs b/scripts/validate.mjs deleted file mode 100644 index 35e83292f..000000000 --- a/scripts/validate.mjs +++ /dev/null @@ -1,29 +0,0 @@ -import Ajv from 'ajv' -import { createGenerator } from 'ts-json-schema-generator' -import path from 'path' -import fs from 'fs' - -const repoRoot = process.cwd() -const ajv = new Ajv() - -const platformSpecificSchemas = { - 'android-config.json': 'AndroidV4Config' -} - -const v4configRoot = path.join(repoRoot, 'generated', 'v4') -for (const platformConfig of fs.readdirSync(v4configRoot)) { - // create schema and validator for this config - const schema = createGenerator({ - path: path.join(repoRoot, 'schema', 'config.d.ts') - }).createSchema(platformSpecificSchemas[platformConfig] || 'GenericV4Config') - const validate = ajv.compile(schema) - const config = JSON.parse(fs.readFileSync(path.join(v4configRoot, platformConfig))) - console.log('Checking', platformConfig) - const result = validate(config) - if (!result) { - console.log(validate.errors) - break - } else { - console.log('OK') - } -} From 956edddb16f9a63cc5ccffcce9ba44299597bb5a Mon Sep 17 00:00:00 2001 From: Sam Macbeth Date: Wed, 23 Oct 2024 11:58:14 +0200 Subject: [PATCH 06/20] Webcompat settings in TS --- schema/config.d.ts | 2 ++ schema/features/webcompat.d.ts | 46 ++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 schema/features/webcompat.d.ts diff --git a/schema/config.d.ts b/schema/config.d.ts index 1064e743f..91c826913 100644 --- a/schema/config.d.ts +++ b/schema/config.d.ts @@ -2,6 +2,7 @@ import { Feature, SiteException } from "./feature"; import { AutoconsentFeature } from "./features/autoconsent"; import { CookieFeature } from "./features/cookie"; import { TrackerAllowlistFeature } from "./features/tracker-allowlist"; +import { WebCompatFeature } from "./features/webcompat"; /** * Defines the structure of the built V4 config output as downloaded by clients. @@ -14,6 +15,7 @@ export type ConfigV4 = { autoconsent: AutoconsentFeature cookie: CookieFeature trackerAllowlist: TrackerAllowlistFeature + webCompat: WebCompatFeature }, unprotectedTemporary: SiteException[], diff --git a/schema/features/webcompat.d.ts b/schema/features/webcompat.d.ts new file mode 100644 index 000000000..9bd8d8607 --- /dev/null +++ b/schema/features/webcompat.d.ts @@ -0,0 +1,46 @@ +import { Feature } from "../feature"; + +type Operation = { + op: 'add' | 'remove' | 'replace' | 'move' | 'copy' | 'test' + from?: string + path: string + value: string +} + +type StateToggle = 'enabled' | 'disabled' +export type WebCompatSettings = { + windowSizing: StateToggle; + navigatorCredentials: StateToggle; + safariObject: StateToggle; + messageHandlers: { + state: StateToggle; + handlerStrategies: { + polyfill: string[]; + reflect: string[]; + undefined: string[]; + } + } + notification: { + state: StateToggle; + } + permissions: { + state: StateToggle; + supportedPermissions: object; + } + mediaSession: StateToggle; + presentation: StateToggle; + webShare: StateToggle; + viewportWidth: StateToggle | { + state: StateToggle; + forcedDesktopValue: string; + forcedMobileValue: string; + } + screenLock: StateToggle; + plainTextViewPort: StateToggle; + domains: { + domain: string | string[]; + patchSettings: Operation[]; + }[] +} + +export type WebCompatFeature = Feature, VersionType>; From 67d76d5e6056ae0b381e469e5fed4e3e21c260e7 Mon Sep 17 00:00:00 2001 From: Sam Macbeth Date: Fri, 8 Nov 2024 11:06:09 +0100 Subject: [PATCH 07/20] Install ts-json-schema-generator --- package-lock.json | 431 +++++++++++++++++++++++++++++++++++++++++++--- package.json | 3 +- 2 files changed, 411 insertions(+), 23 deletions(-) diff --git a/package-lock.json b/package-lock.json index 82a87edc2..a0c68440c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,7 +21,8 @@ "eslint-plugin-import": "^2.31.0", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^5.1.0", - "mocha": "^10.7.3" + "mocha": "^10.7.3", + "ts-json-schema-generator": "^2.3.0" } }, "node_modules/@babel/code-frame": { @@ -141,12 +142,124 @@ "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", "dev": true }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", "dev": true }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -203,6 +316,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, "engines": { "node": ">=8" } @@ -391,7 +505,8 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "node_modules/binary-extensions": { "version": "2.2.0", @@ -623,6 +738,7 @@ "version": "12.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, "engines": { "node": ">=18" } @@ -637,6 +753,7 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -810,12 +927,14 @@ "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true }, "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==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "node_modules/enquirer": { "version": "2.3.6", @@ -1531,6 +1650,22 @@ "is-callable": "^1.1.3" } }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -2013,6 +2148,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, "engines": { "node": ">=8" } @@ -2183,12 +2319,14 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, "node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -2341,6 +2479,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, "engines": { "node": ">=16 || 14 >=14.17" } @@ -2523,6 +2662,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -2671,6 +2811,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -2705,6 +2851,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, "engines": { "node": ">=8" } @@ -2715,6 +2862,28 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, "node_modules/pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", @@ -2937,6 +3106,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -2997,6 +3175,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -3008,6 +3187,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, "engines": { "node": ">=8" } @@ -3034,6 +3214,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, "engines": { "node": ">=14" }, @@ -3101,6 +3282,7 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -3110,6 +3292,21 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.trim": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", @@ -3163,6 +3360,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -3175,6 +3373,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -3282,6 +3481,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-2.3.0.tgz", "integrity": "sha512-t4lBQAwZc0sOJq9LJt3NgbznIcslVnm0JeEMFq8qIRklpMRY8jlYD0YmnRWbqBKANxkby91P1XanSSlSOFpUmg==", + "dev": true, "dependencies": { "@types/json-schema": "^7.0.15", "commander": "^12.0.0", @@ -3303,6 +3503,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -3311,6 +3512,7 @@ "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -3330,6 +3532,7 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, "bin": { "json5": "lib/cli.js" }, @@ -3341,6 +3544,7 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -3366,7 +3570,8 @@ "node_modules/tslib": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true }, "node_modules/type-check": { "version": "0.4.0", @@ -3474,6 +3679,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -3516,6 +3734,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -3598,6 +3817,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -3614,6 +3834,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -3628,6 +3849,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -3638,7 +3860,8 @@ "node_modules/wrap-ansi-cjs/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "node_modules/wrap-ansi/node_modules/ansi-styles": { "version": "4.3.0", @@ -3849,12 +4072,90 @@ "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", "dev": true }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, "@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", "dev": true }, + "@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, "@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -3895,7 +4196,8 @@ "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true }, "ansi-styles": { "version": "3.2.1", @@ -4027,7 +4329,8 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true }, "binary-extensions": { "version": "2.2.0", @@ -4201,7 +4504,8 @@ "commander": { "version": "12.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", - "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==" + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true }, "concat-map": { "version": "0.0.1", @@ -4213,6 +4517,7 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4327,12 +4632,14 @@ "eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true }, "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==" + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true }, "enquirer": { "version": "2.3.6", @@ -4875,6 +5182,16 @@ "is-callable": "^1.1.3" } }, + "foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + } + }, "formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", @@ -5199,7 +5516,8 @@ "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true }, "is-glob": { "version": "4.0.3", @@ -5307,12 +5625,14 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, "jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, "requires": { "@isaacs/cliui": "^8.0.2", "@pkgjs/parseargs": "^0.11.0" @@ -5429,7 +5749,8 @@ "minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==" + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true }, "mocha": { "version": "10.7.3", @@ -5558,7 +5879,8 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true }, "object-inspect": { "version": "1.13.2", @@ -5659,6 +5981,12 @@ "p-limit": "^3.0.2" } }, + "package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5683,7 +6011,8 @@ "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true }, "path-parse": { "version": "1.0.7", @@ -5691,6 +6020,24 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + } + } + }, "pathval": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", @@ -5830,6 +6177,12 @@ "is-regex": "^1.1.4" } }, + "safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "dev": true + }, "semver": { "version": "7.3.5", "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", @@ -5878,6 +6231,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, "requires": { "shebang-regex": "^3.0.0" } @@ -5885,7 +6239,8 @@ "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true }, "side-channel": { "version": "1.0.6", @@ -5902,7 +6257,8 @@ "signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true }, "slice-ansi": { "version": "4.0.0", @@ -5951,12 +6307,24 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.0" } }, + "string-width-cjs": { + "version": "npm:string-width@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, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, "string.prototype.trim": { "version": "1.2.9", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", @@ -5995,6 +6363,7 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "requires": { "ansi-regex": "^5.0.1" } @@ -6003,6 +6372,7 @@ "version": "npm:strip-ansi@6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, "requires": { "ansi-regex": "^5.0.1" } @@ -6080,6 +6450,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/ts-json-schema-generator/-/ts-json-schema-generator-2.3.0.tgz", "integrity": "sha512-t4lBQAwZc0sOJq9LJt3NgbznIcslVnm0JeEMFq8qIRklpMRY8jlYD0YmnRWbqBKANxkby91P1XanSSlSOFpUmg==", + "dev": true, "requires": { "@types/json-schema": "^7.0.15", "commander": "^12.0.0", @@ -6095,6 +6466,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, "requires": { "balanced-match": "^1.0.0" } @@ -6103,6 +6475,7 @@ "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, "requires": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -6115,12 +6488,14 @@ "json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true }, "minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, "requires": { "brace-expansion": "^2.0.1" } @@ -6142,7 +6517,8 @@ "tslib": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", - "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true }, "type-check": { "version": "0.4.0", @@ -6217,6 +6593,12 @@ "possible-typed-array-names": "^1.0.0" } }, + "typescript": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "dev": true + }, "unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -6253,6 +6635,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "requires": { "isexe": "^2.0.0" } @@ -6336,6 +6719,7 @@ "version": "npm:wrap-ansi@7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -6346,6 +6730,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -6354,6 +6739,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } @@ -6361,7 +6747,8 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true } } }, diff --git a/package.json b/package.json index 735a5c65d..1277d8b21 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "eslint-plugin-import": "^2.31.0", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^5.1.0", - "mocha": "^10.7.3" + "mocha": "^10.7.3", + "ts-json-schema-generator": "^2.3.0" }, "dependencies": { "node-fetch": "^3.3.2", From 7a702662ea3ff92e4eacaf8f72d46ae0cbb0eb07 Mon Sep 17 00:00:00 2001 From: Sam Macbeth Date: Fri, 8 Nov 2024 11:11:46 +0100 Subject: [PATCH 08/20] Remove JSONSchema definitions in favor of TS --- tests/config-tests.js | 59 ++++----------------------------- tests/schemas/exception-v4.json | 9 ----- tests/schemas/exception.json | 9 ----- tests/schemas/feature.json | 18 ---------- tests/schemas/root.json | 39 ---------------------- 5 files changed, 6 insertions(+), 128 deletions(-) delete mode 100644 tests/schemas/exception-v4.json delete mode 100644 tests/schemas/exception.json delete mode 100644 tests/schemas/feature.json delete mode 100644 tests/schemas/root.json diff --git a/tests/config-tests.js b/tests/config-tests.js index 4f29f5df4..9d2653852 100644 --- a/tests/config-tests.js +++ b/tests/config-tests.js @@ -33,43 +33,12 @@ const previousConfigs = platforms.map((plat) => { }) describe('Config schema tests', () => { - const rootSchema = JSON.parse(fs.readFileSync('./tests/schemas/root.json')) - const validateRoot = ajv.compile(rootSchema) - const featureSchema = JSON.parse(fs.readFileSync('./tests/schemas/feature.json')) - const validateFeature = ajv.compile(featureSchema) - const exceptionSchema = JSON.parse(fs.readFileSync('./tests/schemas/exception.json')) - const validateException = ajv.compile(exceptionSchema) - const exceptionSchemav4 = JSON.parse(fs.readFileSync('./tests/schemas/exception-v4.json')) - const validateExceptionv4 = ajv.compile(exceptionSchemav4) - const fullSchemaGenerator = createGenerator({ path: './schema/config.d.ts' }) for (const config of latestConfigs) { describe(`${config.name}`, () => { - it('should have a valid root schema', () => { - expect(validateRoot(config.body)).to.be.equal(true, formatErrors(validateRoot.errors)) - }) - - it('should have a vaild feature schema', () => { - for (const featureKey in config.body.features) { - expect(validateFeature(config.body.features[featureKey])).to.be.equal(true, `Feature ${featureKey}: ` + formatErrors(validateFeature.errors)) - } - }) - - it('should have valid exception lists', () => { - for (const featureKey in config.body.features) { - for (const exception of config.body.features[featureKey].exceptions) { - expect(validateExceptionv4(exception)).to.be.equal(true, `Feature ${featureKey}: ` + formatErrors(validateException.errors)) - } - } - - for (const exception of config.body.unprotectedTemporary) { - expect(validateExceptionv4(exception)).to.be.equal(true, 'unprotectedTemporary: ' + formatErrors(validateException.errors)) - } - }) - // appTrackerProtection should only be on the Android config since it is a large feature const shouldContainAppTP = (config.name.split('/')[1] === 'android-config.json') it('should contain appTrackerProtection or not', () => { @@ -86,33 +55,17 @@ describe('Config schema tests', () => { for (const config of previousConfigs) { describe(`${config.name}`, () => { - it('should have a valid root schema', () => { - expect(validateRoot(config.body)).to.be.equal(true, formatErrors(validateRoot.errors)) - }) - - it('should have a vaild feature schema', () => { - for (const featureKey in config.body.features) { - expect(validateFeature(config.body.features[featureKey])).to.be.equal(true, `Feature ${featureKey}: ` + formatErrors(validateFeature.errors)) - } - }) - - it('should have valid exception lists', () => { - for (const featureKey in config.body.features) { - for (const exception of config.body.features[featureKey].exceptions) { - expect(validateException(exception)).to.be.equal(true, `Feature ${featureKey}: ` + formatErrors(validateException.errors)) - } - } - - for (const exception of config.body.unprotectedTemporary) { - expect(validateException(exception)).to.be.equal(true, 'unprotectedTemporary: ' + formatErrors(validateException.errors)) - } - }) - // appTrackerProtection should only be on the Android config since it is a large feature const shouldContainAppTP = (config.name.split('/')[1] === 'android-config.json') it('should contain appTrackerProtection or not', () => { expect('appTrackerProtection' in config.body.features).to.be.equal(shouldContainAppTP, `appTrackerProtection expected: ${shouldContainAppTP}`) }) + + it('should validate against the full configLegacy schema', () => { + const schema = fullSchemaGenerator.createSchema(platformSpecificSchemas[config.name] || 'GenericV4Config') + const validate = ajv.compile(schema) + expect(validate(config.body)).to.be.equal(true, formatErrors(validate.errors)) + }) }) } }) diff --git a/tests/schemas/exception-v4.json b/tests/schemas/exception-v4.json deleted file mode 100644 index 0895d9094..000000000 --- a/tests/schemas/exception-v4.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "type": "object", - "properties": { - "domain": {"type": "string"}, - "reason": {"type": "string"} - }, - "additionalProperties": false, - "required": ["domain"] -} diff --git a/tests/schemas/exception.json b/tests/schemas/exception.json deleted file mode 100644 index d36b16b08..000000000 --- a/tests/schemas/exception.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "type": "object", - "properties": { - "domain": {"type": "string"}, - "reason": {"type": "string"} - }, - "additionalProperties": false, - "required": ["domain", "reason"] -} diff --git a/tests/schemas/feature.json b/tests/schemas/feature.json deleted file mode 100644 index 6aaa89874..000000000 --- a/tests/schemas/feature.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "type": "object", - "properties": { - "state": {"enum": ["enabled", "disabled", "beta", "internal"]}, - "exceptions": { - "type": "array", - "items": {"type": "object"} - }, - "settings": { - "type": "object" - }, - "minSupportedVersion": {"type": ["integer", "string"]}, - "readme": {"type": "string"}, - "hash": {"type": "string"} - }, - "additionalProperties": true, - "required": ["state", "exceptions", "hash"] -} diff --git a/tests/schemas/root.json b/tests/schemas/root.json deleted file mode 100644 index b60f8836e..000000000 --- a/tests/schemas/root.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "type": "object", - "properties": { - "readme": {"type": "string"}, - "experimentalVariants": { - "type": "object", - "properties": { - "variants": { - "type": "array", - "items": { - "type": "object", - "properties": { - "desc": {"type": "string"}, - "variantKey": {"type": "string"}, - "weight": {"type": "number"}, - "filters": {"type": "object"} - }, - "required": ["desc", "variantKey", "weight"] - } - } - }, - "additionalProperties": false - }, - "features": { - "type": "object", - "patternProperties": { - "^[a-zA-Z0-9]+$": {"type": "object"} - }, - "additionalProperties": false - }, - "version": {"type": "integer"}, - "unprotectedTemporary": { - "type": "array", - "items": {"type": "object"} - } - }, - "additionalProperties": false, - "required": ["readme", "features", "version", "unprotectedTemporary"] -} From 0be86507d683fc4e6d8d2fbf3a7119ccd6b41912 Mon Sep 17 00:00:00 2001 From: Sam Macbeth Date: Fri, 8 Nov 2024 11:35:16 +0100 Subject: [PATCH 09/20] Move webcompat tests to TS schema --- schema/config.d.ts | 6 + tests/config-tests.js | 22 +-- tests/schema-validation.js | 37 ++++ tests/schemas/webcompat-settings.json | 239 -------------------------- tests/webcompat-tests.js | 13 +- 5 files changed, 49 insertions(+), 268 deletions(-) create mode 100644 tests/schema-validation.js delete mode 100644 tests/schemas/webcompat-settings.json diff --git a/schema/config.d.ts b/schema/config.d.ts index 91c826913..929c5aebd 100644 --- a/schema/config.d.ts +++ b/schema/config.d.ts @@ -3,6 +3,11 @@ import { AutoconsentFeature } from "./features/autoconsent"; import { CookieFeature } from "./features/cookie"; import { TrackerAllowlistFeature } from "./features/tracker-allowlist"; import { WebCompatFeature } from "./features/webcompat"; +export { WebCompatSettings } from "./features/webcompat"; + +export type SupportedSchemas = 'GenericV4Config' | + 'AndroidV4Config' | + 'WebCompatSettings' /** * Defines the structure of the built V4 config output as downloaded by clients. @@ -41,3 +46,4 @@ export type AndroidV4Config = ConfigV4 & { * - Use string version numbers for minSupportedVersion */ export type GenericV4Config = ConfigV4; + diff --git a/tests/config-tests.js b/tests/config-tests.js index 9d2653852..1a1c74ebb 100644 --- a/tests/config-tests.js +++ b/tests/config-tests.js @@ -1,18 +1,8 @@ const expect = require('chai').expect -const Ajv = require('ajv').default -const ajv = new Ajv() const fs = require('fs') -const createGenerator = require('ts-json-schema-generator').createGenerator +const { createValidator, formatErrors } = require('./schema-validation') const platforms = require('./../platforms').map(item => item.replace('browsers/', 'extension-')) -function formatErrors (errors) { - if (!Array.isArray(errors)) { - return '' - } - - return errors.map(item => `${item.instancePath}: ${item.message}`).join(', ') -} - const platformSpecificSchemas = { 'v4/android-config.json': 'AndroidV4Config' } @@ -33,10 +23,6 @@ const previousConfigs = platforms.map((plat) => { }) describe('Config schema tests', () => { - const fullSchemaGenerator = createGenerator({ - path: './schema/config.d.ts' - }) - for (const config of latestConfigs) { describe(`${config.name}`, () => { // appTrackerProtection should only be on the Android config since it is a large feature @@ -46,8 +32,7 @@ describe('Config schema tests', () => { }) it('should validate against the full configV4 schema', () => { - const schema = fullSchemaGenerator.createSchema(platformSpecificSchemas[config.name] || 'GenericV4Config') - const validate = ajv.compile(schema) + const validate = createValidator(platformSpecificSchemas[config.name] || 'GenericV4Config') expect(validate(config.body)).to.be.equal(true, formatErrors(validate.errors)) }) }) @@ -62,8 +47,7 @@ describe('Config schema tests', () => { }) it('should validate against the full configLegacy schema', () => { - const schema = fullSchemaGenerator.createSchema(platformSpecificSchemas[config.name] || 'GenericV4Config') - const validate = ajv.compile(schema) + const validate = createValidator(platformSpecificSchemas[config.name] || 'GenericV4Config') expect(validate(config.body)).to.be.equal(true, formatErrors(validate.errors)) }) }) diff --git a/tests/schema-validation.js b/tests/schema-validation.js new file mode 100644 index 000000000..7d23a771e --- /dev/null +++ b/tests/schema-validation.js @@ -0,0 +1,37 @@ +const Ajv = require('ajv').default +const ajv = new Ajv() +const createGenerator = require('ts-json-schema-generator').createGenerator + +const fullSchemaGenerator = createGenerator({ + path: './schema/config.d.ts' +}) + +/** + * Generate the JSONSchema for the named TS type + * @param {import('../schema/config').SupportedSchemas} schemaName + */ +function getSchema (schemaName) { + return fullSchemaGenerator.createSchema(schemaName) +} + +/** + * Generate a validator for checking JSON objects against the named TS type + * @param {import('../schema/config').SupportedSchemas} schemaName + */ +function createValidator (schemaName) { + return ajv.compile(getSchema(schemaName)) +} + +function formatErrors (errors) { + if (!Array.isArray(errors)) { + return '' + } + + return errors.map(item => `${item.instancePath}: ${item.message}`).join(', ') +} + +module.exports = { + getSchema, + createValidator, + formatErrors +} diff --git a/tests/schemas/webcompat-settings.json b/tests/schemas/webcompat-settings.json deleted file mode 100644 index b821a2db7..000000000 --- a/tests/schemas/webcompat-settings.json +++ /dev/null @@ -1,239 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Web Compat Settings", - "description": "Settings configuration for Web Compat", - "type": "object", - "additionalProperties": false, - "properties": { - "windowSizing": { - "$ref": "#/definitions/state" - }, - "navigatorCredentials": { - "$ref": "#/definitions/state" - }, - "safariObject": { - "$ref": "#/definitions/state" - }, - "messageHandlers": { - "type": "object", - "additionalProperties": false, - "required": [ - "state", - "handlerStrategies" - ], - "properties": { - "state": { - "$ref": "#/definitions/state" - }, - "handlerStrategies": { - "type": "object", - "additionalProperties": false, - "required": [ - "polyfill", - "reflect", - "undefined" - ], - "properties": { - "reflect": { - "type": "array", - "items": { - "type": "string" - } - }, - "polyfill": { - "type": "array", - "items": { - "type": "string" - } - }, - "undefined": { - "type": "array", - "items": { - "type": "string" - } - } - } - } - } - }, - "modifyLocalStorage": { - "type": "object", - "additionalProperties": false, - "required": [ - "state", - "changes" - ], - "properties": { - "state": { - "$ref": "#/definitions/state" - }, - "changes": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": false, - "required": [ - "key", - "action" - ], - "properties": { - "key": { - "type": "string" - }, - "action": { - "type": "string" - } - } - } - } - } - }, - "notification": { - "type": "object", - "required": [ - "state" - ], - "properties": { - "state": { - "$ref": "#/definitions/state" - } - } - }, - "permissions": { - "type": "object", - "required": [ - "state", - "supportedPermissions" - ], - "properties": { - "state": { - "$ref": "#/definitions/state" - }, - "supportedPermissions": { - "type": "object" - } - } - }, - "mediaSession": { - "$ref": "#/definitions/state" - }, - "presentation": { - "$ref": "#/definitions/state" - }, - "webShare": { - "$ref": "#/definitions/state" - }, - "viewportWidth": { - "oneOf": [ - { - "$ref": "#/definitions/state" - }, - { - "type": "object", - "required": [ - "state" - ], - "properties": { - "state": { - "$ref": "#/definitions/state" - }, - "forcedDesktopValue": { - "type": "string" - }, - "forcedMobileValue": { - "type": "string" - } - } - } - ] - }, - "screenLock": { - "$ref": "#/definitions/state" - }, - "domains": { - "$ref": "#/definitions/domains" - }, - "plainTextViewPort": { - "$ref": "#/definitions/state" - } - }, - "definitions": { - "state": { - "type": "string", - "enum": [ - "enabled", - "disabled" - ] - }, - "domains": { - "description": "List of domains with specific patch settings", - "type": "array", - "items": { - "title": "Domain", - "type": "object", - "additionalProperties": false, - "properties": { - "domain": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ], - "description": "Domain name" - }, - "patchSettings": { - "description": "List of operations to be applied on the settings for a specific domain", - "type": "array", - "items": { - "type": "object", - "title": "Patch Setting", - "additionalProperties": false, - "properties": { - "op": { - "type": "string", - "description": "The operation to be performed" - }, - "path": { - "type": "string", - "description": "The path of the setting to be patched" - }, - "value": { - "oneOf": [ - { - "type": "string" - }, - { - "type": "array" - }, - { - "type": "object" - }, - { - "type": "number" - } - ], - "description": "The value to replace at the specified path" - } - }, - "required": [ - "op", - "path", - "value" - ] - } - } - }, - "required": [ - "domain", - "patchSettings" - ] - } - } - } -} diff --git a/tests/webcompat-tests.js b/tests/webcompat-tests.js index 7f09f9446..bebaf597f 100644 --- a/tests/webcompat-tests.js +++ b/tests/webcompat-tests.js @@ -1,9 +1,7 @@ const expect = require('chai').expect const fs = require('fs') -const Ajv = require('ajv').default -const ajv = new Ajv() -const rootSchema = JSON.parse(fs.readFileSync('./tests/schemas/webcompat-settings.json', 'utf8')) -const validateRoot = ajv.compile(rootSchema) +const { createValidator, formatErrors } = require('./schema-validation') +const validateRoot = createValidator('WebCompatSettings') const platforms = ['macos', 'ios', 'android'] const latestConfigs = platforms.map((plat) => { @@ -17,12 +15,7 @@ for (const config of latestConfigs) { describe(`validates the config settings for ${config.name}`, () => { it('should have valid webCompat setting', () => { const actual = validateRoot(config.body.features.webCompat.settings) - if (validateRoot.errors) { - for (const error of validateRoot.errors) { - console.error(error) - } - } - expect(actual).to.be.equal(true) + expect(actual).to.be.equal(true, formatErrors(validateRoot.errors)) }) }) } From 99e730c36b0c4e33cd00b450c6fec0d651adafcc Mon Sep 17 00:00:00 2001 From: Sam Macbeth Date: Fri, 8 Nov 2024 11:35:42 +0100 Subject: [PATCH 10/20] Add missing webcompat property --- schema/features/webcompat.d.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/schema/features/webcompat.d.ts b/schema/features/webcompat.d.ts index 9bd8d8607..079c883d3 100644 --- a/schema/features/webcompat.d.ts +++ b/schema/features/webcompat.d.ts @@ -4,11 +4,11 @@ type Operation = { op: 'add' | 'remove' | 'replace' | 'move' | 'copy' | 'test' from?: string path: string - value: string + value: any } type StateToggle = 'enabled' | 'disabled' -export type WebCompatSettings = { +type FullWebCompatOptions = { windowSizing: StateToggle; navigatorCredentials: StateToggle; safariObject: StateToggle; @@ -25,6 +25,7 @@ export type WebCompatSettings = { } permissions: { state: StateToggle; + validPermissionNames: string[]; supportedPermissions: object; } mediaSession: StateToggle; @@ -40,7 +41,12 @@ export type WebCompatSettings = { domains: { domain: string | string[]; patchSettings: Operation[]; - }[] + }[], + modifyLocalStorage: { + state: StateToggle, + changes: any[] + } } +export type WebCompatSettings = Partial -export type WebCompatFeature = Feature, VersionType>; +export type WebCompatFeature = Feature; From cad2a52989a845e4fa49b3236c4b7fed54497975 Mon Sep 17 00:00:00 2001 From: Sam Macbeth Date: Fri, 8 Nov 2024 11:45:25 +0100 Subject: [PATCH 11/20] Fix autoconsent schema --- schema/features/autoconsent.d.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/schema/features/autoconsent.d.ts b/schema/features/autoconsent.d.ts index 258f8c12e..dc3d05d3c 100644 --- a/schema/features/autoconsent.d.ts +++ b/schema/features/autoconsent.d.ts @@ -1,5 +1,9 @@ import { Feature } from "../feature"; export type AutoconsentFeature = Feature<{ - disabledCMPs: string[] + disabledCMPs: string[] | undefined; + enableIfMainWorldIsSupported: { + state: 'enabled' | 'disabled'; + minSupportedVersion: VersionType; + } | undefined }, VersionType>; From 9b7f60e3ae22646bcc7d0e91b0f5023f6d98fe09 Mon Sep 17 00:00:00 2001 From: Sam Macbeth Date: Fri, 8 Nov 2024 11:48:16 +0100 Subject: [PATCH 12/20] Add filters to android experimentalVariants --- schema/config.d.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/schema/config.d.ts b/schema/config.d.ts index 929c5aebd..3ca204c28 100644 --- a/schema/config.d.ts +++ b/schema/config.d.ts @@ -37,6 +37,9 @@ export type AndroidV4Config = ConfigV4 & { desc: string; variantKey: string; weight: number; + filters?: { + privacyProEligible?: boolean; + } }[] } } From e8b54ebad69b3b113cbe0cde8773e7a68979f868 Mon Sep 17 00:00:00 2001 From: Sam Macbeth Date: Fri, 8 Nov 2024 11:57:08 +0100 Subject: [PATCH 13/20] Add duckplayer schema --- schema/config.d.ts | 7 ++++++- schema/features/duckplayer.d.ts | 33 +++++++++++++++++++++++++++++++++ schema/features/webcompat.d.ts | 10 ++-------- schema/json-patch.d.ts | 6 ++++++ tests/duckplayer-tests.js | 20 ++++---------------- 5 files changed, 51 insertions(+), 25 deletions(-) create mode 100644 schema/features/duckplayer.d.ts create mode 100644 schema/json-patch.d.ts diff --git a/schema/config.d.ts b/schema/config.d.ts index 3ca204c28..f45131817 100644 --- a/schema/config.d.ts +++ b/schema/config.d.ts @@ -3,11 +3,15 @@ import { AutoconsentFeature } from "./features/autoconsent"; import { CookieFeature } from "./features/cookie"; import { TrackerAllowlistFeature } from "./features/tracker-allowlist"; import { WebCompatFeature } from "./features/webcompat"; +import { DuckPlayerFeature } from "./features/duckplayer"; + export { WebCompatSettings } from "./features/webcompat"; +export { DuckPlayerSettings } from './features/duckplayer'; export type SupportedSchemas = 'GenericV4Config' | 'AndroidV4Config' | - 'WebCompatSettings' + 'WebCompatSettings' | + 'DuckPlayerSettings' /** * Defines the structure of the built V4 config output as downloaded by clients. @@ -19,6 +23,7 @@ export type ConfigV4 = { // These features have typed settings autoconsent: AutoconsentFeature cookie: CookieFeature + duckPlayer: DuckPlayerFeature trackerAllowlist: TrackerAllowlistFeature webCompat: WebCompatFeature }, diff --git a/schema/features/duckplayer.d.ts b/schema/features/duckplayer.d.ts new file mode 100644 index 000000000..6ad483b6d --- /dev/null +++ b/schema/features/duckplayer.d.ts @@ -0,0 +1,33 @@ +import { Feature } from "../feature"; +import { Operation } from "../json-patch"; + +type State = 'enabled' | 'disabled' +type StateObject = { + state: State +} +export type DuckPlayerSettings = { + overlays: { + youtube: { + state: State + selectors: { + thumbLink: string; + excludedRegions: string[]; + hoverExcluded: string[]; + clickExcluded: string[]; + allowedEventTargets: string[]; + videoElement: string; + videoElementContainer: string[] + }; + thumbnailOverlays: StateObject; + clickInterception: StateObject; + videoOverlays: StateObject; + }; + serpProxy: StateObject; + }; + domains: { + domain: string; + patchSettings: Operation + }[] +}; + +export type DuckPlayerFeature = Feature; diff --git a/schema/features/webcompat.d.ts b/schema/features/webcompat.d.ts index 079c883d3..edf2b6b95 100644 --- a/schema/features/webcompat.d.ts +++ b/schema/features/webcompat.d.ts @@ -1,11 +1,5 @@ import { Feature } from "../feature"; - -type Operation = { - op: 'add' | 'remove' | 'replace' | 'move' | 'copy' | 'test' - from?: string - path: string - value: any -} +import { Operation } from "../json-patch"; type StateToggle = 'enabled' | 'disabled' type FullWebCompatOptions = { @@ -40,7 +34,7 @@ type FullWebCompatOptions = { plainTextViewPort: StateToggle; domains: { domain: string | string[]; - patchSettings: Operation[]; + patchSettings: Operation[]; }[], modifyLocalStorage: { state: StateToggle, diff --git a/schema/json-patch.d.ts b/schema/json-patch.d.ts new file mode 100644 index 000000000..89014af23 --- /dev/null +++ b/schema/json-patch.d.ts @@ -0,0 +1,6 @@ +export type Operation = { + op: "add" | "remove" | "replace" | "move" | "copy" | "test"; + from?: string; + path: string; + value: ValueType; +}; diff --git a/tests/duckplayer-tests.js b/tests/duckplayer-tests.js index 746dfa0a9..1744ac321 100644 --- a/tests/duckplayer-tests.js +++ b/tests/duckplayer-tests.js @@ -1,30 +1,18 @@ const expect = require('chai').expect const fs = require('fs') +const { createValidator, formatErrors } = require('./schema-validation') describe('Duck Player Settings Tests', () => { - const Ajv = require('ajv').default - const ajv = new Ajv() - const rootSchema = JSON.parse(fs.readFileSync('./tests/schemas/duckplayer-settings.json', 'utf8')) - const validateRoot = ajv.compile(rootSchema) + const validateRoot = createValidator('DuckPlayerSettings') it('validates the duckplayer settings in `./generated/v4/macos-config.json`', () => { const macosConfig = JSON.parse(fs.readFileSync('./generated/v4/macos-config.json', 'utf8')) const actual = validateRoot(macosConfig.features.duckPlayer.settings) - if (validateRoot.errors) { - for (const error of validateRoot.errors) { - console.error(error) - } - } - expect(actual).to.be.equal(true) + expect(actual).to.be.equal(true, formatErrors(validateRoot.errors)) }) it('validates the duckplayer settings in `./generated/v4/windows-config.json`', () => { const windowsConfig = JSON.parse(fs.readFileSync('./generated/v4/windows-config.json', 'utf8')) const actual = validateRoot(windowsConfig.features.duckPlayer.settings) - if (validateRoot.errors) { - for (const error of validateRoot.errors) { - console.error(error) - } - } - expect(actual).to.be.equal(true) + expect(actual).to.be.equal(true, formatErrors(validateRoot.errors)) }) }) From 24d8ab00c67b18d41915c2a068c6ee452e299f1e Mon Sep 17 00:00:00 2001 From: Sam Macbeth Date: Fri, 8 Nov 2024 12:05:20 +0100 Subject: [PATCH 14/20] Complete Duckplayer schema --- schema/config.d.ts | 2 +- schema/features/duckplayer.d.ts | 12 ++++++++++-- tests/schema-validation.js | 21 ++++++++++++++------- 3 files changed, 25 insertions(+), 10 deletions(-) diff --git a/schema/config.d.ts b/schema/config.d.ts index f45131817..c46730dc9 100644 --- a/schema/config.d.ts +++ b/schema/config.d.ts @@ -8,7 +8,7 @@ import { DuckPlayerFeature } from "./features/duckplayer"; export { WebCompatSettings } from "./features/webcompat"; export { DuckPlayerSettings } from './features/duckplayer'; -export type SupportedSchemas = 'GenericV4Config' | +export type ExportedSchemas = 'GenericV4Config' | 'AndroidV4Config' | 'WebCompatSettings' | 'DuckPlayerSettings' diff --git a/schema/features/duckplayer.d.ts b/schema/features/duckplayer.d.ts index 6ad483b6d..c75aa4cf0 100644 --- a/schema/features/duckplayer.d.ts +++ b/schema/features/duckplayer.d.ts @@ -6,6 +6,14 @@ type StateObject = { state: State } export type DuckPlayerSettings = { + tryDuckPlayerLink: string; + duckPlayerDisabledHelpPageLink: string | null; + youtubePath: string; + youtubeEmbedUrl: string; + youTubeUrl: string; + youTubeReferrerHeaders: string[]; + youTubeReferrerQueryParams: string[]; + youTubeVideoIDQueryParam: string; overlays: { youtube: { state: State @@ -16,7 +24,7 @@ export type DuckPlayerSettings = { clickExcluded: string[]; allowedEventTargets: string[]; videoElement: string; - videoElementContainer: string[] + videoElementContainer: string; }; thumbnailOverlays: StateObject; clickInterception: StateObject; @@ -26,7 +34,7 @@ export type DuckPlayerSettings = { }; domains: { domain: string; - patchSettings: Operation + patchSettings: Operation[] }[] }; diff --git a/tests/schema-validation.js b/tests/schema-validation.js index 7d23a771e..4618e5875 100644 --- a/tests/schema-validation.js +++ b/tests/schema-validation.js @@ -1,22 +1,29 @@ const Ajv = require('ajv').default const ajv = new Ajv() -const createGenerator = require('ts-json-schema-generator').createGenerator +const schemaGenerator = require('ts-json-schema-generator') -const fullSchemaGenerator = createGenerator({ - path: './schema/config.d.ts' -}) +function createGenerator () { + try { + return schemaGenerator.createGenerator({ + path: './schema/config.d.ts' + }) + } catch (e) { + console.error(e.diagnostic) + throw e + } +} /** * Generate the JSONSchema for the named TS type - * @param {import('../schema/config').SupportedSchemas} schemaName + * @param {import('../schema/config').ExportedSchemas} schemaName */ function getSchema (schemaName) { - return fullSchemaGenerator.createSchema(schemaName) + return createGenerator().createSchema(schemaName) } /** * Generate a validator for checking JSON objects against the named TS type - * @param {import('../schema/config').SupportedSchemas} schemaName + * @param {import('../schema/config').ExportedSchemas} schemaName */ function createValidator (schemaName) { return ajv.compile(getSchema(schemaName)) From 7d66aae10109b6c1662a1080a65807316c125022 Mon Sep 17 00:00:00 2001 From: Sam Macbeth Date: Fri, 8 Nov 2024 12:07:16 +0100 Subject: [PATCH 15/20] Remove duckplayer JSONSchema --- tests/schemas/duckplayer-settings.json | 182 ------------------------- 1 file changed, 182 deletions(-) delete mode 100644 tests/schemas/duckplayer-settings.json diff --git a/tests/schemas/duckplayer-settings.json b/tests/schemas/duckplayer-settings.json deleted file mode 100644 index c7df88f55..000000000 --- a/tests/schemas/duckplayer-settings.json +++ /dev/null @@ -1,182 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "DuckPlayerSettings", - "description": "Settings configuration for video player", - "type": "object", - "properties": { - "overlays": { - "title": "Overlays", - "description": "Specific configurations for different overlay types", - "type": "object", - "additionalProperties": false, - "properties": { - "youtube": { - "title": "YouTube Overlay", - "description": "Configuration specific to YouTube overlays", - "type": "object", - "additionalProperties": false, - "properties": { - "state": { - "$ref": "#/definitions/state" - }, - "selectors": { - "title": "Selectors", - "description": "CSS selectors for identifying specific HTML elements on a YouTube page", - "type": "object", - "additionalProperties": false, - "properties": { - "thumbLink": { - "type": "string", - "description": "CSS selector for YouTube thumbnail links" - }, - "excludedRegions": { - "type": "array", - "description": "CSS selectors for regions to exclude from hover/click interactions", - "items": { - "type": "string" - } - }, - "hoverExcluded": { - "type": "array", - "description": "CSS selectors for elements that should prevent side effects from hovers", - "items": { - "type": "string" - } - }, - "clickExcluded": { - "type": "array", - "description": "CSS selectors for elements that should prevent side effects from clicks", - "items": { - "type": "string" - } - }, - "allowedEventTargets": { - "type": "array", - "description": "CSS selectors to explicitly allow known event targets for hovers/clicks. For example, preview overlays.", - "items": { - "type": "string" - } - }, - "videoElement": { - "type": "string", - "description": "CSS selector for the video element on YouTube" - }, - "videoElementContainer": { - "type": "string", - "description": "CSS selector for the container of the video element" - } - }, - "required": [ - "thumbLink", - "excludedRegions", - "videoElement", - "videoElementContainer", - "clickExcluded", - "hoverExcluded", - "allowedEventTargets" - ] - }, - "thumbnailOverlays": { - "title": "Thumbnail Overlays", - "description": "Settings related to the display of thumbnail overlays", - "$ref": "#/definitions/stateObject" - }, - "clickInterception": { - "title": "Click Interception", - "description": "Settings for intercepting click events", - "$ref": "#/definitions/stateObject" - }, - "videoOverlays": { - "title": "Video Overlays", - "description": "Settings related to the display of video overlays", - "$ref": "#/definitions/stateObject" - } - }, - "required": [ - "state", - "selectors", - "thumbnailOverlays", - "clickInterception", - "videoOverlays" - ] - }, - "serpProxy": { - "title": "SERP Proxy", - "description": "Configuration for the SERP (Search Engine Results Page) proxy", - "$ref": "#/definitions/stateObject" - } - }, - "required": [ - "youtube", - "serpProxy" - ] - }, - "domains": { - "description": "List of domains with specific patch settings", - "type": "array", - "items": { - "title": "Domain", - "type": "object", - "additionalProperties": false, - "properties": { - "domain": { - "type": "string", - "description": "Domain name" - }, - "patchSettings": { - "description": "List of operations to be applied on the settings for a specific domain", - "type": "array", - "items": { - "type": "object", - "title": "Patch Setting", - "additionalProperties": false, - "properties": { - "op": { - "type": "string", - "description": "The operation to be performed" - }, - "path": { - "type": "string", - "description": "The path of the setting to be patched" - }, - "value": { - "type": "string", - "description": "The value to replace at the specified path" - } - }, - "required": [ - "op", - "path", - "value" - ] - } - } - }, - "required": [ - "domain", - "patchSettings" - ] - } - } - }, - "required": [ - "overlays", - "domains" - ], - "definitions": { - "stateObject": { - "type": "object", - "additionalProperties": false, - "properties": { - "state": { "$ref": "#/definitions/state" } - }, - "required": [ - "state" - ] - }, - "state": { - "type": "string", - "enum": ["enabled", "disabled"] - } - } -} From de90e0778830aa368aa8fe0355b1e0072d4f69c6 Mon Sep 17 00:00:00 2001 From: Sam Macbeth Date: Fri, 8 Nov 2024 12:09:58 +0100 Subject: [PATCH 16/20] Make things prettier --- .prettierrc | 5 +++ schema/config.d.ts | 47 ++++++++++++-------------- schema/feature.d.ts | 17 +++++----- schema/features/autoconsent.d.ts | 21 +++++++----- schema/features/cookie.d.ts | 38 ++++++++++----------- schema/features/duckplayer.d.ts | 16 ++++----- schema/features/tracker-allowlist.d.ts | 16 ++++----- schema/features/webcompat.d.ts | 38 +++++++++++---------- schema/json-patch.d.ts | 8 ++--- 9 files changed, 106 insertions(+), 100 deletions(-) create mode 100644 .prettierrc diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..ba94134ce --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "singleQuote": true, + "printWidth": 140, + "tabWidth": 4 +} \ No newline at end of file diff --git a/schema/config.d.ts b/schema/config.d.ts index c46730dc9..d9765ba1f 100644 --- a/schema/config.d.ts +++ b/schema/config.d.ts @@ -1,35 +1,31 @@ -import { Feature, SiteException } from "./feature"; -import { AutoconsentFeature } from "./features/autoconsent"; -import { CookieFeature } from "./features/cookie"; -import { TrackerAllowlistFeature } from "./features/tracker-allowlist"; -import { WebCompatFeature } from "./features/webcompat"; -import { DuckPlayerFeature } from "./features/duckplayer"; +import { Feature, SiteException } from './feature'; +import { AutoconsentFeature } from './features/autoconsent'; +import { CookieFeature } from './features/cookie'; +import { TrackerAllowlistFeature } from './features/tracker-allowlist'; +import { WebCompatFeature } from './features/webcompat'; +import { DuckPlayerFeature } from './features/duckplayer'; -export { WebCompatSettings } from "./features/webcompat"; +export { WebCompatSettings } from './features/webcompat'; export { DuckPlayerSettings } from './features/duckplayer'; -export type ExportedSchemas = 'GenericV4Config' | - 'AndroidV4Config' | - 'WebCompatSettings' | - 'DuckPlayerSettings' +export type ExportedSchemas = 'GenericV4Config' | 'AndroidV4Config' | 'WebCompatSettings' | 'DuckPlayerSettings'; /** * Defines the structure of the built V4 config output as downloaded by clients. */ export type ConfigV4 = { readme: string; - version: number + version: number; features: Record> & { // These features have typed settings - autoconsent: AutoconsentFeature - cookie: CookieFeature - duckPlayer: DuckPlayerFeature - trackerAllowlist: TrackerAllowlistFeature - webCompat: WebCompatFeature - }, - unprotectedTemporary: SiteException[], - -} + autoconsent: AutoconsentFeature; + cookie: CookieFeature; + duckPlayer: DuckPlayerFeature; + trackerAllowlist: TrackerAllowlistFeature; + webCompat: WebCompatFeature; + }; + unprotectedTemporary: SiteException[]; +}; /** * Android: @@ -44,14 +40,13 @@ export type AndroidV4Config = ConfigV4 & { weight: number; filters?: { privacyProEligible?: boolean; - } - }[] - } -} + }; + }[]; + }; +}; /** * Generic spec: covers mac, iOS, windows and extension configs * - Use string version numbers for minSupportedVersion */ export type GenericV4Config = ConfigV4; - diff --git a/schema/feature.d.ts b/schema/feature.d.ts index d2cf1a504..ba6f1f854 100644 --- a/schema/feature.d.ts +++ b/schema/feature.d.ts @@ -1,26 +1,25 @@ - export type SiteException = { domain: string; reason?: string; -} +}; -export type FeatureState = 'enabled' | 'disabled' | 'internal' +export type FeatureState = 'enabled' | 'disabled' | 'internal'; type FeatureMeta = { description: string; - sampleExcludeRecords?: any -} + sampleExcludeRecords?: any; +}; type SubFeature = { state: FeatureState; rollout?: { - steps: { percent: number }[] + steps: { percent: number }[]; }; targets?: { - variantKey?: string + variantKey?: string; }[]; minSupportedVersion?: VersionType; -} +}; export type Feature = { readme?: string; @@ -31,6 +30,6 @@ export type Feature = { features?: Record>; hash: string; minSupportedVersion?: VersionType; -} +}; export type GenericFeature = Feature; diff --git a/schema/features/autoconsent.d.ts b/schema/features/autoconsent.d.ts index dc3d05d3c..a204857c5 100644 --- a/schema/features/autoconsent.d.ts +++ b/schema/features/autoconsent.d.ts @@ -1,9 +1,14 @@ -import { Feature } from "../feature"; +import { Feature } from '../feature'; -export type AutoconsentFeature = Feature<{ - disabledCMPs: string[] | undefined; - enableIfMainWorldIsSupported: { - state: 'enabled' | 'disabled'; - minSupportedVersion: VersionType; - } | undefined -}, VersionType>; +export type AutoconsentFeature = Feature< + { + disabledCMPs: string[] | undefined; + enableIfMainWorldIsSupported: + | { + state: 'enabled' | 'disabled'; + minSupportedVersion: VersionType; + } + | undefined; + }, + VersionType +>; diff --git a/schema/features/cookie.d.ts b/schema/features/cookie.d.ts index e24fb8fda..1b5659925 100644 --- a/schema/features/cookie.d.ts +++ b/schema/features/cookie.d.ts @@ -1,24 +1,24 @@ -import { Feature, FeatureState, SiteException } from "../feature"; +import { Feature, FeatureState, SiteException } from '../feature'; type CookieSettings = { - trackerCookie: FeatureState; - nonTrackerCookie: FeatureState; - excludedCookieDomains: SiteException[]; - firstPartyTrackerCookiePolicy: { - threshold: number; - maxAge: number; - }; - firstPartyCookiePolicy: { - threshold: number; - maxAge: number; - }; - thirdPartyCookieNames?: string[]; - // Windows-specific key - excludedCookieNames?: { - domain: string; - name: string; - reason: string; - }[]; + trackerCookie: FeatureState; + nonTrackerCookie: FeatureState; + excludedCookieDomains: SiteException[]; + firstPartyTrackerCookiePolicy: { + threshold: number; + maxAge: number; + }; + firstPartyCookiePolicy: { + threshold: number; + maxAge: number; + }; + thirdPartyCookieNames?: string[]; + // Windows-specific key + excludedCookieNames?: { + domain: string; + name: string; + reason: string; + }[]; }; export type CookieFeature = Feature; diff --git a/schema/features/duckplayer.d.ts b/schema/features/duckplayer.d.ts index c75aa4cf0..a206643ff 100644 --- a/schema/features/duckplayer.d.ts +++ b/schema/features/duckplayer.d.ts @@ -1,10 +1,10 @@ -import { Feature } from "../feature"; -import { Operation } from "../json-patch"; +import { Feature } from '../feature'; +import { Operation } from '../json-patch'; -type State = 'enabled' | 'disabled' +type State = 'enabled' | 'disabled'; type StateObject = { - state: State -} + state: State; +}; export type DuckPlayerSettings = { tryDuckPlayerLink: string; duckPlayerDisabledHelpPageLink: string | null; @@ -16,7 +16,7 @@ export type DuckPlayerSettings = { youTubeVideoIDQueryParam: string; overlays: { youtube: { - state: State + state: State; selectors: { thumbLink: string; excludedRegions: string[]; @@ -34,8 +34,8 @@ export type DuckPlayerSettings = { }; domains: { domain: string; - patchSettings: Operation[] - }[] + patchSettings: Operation[]; + }[]; }; export type DuckPlayerFeature = Feature; diff --git a/schema/features/tracker-allowlist.d.ts b/schema/features/tracker-allowlist.d.ts index d193d4603..c1901d9a8 100644 --- a/schema/features/tracker-allowlist.d.ts +++ b/schema/features/tracker-allowlist.d.ts @@ -1,16 +1,16 @@ -import { Feature } from "../feature"; +import { Feature } from '../feature'; type AllowlistRule = { - rule: string - domains: string[] -} + rule: string; + domains: string[]; +}; type TrackerAllowlist = { allowlistedTrackers: { [domain: string]: { - rules: AllowlistRule[] - } - } -} + rules: AllowlistRule[]; + }; + }; +}; export type TrackerAllowlistFeature = Feature; diff --git a/schema/features/webcompat.d.ts b/schema/features/webcompat.d.ts index edf2b6b95..a2d35dedd 100644 --- a/schema/features/webcompat.d.ts +++ b/schema/features/webcompat.d.ts @@ -1,7 +1,7 @@ -import { Feature } from "../feature"; -import { Operation } from "../json-patch"; +import { Feature } from '../feature'; +import { Operation } from '../json-patch'; -type StateToggle = 'enabled' | 'disabled' +type StateToggle = 'enabled' | 'disabled'; type FullWebCompatOptions = { windowSizing: StateToggle; navigatorCredentials: StateToggle; @@ -12,35 +12,37 @@ type FullWebCompatOptions = { polyfill: string[]; reflect: string[]; undefined: string[]; - } - } + }; + }; notification: { state: StateToggle; - } + }; permissions: { state: StateToggle; validPermissionNames: string[]; supportedPermissions: object; - } + }; mediaSession: StateToggle; presentation: StateToggle; webShare: StateToggle; - viewportWidth: StateToggle | { - state: StateToggle; - forcedDesktopValue: string; - forcedMobileValue: string; - } + viewportWidth: + | StateToggle + | { + state: StateToggle; + forcedDesktopValue: string; + forcedMobileValue: string; + }; screenLock: StateToggle; plainTextViewPort: StateToggle; domains: { domain: string | string[]; patchSettings: Operation[]; - }[], + }[]; modifyLocalStorage: { - state: StateToggle, - changes: any[] - } -} -export type WebCompatSettings = Partial + state: StateToggle; + changes: any[]; + }; +}; +export type WebCompatSettings = Partial; export type WebCompatFeature = Feature; diff --git a/schema/json-patch.d.ts b/schema/json-patch.d.ts index 89014af23..8022176dc 100644 --- a/schema/json-patch.d.ts +++ b/schema/json-patch.d.ts @@ -1,6 +1,6 @@ export type Operation = { - op: "add" | "remove" | "replace" | "move" | "copy" | "test"; - from?: string; - path: string; - value: ValueType; + op: 'add' | 'remove' | 'replace' | 'move' | 'copy' | 'test'; + from?: string; + path: string; + value: ValueType; }; From bef8a637f16a52c92c2970605f950a81907d9b02 Mon Sep 17 00:00:00 2001 From: Sam Macbeth Date: Fri, 8 Nov 2024 12:15:34 +0100 Subject: [PATCH 17/20] Validate v3 config files --- schema/config.d.ts | 11 ++++++++++- schema/features/tracker-allowlist.d.ts | 1 + tests/config-tests.js | 7 ++++--- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/schema/config.d.ts b/schema/config.d.ts index d9765ba1f..b7c9743c3 100644 --- a/schema/config.d.ts +++ b/schema/config.d.ts @@ -8,7 +8,13 @@ import { DuckPlayerFeature } from './features/duckplayer'; export { WebCompatSettings } from './features/webcompat'; export { DuckPlayerSettings } from './features/duckplayer'; -export type ExportedSchemas = 'GenericV4Config' | 'AndroidV4Config' | 'WebCompatSettings' | 'DuckPlayerSettings'; +export type ExportedSchemas = + | 'GenericV4Config' + | 'AndroidV4Config' + | 'LegacyConfig' + | 'LegacyAndroidConfig' + | 'WebCompatSettings' + | 'DuckPlayerSettings'; /** * Defines the structure of the built V4 config output as downloaded by clients. @@ -50,3 +56,6 @@ export type AndroidV4Config = ConfigV4 & { * - Use string version numbers for minSupportedVersion */ export type GenericV4Config = ConfigV4; + +export type LegacyConfig = GenericV4Config; +export type LegacyAndroidConfig = AndroidV4Config; diff --git a/schema/features/tracker-allowlist.d.ts b/schema/features/tracker-allowlist.d.ts index c1901d9a8..de995f93c 100644 --- a/schema/features/tracker-allowlist.d.ts +++ b/schema/features/tracker-allowlist.d.ts @@ -3,6 +3,7 @@ import { Feature } from '../feature'; type AllowlistRule = { rule: string; domains: string[]; + reason?: string; }; type TrackerAllowlist = { diff --git a/tests/config-tests.js b/tests/config-tests.js index 1a1c74ebb..7d9354d28 100644 --- a/tests/config-tests.js +++ b/tests/config-tests.js @@ -4,7 +4,8 @@ const { createValidator, formatErrors } = require('./schema-validation') const platforms = require('./../platforms').map(item => item.replace('browsers/', 'extension-')) const platformSpecificSchemas = { - 'v4/android-config.json': 'AndroidV4Config' + 'v4/android-config.json': 'AndroidV4Config', + 'v3/android-config.json': 'LegacyAndroidConfig' } // Test the latest 2 versions of each platform @@ -46,8 +47,8 @@ describe('Config schema tests', () => { expect('appTrackerProtection' in config.body.features).to.be.equal(shouldContainAppTP, `appTrackerProtection expected: ${shouldContainAppTP}`) }) - it('should validate against the full configLegacy schema', () => { - const validate = createValidator(platformSpecificSchemas[config.name] || 'GenericV4Config') + it('should validate against the legacy schema', () => { + const validate = createValidator(platformSpecificSchemas[config.name] || 'LegacyConfig') expect(validate(config.body)).to.be.equal(true, formatErrors(validate.errors)) }) }) From 6afcd1412a90063dcd1b0b29f24405ef75d48835 Mon Sep 17 00:00:00 2001 From: Sam Macbeth Date: Fri, 8 Nov 2024 12:23:19 +0100 Subject: [PATCH 18/20] Use node 20 for tests --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index adaf1fca4..5b3d3267e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,10 +18,10 @@ jobs: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v4 - - name: Use Node.js 16 + - name: Use Node.js 20 uses: actions/setup-node@v4 with: - node-version: 16.x + node-version: 20.x - name: Install dependencies run: npm install - uses: ./ From a90a639fa80bbdbbd257234c4194b4c9dc644acd Mon Sep 17 00:00:00 2001 From: Sam Macbeth Date: Fri, 8 Nov 2024 13:23:11 +0100 Subject: [PATCH 19/20] Narrow webcompat patchSettings --- schema/features/webcompat.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/schema/features/webcompat.d.ts b/schema/features/webcompat.d.ts index a2d35dedd..4ae5db8c2 100644 --- a/schema/features/webcompat.d.ts +++ b/schema/features/webcompat.d.ts @@ -36,7 +36,7 @@ type FullWebCompatOptions = { plainTextViewPort: StateToggle; domains: { domain: string | string[]; - patchSettings: Operation[]; + patchSettings: Operation[]; }[]; modifyLocalStorage: { state: StateToggle; From 4e6f8f620699c08b2190301bb817c40961bf9844 Mon Sep 17 00:00:00 2001 From: Sam Macbeth Date: Fri, 8 Nov 2024 17:55:04 +0100 Subject: [PATCH 20/20] Add test for feature name matching expected regex --- tests/config-tests.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/config-tests.js b/tests/config-tests.js index 7d9354d28..46adbd904 100644 --- a/tests/config-tests.js +++ b/tests/config-tests.js @@ -36,6 +36,13 @@ describe('Config schema tests', () => { const validate = createValidator(platformSpecificSchemas[config.name] || 'GenericV4Config') expect(validate(config.body)).to.be.equal(true, formatErrors(validate.errors)) }) + + it('all features should be named correctly', () => { + const featureNameRegex = /^[a-zA-Z0-9]+$/ + for (const featureName of Object.keys(config.body.features)) { + expect(featureName).to.match(featureNameRegex) + } + }) }) }