diff --git a/deno.ts b/deno.ts index 1225fd4bee..660de98f67 100644 --- a/deno.ts +++ b/deno.ts @@ -1,3 +1,3 @@ export * from './deno/src/platform/deno.ts'; import Innertube from './deno/src/platform/deno.ts'; -export default Innertube; \ No newline at end of file +export default Innertube; diff --git a/package-lock.json b/package-lock.json index 4e0359be73..6c8adc8311 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,7 +1,7 @@ { "name": "youtubei.js", "version": "5.8.0", - "lockfileVersion": 2, + "lockfileVersion": 3, "requires": true, "packages": { "": { @@ -13,7 +13,6 @@ "license": "MIT", "dependencies": { "jintr": "^1.1.0", - "linkedom": "^0.14.12", "tslib": "^2.5.0", "undici": "^5.19.1" }, @@ -58,17 +57,89 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", - "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", + "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", "dev": true, "dependencies": { - "@babel/highlight": "^7.22.5" + "@babel/highlight": "^7.22.10", + "chalk": "^2.4.2" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@babel/compat-data": { "version": "7.22.9", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", @@ -79,21 +150,21 @@ } }, "node_modules/@babel/core": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", - "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.10.tgz", + "integrity": "sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", + "@babel/code-frame": "^7.22.10", + "@babel/generator": "^7.22.10", + "@babel/helper-compilation-targets": "^7.22.10", "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.6", - "@babel/parser": "^7.22.7", + "@babel/helpers": "^7.22.10", + "@babel/parser": "^7.22.10", "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.8", - "@babel/types": "^7.22.5", + "@babel/traverse": "^7.22.10", + "@babel/types": "^7.22.10", "convert-source-map": "^1.7.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -118,12 +189,12 @@ } }, "node_modules/@babel/generator": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz", - "integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", + "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", "dev": true, "dependencies": { - "@babel/types": "^7.22.5", + "@babel/types": "^7.22.10", "@jridgewell/gen-mapping": "^0.3.2", "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" @@ -133,9 +204,9 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz", - "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz", + "integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==", "dev": true, "dependencies": { "@babel/compat-data": "^7.22.9", @@ -146,9 +217,6 @@ }, "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { @@ -286,27 +354,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", - "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.10.tgz", + "integrity": "sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw==", "dev": true, "dependencies": { "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.6", - "@babel/types": "^7.22.5" + "@babel/traverse": "^7.22.10", + "@babel/types": "^7.22.10" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", + "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -385,9 +453,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", - "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz", + "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -573,19 +641,19 @@ } }, "node_modules/@babel/traverse": { - "version": "7.22.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz", - "integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz", + "integrity": "sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.7", + "@babel/code-frame": "^7.22.10", + "@babel/generator": "^7.22.10", "@babel/helper-environment-visitor": "^7.22.5", "@babel/helper-function-name": "^7.22.5", "@babel/helper-hoist-variables": "^7.22.5", "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/types": "^7.22.5", + "@babel/parser": "^7.22.10", + "@babel/types": "^7.22.10", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -603,9 +671,9 @@ } }, "node_modules/@babel/types": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", - "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", + "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.22.5", @@ -654,18 +722,18 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.6.2.tgz", + "integrity": "sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", - "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.1.tgz", + "integrity": "sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -686,9 +754,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", - "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.46.0.tgz", + "integrity": "sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1149,9 +1217,9 @@ } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", "dev": true, "engines": { "node": ">=6.0.0" @@ -1173,21 +1241,15 @@ "dev": true }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", + "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", "dev": true, "dependencies": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - }, "node_modules/@microsoft/tsdoc": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", @@ -1307,9 +1369,9 @@ } }, "node_modules/@types/emscripten": { - "version": "1.39.6", - "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.6.tgz", - "integrity": "sha512-H90aoynNhhkQP6DRweEjJp5vfUVdIj7tdPLsu7pq89vODD/lcugKfZOsfgwpvM6XUewEp2N5dCg1Uf3Qe55Dcg==", + "version": "1.39.7", + "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.7.tgz", + "integrity": "sha512-tLqYV94vuqDrXh515F/FOGtBcRMTPGvVV1LzLbtYDcQmmhtpf/gLYf+hikBbQk8MzOHNz37wpFfJbYAuSn8HqA==", "dev": true }, "node_modules/@types/glob": { @@ -1894,11 +1956,6 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" - }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1922,9 +1979,9 @@ } }, "node_modules/browserslist": { - "version": "4.21.9", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", - "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", + "version": "4.21.10", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", + "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", "dev": true, "funding": [ { @@ -1941,9 +1998,9 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001503", - "electron-to-chromium": "^1.4.431", - "node-releases": "^2.0.12", + "caniuse-lite": "^1.0.30001517", + "electron-to-chromium": "^1.4.477", + "node-releases": "^2.0.13", "update-browserslist-db": "^1.0.11" }, "bin": { @@ -2052,9 +2109,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001516", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001516.tgz", - "integrity": "sha512-Wmec9pCBY8CWbmI4HsjBeQLqDTqV91nFVR83DnZpYyRnPI1wePDsTg0bGLPC5VU/3OIZV1fmxEea1b+tFKe86g==", + "version": "1.0.30001519", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001519.tgz", + "integrity": "sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg==", "dev": true, "funding": [ { @@ -2320,37 +2377,6 @@ "node": ">= 8" } }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/cssom": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", - "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==" - }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2477,61 +2503,10 @@ "node": ">=6.0.0" } }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, "node_modules/electron-to-chromium": { - "version": "1.4.461", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.461.tgz", - "integrity": "sha512-1JkvV2sgEGTDXjdsaQCeSwYYuhLRphRpc+g6EHTFELJXEiznLt3/0pZ9JuAOQ5p2rI3YxKTbivtvajirIfhrEQ==", + "version": "1.4.488", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.488.tgz", + "integrity": "sha512-Dv4sTjiW7t/UWGL+H8ZkgIjtUAVZDgb/PwGWvMsCT7jipzUV/u5skbLXPFKb6iV0tiddVi/bcS2/kUrczeWgIQ==", "dev": true }, "node_modules/emittery": { @@ -2552,17 +2527,6 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -2950,27 +2914,27 @@ } }, "node_modules/eslint": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.45.0.tgz", - "integrity": "sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==", + "version": "8.46.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.46.0.tgz", + "integrity": "sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.1.0", - "@eslint/js": "8.44.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.1", + "@eslint/js": "^8.46.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.6.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.2", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -3027,9 +2991,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz", + "integrity": "sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -3039,9 +3003,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.1.tgz", - "integrity": "sha512-CvefSOsDdaYYvxChovdrPo/ZGt8d5lrJWleAc1diXRKhHGiTYEI26cvo8Kle/wGnsizoCJjK73FMg1/IkIwiNA==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -3208,9 +3172,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", - "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -3554,24 +3518,6 @@ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } - }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -3669,9 +3615,9 @@ "dev": true }, "node_modules/is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -3799,17 +3745,32 @@ } }, "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "engines": { - "node": ">=8" + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/istanbul-lib-source-maps": { @@ -3827,9 +3788,9 @@ } }, "node_modules/istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", @@ -4222,12 +4183,12 @@ } }, "node_modules/jest-resolve/node_modules/resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", + "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", "dev": true, "dependencies": { - "is-core-module": "^2.11.0", + "is-core-module": "^2.13.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, @@ -4586,23 +4547,6 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, - "node_modules/linkedom": { - "version": "0.14.26", - "resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.14.26.tgz", - "integrity": "sha512-mK6TrydfFA7phrnp+1j57ycBwFI5bGSW6YXlw9acHoqF+mP/y+FooEYYyniOt5Ot57FSKB3iwmnuQ1UUyNLm5A==", - "dependencies": { - "css-select": "^5.1.0", - "cssom": "^0.5.0", - "html-escaper": "^3.0.3", - "htmlparser2": "^8.0.1", - "uhyphen": "^0.2.0" - } - }, - "node_modules/linkedom/node_modules/html-escaper": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", - "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==" - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4890,17 +4834,6 @@ "node": ">=8" } }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -6229,9 +6162,9 @@ } }, "node_modules/tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==" }, "node_modules/tsutils": { "version": "3.21.0", @@ -6300,15 +6233,10 @@ "node": ">=14.17" } }, - "node_modules/uhyphen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/uhyphen/-/uhyphen-0.2.0.tgz", - "integrity": "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==" - }, "node_modules/undici": { - "version": "5.22.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.22.1.tgz", - "integrity": "sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==", + "version": "5.23.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.23.0.tgz", + "integrity": "sha512-1D7w+fvRsqlQ9GscLBwcAJinqcZGHUKjbOmXdlE/v8BvEGXjeWAax+341q44EuTcHXXnfyKNbKRq4Lg7OzhMmg==", "dependencies": { "busboy": "^1.6.0" }, @@ -6508,4696 +6436,5 @@ "url": "https://github.com/sponsors/sindresorhus" } } - }, - "dependencies": { - "@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "dev": true - }, - "@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@babel/code-frame": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz", - "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==", - "dev": true, - "requires": { - "@babel/highlight": "^7.22.5" - } - }, - "@babel/compat-data": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", - "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", - "dev": true - }, - "@babel/core": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.9.tgz", - "integrity": "sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.9", - "@babel/helper-compilation-targets": "^7.22.9", - "@babel/helper-module-transforms": "^7.22.9", - "@babel/helpers": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.8", - "@babel/types": "^7.22.5", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.2", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.9.tgz", - "integrity": "sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz", - "integrity": "sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.5", - "browserslist": "^4.21.9", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", - "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", - "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", - "dev": true, - "requires": { - "@babel/template": "^7.22.5", - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-module-imports": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", - "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-module-transforms": { - "version": "7.22.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", - "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.5" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - }, - "@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", - "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", - "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", - "dev": true - }, - "@babel/helpers": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.6.tgz", - "integrity": "sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA==", - "dev": true, - "requires": { - "@babel/template": "^7.22.5", - "@babel/traverse": "^7.22.6", - "@babel/types": "^7.22.5" - } - }, - "@babel/highlight": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz", - "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.22.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.22.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", - "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==", - "dev": true - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/template": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", - "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/parser": "^7.22.5", - "@babel/types": "^7.22.5" - } - }, - "@babel/traverse": { - "version": "7.22.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.8.tgz", - "integrity": "sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.5", - "@babel/generator": "^7.22.7", - "@babel/helper-environment-visitor": "^7.22.5", - "@babel/helper-function-name": "^7.22.5", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.22.7", - "@babel/types": "^7.22.5", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz", - "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.5", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@esbuild/linux-loong64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz", - "integrity": "sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==", - "dev": true, - "optional": true - }, - "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^3.3.0" - } - }, - "@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", - "dev": true - }, - "@eslint/eslintrc": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", - "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - } - }, - "@eslint/js": { - "version": "8.44.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", - "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", - "dev": true - }, - "@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true - }, - "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jest/console": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", - "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0" - } - }, - "@jest/core": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-28.1.3.tgz", - "integrity": "sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==", - "dev": true, - "requires": { - "@jest/console": "^28.1.3", - "@jest/reporters": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^28.1.3", - "jest-config": "^28.1.3", - "jest-haste-map": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-resolve-dependencies": "^28.1.3", - "jest-runner": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "jest-watcher": "^28.1.3", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "@jest/environment": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", - "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "jest-mock": "^28.1.3" - } - }, - "@jest/expect": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz", - "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==", - "dev": true, - "requires": { - "expect": "^28.1.3", - "jest-snapshot": "^28.1.3" - } - }, - "@jest/expect-utils": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", - "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", - "dev": true, - "requires": { - "jest-get-type": "^28.0.2" - } - }, - "@jest/fake-timers": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", - "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@sinonjs/fake-timers": "^9.1.2", - "@types/node": "*", - "jest-message-util": "^28.1.3", - "jest-mock": "^28.1.3", - "jest-util": "^28.1.3" - } - }, - "@jest/globals": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", - "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", - "dev": true, - "requires": { - "@jest/environment": "^28.1.3", - "@jest/expect": "^28.1.3", - "@jest/types": "^28.1.3" - } - }, - "@jest/reporters": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz", - "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@jridgewell/trace-mapping": "^0.3.13", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^5.1.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "jest-worker": "^28.1.3", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "@jest/schemas": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", - "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.24.1" - } - }, - "@jest/source-map": { - "version": "28.1.2", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", - "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.13", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - } - }, - "@jest/test-result": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", - "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", - "dev": true, - "requires": { - "@jest/console": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz", - "integrity": "sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==", - "dev": true, - "requires": { - "@jest/test-result": "^28.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "slash": "^3.0.0" - } - }, - "@jest/transform": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", - "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/types": "^28.1.3", - "@jridgewell/trace-mapping": "^0.3.13", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-util": "^28.1.3", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.1" - } - }, - "@jest/types": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", - "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", - "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.18", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", - "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" - }, - "dependencies": { - "@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true - } - } - }, - "@microsoft/tsdoc": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", - "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", - "dev": true - }, - "@microsoft/tsdoc-config": { - "version": "0.16.2", - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", - "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", - "dev": true, - "requires": { - "@microsoft/tsdoc": "0.14.2", - "ajv": "~6.12.6", - "jju": "~1.4.0", - "resolve": "~1.19.0" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@sinclair/typebox": { - "version": "0.24.51", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", - "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", - "dev": true - }, - "@sinonjs/commons": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", - "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", - "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@types/babel__core": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", - "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", - "dev": true, - "requires": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", - "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", - "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.20.1", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", - "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", - "dev": true, - "requires": { - "@babel/types": "^7.20.7" - } - }, - "@types/emscripten": { - "version": "1.39.6", - "resolved": "https://registry.npmjs.org/@types/emscripten/-/emscripten-1.39.6.tgz", - "integrity": "sha512-H90aoynNhhkQP6DRweEjJp5vfUVdIj7tdPLsu7pq89vODD/lcugKfZOsfgwpvM6XUewEp2N5dCg1Uf3Qe55Dcg==", - "dev": true - }, - "@types/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", - "dev": true, - "requires": { - "@types/minimatch": "^5.1.2", - "@types/node": "*" - } - }, - "@types/graceful-fs": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", - "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "28.1.8", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-28.1.8.tgz", - "integrity": "sha512-8TJkV++s7B6XqnDrzR1m/TT0A0h948Pnl/097veySPN67VRAgQ4gZ7n2KfJo2rVq6njQjdxU3GCCyDvAeuHoiw==", - "dev": true, - "requires": { - "expect": "^28.0.0", - "pretty-format": "^28.0.0" - } - }, - "@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", - "dev": true - }, - "@types/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", - "dev": true - }, - "@types/minimist": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", - "integrity": "sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ==", - "dev": true - }, - "@types/node": { - "version": "17.0.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", - "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", - "dev": true - }, - "@types/normalize-package-data": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", - "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", - "dev": true - }, - "@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", - "dev": true - }, - "@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", - "dev": true - }, - "@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true - }, - "@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", - "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", - "dev": true, - "requires": { - "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/type-utils": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "natural-compare-lite": "^1.4.0", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/parser": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", - "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - } - }, - "@typescript-eslint/type-utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", - "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "5.62.0", - "@typescript-eslint/utils": "5.62.0", - "debug": "^4.3.4", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - } - }, - "@yarnpkg/fslib": { - "version": "2.10.3", - "resolved": "https://registry.npmjs.org/@yarnpkg/fslib/-/fslib-2.10.3.tgz", - "integrity": "sha512-41H+Ga78xT9sHvWLlFOZLIhtU6mTGZ20pZ29EiZa97vnxdohJD2AF42rCoAoWfqUz486xY6fhjMH+DYEM9r14A==", - "dev": true, - "requires": { - "@yarnpkg/libzip": "^2.3.0", - "tslib": "^1.13.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "@yarnpkg/libzip": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@yarnpkg/libzip/-/libzip-2.3.0.tgz", - "integrity": "sha512-6xm38yGVIa6mKm/DUCF2zFFJhERh/QWp1ufm4cNUvxsONBmfPg8uZ9pZBdOmF6qFGr/HlT6ABBkCSx/dlEtvWg==", - "dev": true, - "requires": { - "@types/emscripten": "^1.39.6", - "tslib": "^1.13.0" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "acorn": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", - "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==" - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} - }, - "aggregate-error": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", - "integrity": "sha512-0poP0T7el6Vq3rstR8Mn4V/IQrpBLO6POkUSrN7RhyY+GF/InCFShQzsQ39T25gkHhLgSLByyAz+Kjb+c2L98w==", - "dev": true, - "requires": { - "clean-stack": "^4.0.0", - "indent-string": "^5.0.0" - } - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - } - } - }, - "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 - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "arrify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-3.0.0.tgz", - "integrity": "sha512-tLkvA81vQG/XqE2mjDkGQHoOINtMHtysSnemrmoGe6PydDPMRbVugqyk4A6V/WDWEfm3l+0d8anA9r8cv/5Jaw==", - "dev": true - }, - "babel-jest": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz", - "integrity": "sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==", - "dev": true, - "requires": { - "@jest/transform": "^28.1.3", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^28.1.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz", - "integrity": "sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz", - "integrity": "sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^28.1.3", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "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 - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browserslist": { - "version": "4.21.9", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz", - "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001503", - "electron-to-chromium": "^1.4.431", - "node-releases": "^2.0.12", - "update-browserslist-db": "^1.0.11" - } - }, - "bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "requires": { - "streamsearch": "^1.1.0" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "camelcase-keys": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-7.0.2.tgz", - "integrity": "sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==", - "dev": true, - "requires": { - "camelcase": "^6.3.0", - "map-obj": "^4.1.0", - "quick-lru": "^5.1.1", - "type-fest": "^1.2.1" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - }, - "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "dev": true - } - } - }, - "caniuse-lite": { - "version": "1.0.30001516", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001516.tgz", - "integrity": "sha512-Wmec9pCBY8CWbmI4HsjBeQLqDTqV91nFVR83DnZpYyRnPI1wePDsTg0bGLPC5VU/3OIZV1fmxEea1b+tFKe86g==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", - "dev": true - }, - "cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "clean-stack": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-4.2.0.tgz", - "integrity": "sha512-LYv6XPxoyODi36Dp976riBtSY27VmFo+MKqEU9QCCWyTrdEPDog+RWA7xQWHi6Vbp61j5c4cdzzX1NidnwtUWg==", - "dev": true, - "requires": { - "escape-string-regexp": "5.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "dev": true - } - } - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "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==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "convert-source-map": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", - "dev": true - }, - "core-js": { - "version": "3.18.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.18.1.tgz", - "integrity": "sha512-vJlUi/7YdlCZeL6fXvWNaLUPh/id12WXj3MbkMw5uOyF0PfWPBNOCNbs53YqgrvtujLNlt9JQpruyIKkUZ+PKA==", - "dev": true - }, - "cp-file": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/cp-file/-/cp-file-9.1.0.tgz", - "integrity": "sha512-3scnzFj/94eb7y4wyXRWwvzLFaQp87yyfTnChIjlfYrVqp5lVO3E2hIJMeQIltUT0K2ZAB3An1qXcBmwGyvuwA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "nested-error-stacks": "^2.0.0", - "p-event": "^4.1.0" - } - }, - "cpy": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/cpy/-/cpy-9.0.1.tgz", - "integrity": "sha512-D9U0DR5FjTCN3oMTcFGktanHnAG5l020yvOCR1zKILmAyPP7I/9pl6NFgRbDcmSENtbK1sQLBz1p9HIOlroiNg==", - "dev": true, - "requires": { - "arrify": "^3.0.0", - "cp-file": "^9.1.0", - "globby": "^13.1.1", - "junk": "^4.0.0", - "micromatch": "^4.0.4", - "nested-error-stacks": "^2.1.0", - "p-filter": "^3.0.0", - "p-map": "^5.3.0" - }, - "dependencies": { - "globby": { - "version": "13.2.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", - "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", - "dev": true, - "requires": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.3.0", - "ignore": "^5.2.4", - "merge2": "^1.4.1", - "slash": "^4.0.0" - } - }, - "slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true - } - } - }, - "cpy-cli": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/cpy-cli/-/cpy-cli-4.2.0.tgz", - "integrity": "sha512-b04b+cbdr29CdpREPKw/itrfjO43Ty0Aj7wRM6M6LoE4GJxZJCk9Xp+Eu1IqztkKh3LxIBt1tDplENsa6KYprg==", - "dev": true, - "requires": { - "cpy": "^9.0.0", - "meow": "^10.1.2" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "requires": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - } - }, - "css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" - }, - "cssom": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", - "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==" - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "decamelize": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz", - "integrity": "sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==", - "dev": true - }, - "decamelize-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", - "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", - "dev": true, - "requires": { - "decamelize": "^1.1.0", - "map-obj": "^1.0.0" - }, - "dependencies": { - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true - }, - "map-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", - "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", - "dev": true - } - } - }, - "dedent": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", - "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", - "dev": true - }, - "deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "diff-sequences": { - "version": "28.1.1", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", - "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - } - }, - "domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==" - }, - "domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "requires": { - "domelementtype": "^2.3.0" - } - }, - "domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "requires": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - } - }, - "electron-to-chromium": { - "version": "1.4.461", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.461.tgz", - "integrity": "sha512-1JkvV2sgEGTDXjdsaQCeSwYYuhLRphRpc+g6EHTFELJXEiznLt3/0pZ9JuAOQ5p2rI3YxKTbivtvajirIfhrEQ==", - "dev": true - }, - "emittery": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", - "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", - "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==", - "dev": true - }, - "entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "esbuild": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.54.tgz", - "integrity": "sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==", - "dev": true, - "requires": { - "@esbuild/linux-loong64": "0.14.54", - "esbuild-android-64": "0.14.54", - "esbuild-android-arm64": "0.14.54", - "esbuild-darwin-64": "0.14.54", - "esbuild-darwin-arm64": "0.14.54", - "esbuild-freebsd-64": "0.14.54", - "esbuild-freebsd-arm64": "0.14.54", - "esbuild-linux-32": "0.14.54", - "esbuild-linux-64": "0.14.54", - "esbuild-linux-arm": "0.14.54", - "esbuild-linux-arm64": "0.14.54", - "esbuild-linux-mips64le": "0.14.54", - "esbuild-linux-ppc64le": "0.14.54", - "esbuild-linux-riscv64": "0.14.54", - "esbuild-linux-s390x": "0.14.54", - "esbuild-netbsd-64": "0.14.54", - "esbuild-openbsd-64": "0.14.54", - "esbuild-sunos-64": "0.14.54", - "esbuild-windows-32": "0.14.54", - "esbuild-windows-64": "0.14.54", - "esbuild-windows-arm64": "0.14.54" - } - }, - "esbuild-android-64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz", - "integrity": "sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==", - "dev": true, - "optional": true - }, - "esbuild-android-arm64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz", - "integrity": "sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==", - "dev": true, - "optional": true - }, - "esbuild-darwin-64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz", - "integrity": "sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==", - "dev": true, - "optional": true - }, - "esbuild-darwin-arm64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz", - "integrity": "sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==", - "dev": true, - "optional": true - }, - "esbuild-freebsd-64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz", - "integrity": "sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==", - "dev": true, - "optional": true - }, - "esbuild-freebsd-arm64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz", - "integrity": "sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==", - "dev": true, - "optional": true - }, - "esbuild-linux-32": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz", - "integrity": "sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==", - "dev": true, - "optional": true - }, - "esbuild-linux-64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz", - "integrity": "sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==", - "dev": true, - "optional": true - }, - "esbuild-linux-arm": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz", - "integrity": "sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==", - "dev": true, - "optional": true - }, - "esbuild-linux-arm64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz", - "integrity": "sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==", - "dev": true, - "optional": true - }, - "esbuild-linux-mips64le": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz", - "integrity": "sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==", - "dev": true, - "optional": true - }, - "esbuild-linux-ppc64le": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz", - "integrity": "sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==", - "dev": true, - "optional": true - }, - "esbuild-linux-riscv64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz", - "integrity": "sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==", - "dev": true, - "optional": true - }, - "esbuild-linux-s390x": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz", - "integrity": "sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==", - "dev": true, - "optional": true - }, - "esbuild-netbsd-64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz", - "integrity": "sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==", - "dev": true, - "optional": true - }, - "esbuild-openbsd-64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz", - "integrity": "sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==", - "dev": true, - "optional": true - }, - "esbuild-sunos-64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz", - "integrity": "sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==", - "dev": true, - "optional": true - }, - "esbuild-windows-32": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz", - "integrity": "sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==", - "dev": true, - "optional": true - }, - "esbuild-windows-64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz", - "integrity": "sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==", - "dev": true, - "optional": true - }, - "esbuild-windows-arm64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz", - "integrity": "sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==", - "dev": true, - "optional": true - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint": { - "version": "8.45.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.45.0.tgz", - "integrity": "sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.1.0", - "@eslint/js": "8.44.0", - "@humanwhocodes/config-array": "^0.11.10", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.6.0", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "dependencies": { - "eslint-scope": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.1.tgz", - "integrity": "sha512-CvefSOsDdaYYvxChovdrPo/ZGt8d5lrJWleAc1diXRKhHGiTYEI26cvo8Kle/wGnsizoCJjK73FMg1/IkIwiNA==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "eslint-plugin-tsdoc": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.17.tgz", - "integrity": "sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==", - "dev": true, - "requires": { - "@microsoft/tsdoc": "0.14.2", - "@microsoft/tsdoc-config": "0.16.2" - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", - "dev": true - }, - "espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "requires": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "expect": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", - "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", - "dev": true, - "requires": { - "@jest/expect-utils": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", - "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, - "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "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==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0" - } - }, - "minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, - "hard-rejection": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", - "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "hosted-git-info": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", - "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "requires": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true - }, - "indent-string": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz", - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "is-core-module": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz", - "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true - }, - "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 - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", - "dev": true - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "istanbul-reports": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", - "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "jest": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.3.tgz", - "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==", - "dev": true, - "requires": { - "@jest/core": "^28.1.3", - "@jest/types": "^28.1.3", - "import-local": "^3.0.2", - "jest-cli": "^28.1.3" - } - }, - "jest-changed-files": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-28.1.3.tgz", - "integrity": "sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==", - "dev": true, - "requires": { - "execa": "^5.0.0", - "p-limit": "^3.1.0" - } - }, - "jest-circus": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-28.1.3.tgz", - "integrity": "sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==", - "dev": true, - "requires": { - "@jest/environment": "^28.1.3", - "@jest/expect": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^0.7.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^28.1.3", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "p-limit": "^3.1.0", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-cli": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz", - "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==", - "dev": true, - "requires": { - "@jest/core": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "import-local": "^3.0.2", - "jest-config": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "prompts": "^2.0.1", - "yargs": "^17.3.1" - } - }, - "jest-config": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", - "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^28.1.3", - "@jest/types": "^28.1.3", - "babel-jest": "^28.1.3", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^28.1.3", - "jest-environment-node": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-runner": "^28.1.3", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "jest-diff": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", - "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^28.1.1", - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" - } - }, - "jest-docblock": { - "version": "28.1.1", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz", - "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-28.1.3.tgz", - "integrity": "sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "jest-get-type": "^28.0.2", - "jest-util": "^28.1.3", - "pretty-format": "^28.1.3" - } - }, - "jest-environment-node": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz", - "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==", - "dev": true, - "requires": { - "@jest/environment": "^28.1.3", - "@jest/fake-timers": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "jest-mock": "^28.1.3", - "jest-util": "^28.1.3" - } - }, - "jest-get-type": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", - "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", - "dev": true - }, - "jest-haste-map": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", - "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^28.0.2", - "jest-util": "^28.1.3", - "jest-worker": "^28.1.3", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - } - }, - "jest-leak-detector": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", - "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", - "dev": true, - "requires": { - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" - } - }, - "jest-matcher-utils": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", - "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^28.1.3", - "jest-get-type": "^28.0.2", - "pretty-format": "^28.1.3" - } - }, - "jest-message-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", - "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^28.1.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^28.1.3", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-mock": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", - "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*" - } - }, - "jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "requires": {} - }, - "jest-regex-util": { - "version": "28.0.2", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", - "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", - "dev": true - }, - "jest-resolve": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", - "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^28.1.3", - "jest-validate": "^28.1.3", - "resolve": "^1.20.0", - "resolve.exports": "^1.1.0", - "slash": "^3.0.0" - }, - "dependencies": { - "resolve": { - "version": "1.22.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.2.tgz", - "integrity": "sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g==", - "dev": true, - "requires": { - "is-core-module": "^2.11.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - } - } - }, - "jest-resolve-dependencies": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz", - "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==", - "dev": true, - "requires": { - "jest-regex-util": "^28.0.2", - "jest-snapshot": "^28.1.3" - } - }, - "jest-runner": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", - "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", - "dev": true, - "requires": { - "@jest/console": "^28.1.3", - "@jest/environment": "^28.1.3", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.10.2", - "graceful-fs": "^4.2.9", - "jest-docblock": "^28.1.1", - "jest-environment-node": "^28.1.3", - "jest-haste-map": "^28.1.3", - "jest-leak-detector": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-resolve": "^28.1.3", - "jest-runtime": "^28.1.3", - "jest-util": "^28.1.3", - "jest-watcher": "^28.1.3", - "jest-worker": "^28.1.3", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - } - }, - "jest-runtime": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", - "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", - "dev": true, - "requires": { - "@jest/environment": "^28.1.3", - "@jest/fake-timers": "^28.1.3", - "@jest/globals": "^28.1.3", - "@jest/source-map": "^28.1.2", - "@jest/test-result": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "execa": "^5.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-mock": "^28.1.3", - "jest-regex-util": "^28.0.2", - "jest-resolve": "^28.1.3", - "jest-snapshot": "^28.1.3", - "jest-util": "^28.1.3", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "jest-snapshot": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", - "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/traverse": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^28.1.3", - "@jest/transform": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/babel__traverse": "^7.0.6", - "@types/prettier": "^2.1.5", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^28.1.3", - "graceful-fs": "^4.2.9", - "jest-diff": "^28.1.3", - "jest-get-type": "^28.0.2", - "jest-haste-map": "^28.1.3", - "jest-matcher-utils": "^28.1.3", - "jest-message-util": "^28.1.3", - "jest-util": "^28.1.3", - "natural-compare": "^1.4.0", - "pretty-format": "^28.1.3", - "semver": "^7.3.5" - } - }, - "jest-util": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", - "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "jest-validate": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz", - "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==", - "dev": true, - "requires": { - "@jest/types": "^28.1.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^28.0.2", - "leven": "^3.1.0", - "pretty-format": "^28.1.3" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - } - } - }, - "jest-watcher": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", - "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", - "dev": true, - "requires": { - "@jest/test-result": "^28.1.3", - "@jest/types": "^28.1.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.10.2", - "jest-util": "^28.1.3", - "string-length": "^4.0.1" - } - }, - "jest-worker": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", - "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jintr": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jintr/-/jintr-1.1.0.tgz", - "integrity": "sha512-Tu9wk3BpN2v+kb8yT6YBtue+/nbjeLFv4vvVC4PJ7oCidHKbifWhvORrAbQfxVIQZG+67am/mDagpiGSVtvrZg==", - "requires": { - "acorn": "^8.8.0" - } - }, - "jju": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", - "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "junk": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/junk/-/junk-4.0.1.tgz", - "integrity": "sha512-Qush0uP+G8ZScpGMZvHUiRfI0YBWuB3gVBYlI0v0vvOJt5FLicco+IkP0a50LqTTQhmts/m6tP5SWE+USyIvcQ==", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "linkedom": { - "version": "0.14.26", - "resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.14.26.tgz", - "integrity": "sha512-mK6TrydfFA7phrnp+1j57ycBwFI5bGSW6YXlw9acHoqF+mP/y+FooEYYyniOt5Ot57FSKB3iwmnuQ1UUyNLm5A==", - "requires": { - "css-select": "^5.1.0", - "cssom": "^0.5.0", - "html-escaper": "^3.0.3", - "htmlparser2": "^8.0.1", - "uhyphen": "^0.2.0" - }, - "dependencies": { - "html-escaper": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", - "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==" - } - } - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - } - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "requires": { - "tmpl": "1.0.5" - } - }, - "map-obj": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", - "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", - "dev": true - }, - "meow": { - "version": "10.1.5", - "resolved": "https://registry.npmjs.org/meow/-/meow-10.1.5.tgz", - "integrity": "sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==", - "dev": true, - "requires": { - "@types/minimist": "^1.2.2", - "camelcase-keys": "^7.0.0", - "decamelize": "^5.0.0", - "decamelize-keys": "^1.1.0", - "hard-rejection": "^2.1.0", - "minimist-options": "4.1.0", - "normalize-package-data": "^3.0.2", - "read-pkg-up": "^8.0.0", - "redent": "^4.0.0", - "trim-newlines": "^4.0.2", - "type-fest": "^1.2.2", - "yargs-parser": "^20.2.9" - }, - "dependencies": { - "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "dev": true - } - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", - "dev": true, - "requires": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist-options": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", - "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", - "dev": true, - "requires": { - "arrify": "^1.0.1", - "is-plain-obj": "^1.1.0", - "kind-of": "^6.0.3" - }, - "dependencies": { - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", - "dev": true - } - } - }, - "mri": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", - "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true - }, - "natural-compare-lite": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", - "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", - "dev": true - }, - "nested-error-stacks": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nested-error-stacks/-/nested-error-stacks-2.1.1.tgz", - "integrity": "sha512-9iN1ka/9zmX1ZvLV9ewJYEk9h7RyRRtqdK0woXcqohu8EWIerfPUjYJPg0ULy0UqP7cslmdGc8xKDJcojlKiaw==", - "dev": true - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", - "dev": true - }, - "normalize-package-data": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", - "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", - "dev": true, - "requires": { - "hosted-git-info": "^4.0.1", - "is-core-module": "^2.5.0", - "semver": "^7.3.4", - "validate-npm-package-license": "^3.0.1" - } - }, - "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 - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "requires": { - "boolbase": "^1.0.0" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dev": true, - "requires": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - } - }, - "p-event": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", - "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", - "dev": true, - "requires": { - "p-timeout": "^3.1.0" - } - }, - "p-filter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-filter/-/p-filter-3.0.0.tgz", - "integrity": "sha512-QtoWLjXAW++uTX67HZQz1dbTpqBfiidsB6VtQUC9iR85S120+s0T5sO6s+B5MLzFcZkrEd/DGMmCjR+f2Qpxwg==", - "dev": true, - "requires": { - "p-map": "^5.1.0" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", - "dev": true - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } - }, - "p-map": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-5.5.0.tgz", - "integrity": "sha512-VFqfGDHlx87K66yZrNdI4YGtD70IRyd+zSvgks6mzHPRNkoKy+9EKP4SFC77/vTTQYmRmti7dvqC+m5jBrBAcg==", - "dev": true, - "requires": { - "aggregate-error": "^4.0.0" - } - }, - "p-timeout": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", - "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", - "dev": true, - "requires": { - "p-finally": "^1.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true - }, - "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 - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "pbkit": { - "version": "0.0.59", - "resolved": "https://registry.npmjs.org/pbkit/-/pbkit-0.0.59.tgz", - "integrity": "sha512-bBivFTDbmQPqUukFegIK2ScTSNnvdp5p2ghsdpivL1ZUZ/mkzy3rGDT+PvX1enoJJTY2G6nV1Zp4G+SM5vjMJg==", - "dev": true, - "requires": { - "@yarnpkg/fslib": "^2.6.0-rc.8", - "@yarnpkg/libzip": "^2.2.2", - "core-js": "3.18.1", - "mri": "^1.2.0" - } - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true - }, - "picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true - }, - "pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "pretty-format": { - "version": "28.1.3", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", - "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", - "dev": true, - "requires": { - "@jest/schemas": "^28.1.3", - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - } - } - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "quick-lru": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", - "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", - "dev": true - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "read-pkg": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-6.0.0.tgz", - "integrity": "sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^3.0.2", - "parse-json": "^5.2.0", - "type-fest": "^1.0.1" - }, - "dependencies": { - "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-8.0.0.tgz", - "integrity": "sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==", - "dev": true, - "requires": { - "find-up": "^5.0.0", - "read-pkg": "^6.0.0", - "type-fest": "^1.0.1" - }, - "dependencies": { - "type-fest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", - "dev": true - } - } - }, - "redent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-4.0.0.tgz", - "integrity": "sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==", - "dev": true, - "requires": { - "indent-string": "^5.0.0", - "strip-indent": "^4.0.0" - } - }, - "replace": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/replace/-/replace-1.2.2.tgz", - "integrity": "sha512-C4EDifm22XZM2b2JOYe6Mhn+lBsLBAvLbK8drfUQLTfD1KYl/n3VaW/CDju0Ny4w3xTtegBpg8YNSpFJPUDSjA==", - "dev": true, - "requires": { - "chalk": "2.4.2", - "minimatch": "3.0.5", - "yargs": "^15.3.1" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "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==", - "dev": true, - "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==", - "dev": true, - "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==", - "dev": true - } - } - }, - "y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", - "dev": true, - "requires": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "resolve.exports": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", - "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "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 - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "spdx-correct": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", - "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz", - "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } - } - }, - "streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" - }, - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "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, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true - }, - "strip-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz", - "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==", - "dev": true, - "requires": { - "min-indent": "^1.0.1" - } - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-hyperlinks": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "dev": true, - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "dependencies": { - "glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, - "tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "trim-newlines": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-4.1.1.tgz", - "integrity": "sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==", - "dev": true - }, - "ts-jest": { - "version": "28.0.8", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-28.0.8.tgz", - "integrity": "sha512-5FaG0lXmRPzApix8oFG8RKjAz4ehtm8yMKOTy5HX3fY6W8kmvOrmcY0hKDElW52FJov+clhUbrKAqofnj4mXTg==", - "dev": true, - "requires": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^28.0.0", - "json5": "^2.2.1", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "7.x", - "yargs-parser": "^21.0.1" - }, - "dependencies": { - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true - } - } - }, - "tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==" - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", - "dev": true - }, - "uhyphen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/uhyphen/-/uhyphen-0.2.0.tgz", - "integrity": "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==" - }, - "undici": { - "version": "5.22.1", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.22.1.tgz", - "integrity": "sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw==", - "requires": { - "busboy": "^1.6.0" - } - }, - "update-browserslist-db": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", - "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", - "dev": true, - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "v8-to-istanbul": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", - "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0" - } - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "requires": { - "makeerror": "1.0.12" - } - }, - "which": { - "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" - } - }, - "which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - } - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "dependencies": { - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true - } - } - }, - "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true - } } } diff --git a/package.json b/package.json index e9326f5751..59c8e0fa8f 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "build:parser-map": "node ./scripts/gen-parser-map.mjs", "build:proto": "npx pb-gen-ts --entry-path=\"src/proto\" --out-dir=\"src/proto/generated\" --ext-in-import=\".js\"", "build:esm": "npx tsc", - "build:deno": "npx cpy ./src ./deno && npx cpy ./package.json ./deno && npx replace \".js';\" \".ts';\" ./deno -r && npx replace '.js\";' '.ts\";' ./deno -r && npx replace \"'linkedom';\" \"'https://esm.sh/linkedom';\" ./deno -r && npx replace \"'jintr';\" \"'https://esm.sh/jintr';\" ./deno -r && npx replace \"new Jinter.default\" \"new Jinter\" ./deno -r", + "build:deno": "npx cpy ./src ./deno && npx esbuild ./src/utils/DashManifest.tsx --keep-names --format=esm --platform=neutral --target=es2020 --outfile=./deno/src/utils/DashManifest.js && npx cpy ./package.json ./deno && npx replace \".js';\" \".ts';\" ./deno -r && npx replace '.js\";' '.ts\";' ./deno -r && npx replace \"'./DashManifest.ts';\" \"'./DashManifest.js';\" ./deno -r && npx replace \"'jintr';\" \"'https://esm.sh/jintr';\" ./deno -r", "bundle:node": "npx esbuild ./dist/src/platform/node.js --bundle --target=node10 --keep-names --format=cjs --platform=node --outfile=./bundle/node.cjs --external:jintr --external:undici --external:linkedom --external:tslib --sourcemap --banner:js=\"/* eslint-disable */\"", "bundle:browser": "npx esbuild ./dist/src/platform/web.js --banner:js=\"/* eslint-disable */\" --bundle --target=chrome58 --keep-names --format=esm --sourcemap --define:global=globalThis --conditions=module --outfile=./bundle/browser.js --platform=browser", "bundle:browser:prod": "npm run bundle:browser -- --outfile=./bundle/browser.min.js --minify", @@ -86,7 +86,6 @@ "license": "MIT", "dependencies": { "jintr": "^1.1.0", - "linkedom": "^0.14.12", "tslib": "^2.5.0", "undici": "^5.19.1" }, diff --git a/src/Innertube.ts b/src/Innertube.ts index e9f4944f1c..6cdcf97387 100644 --- a/src/Innertube.ts +++ b/src/Innertube.ts @@ -19,7 +19,7 @@ import { Kids, Music, Studio } from './core/clients/index.js'; import { AccountManager, InteractionManager, PlaylistManager } from './core/managers/index.js'; import { Feed, TabbedFeed } from './core/mixins/index.js'; -import Proto from './proto/index.js'; +import * as Proto from './proto/index.js'; import * as Constants from './utils/Constants.js'; import { InnertubeError, generateRandomString, throwIfMissing } from './utils/Utils.js'; @@ -38,7 +38,7 @@ import { GetUnseenCountEndpoint } from './core/endpoints/notification/index.js'; import type { ApiResponse } from './core/Actions.js'; import type { IBrowseResponse, IParsedResponse } from './parser/types/index.js'; import type { INextRequest } from './types/index.js'; -import type { DownloadOptions, FormatOptions } from './utils/FormatUtils.js'; +import type { DownloadOptions, FormatOptions } from './types/FormatUtils.js'; export type InnertubeConfig = SessionOptions; diff --git a/src/core/Session.ts b/src/core/Session.ts index e0c4a70c35..0a42627dcf 100644 --- a/src/core/Session.ts +++ b/src/core/Session.ts @@ -3,7 +3,7 @@ import EventEmitterLike from '../utils/EventEmitterLike.js'; import Actions from './Actions.js'; import Player from './Player.js'; -import Proto from '../proto/index.js'; +import * as Proto from '../proto/index.js'; import type { ICache } from '../types/Cache.js'; import type { FetchFunction } from '../types/PlatformShim.js'; import HTTPClient from '../utils/HTTPClient.js'; diff --git a/src/core/clients/Music.ts b/src/core/clients/Music.ts index 4e2b5156c8..b9e7fbeb36 100644 --- a/src/core/clients/Music.ts +++ b/src/core/clients/Music.ts @@ -18,7 +18,7 @@ import PlaylistPanel from '../../parser/classes/PlaylistPanel.js'; import SearchSuggestionsSection from '../../parser/classes/SearchSuggestionsSection.js'; import SectionList from '../../parser/classes/SectionList.js'; import Tab from '../../parser/classes/Tab.js'; -import Proto from '../../proto/index.js'; +import * as Proto from '../../proto/index.js'; import type { ObservedArray, YTNode } from '../../parser/helpers.js'; import type { MusicSearchFilters } from '../../types/index.js'; diff --git a/src/core/clients/Studio.ts b/src/core/clients/Studio.ts index a969b10052..ceaa70ed32 100644 --- a/src/core/clients/Studio.ts +++ b/src/core/clients/Studio.ts @@ -1,4 +1,4 @@ -import Proto from '../../proto/index.js'; +import * as Proto from '../../proto/index.js'; import * as Constants from '../../utils/Constants.js'; import { InnertubeError, MissingParamError, Platform } from '../../utils/Utils.js'; diff --git a/src/core/managers/AccountManager.ts b/src/core/managers/AccountManager.ts index b284c397e8..9ae8c8ea5b 100644 --- a/src/core/managers/AccountManager.ts +++ b/src/core/managers/AccountManager.ts @@ -3,7 +3,7 @@ import Analytics from '../../parser/youtube/Analytics.js'; import Settings from '../../parser/youtube/Settings.js'; import TimeWatched from '../../parser/youtube/TimeWatched.js'; -import Proto from '../../proto/index.js'; +import * as Proto from '../../proto/index.js'; import { InnertubeError } from '../../utils/Utils.js'; import { Account, BrowseEndpoint, Channel } from '../endpoints/index.js'; diff --git a/src/core/managers/InteractionManager.ts b/src/core/managers/InteractionManager.ts index 151bf04401..8859ae4f28 100644 --- a/src/core/managers/InteractionManager.ts +++ b/src/core/managers/InteractionManager.ts @@ -1,4 +1,4 @@ -import Proto from '../../proto/index.js'; +import * as Proto from '../../proto/index.js'; import type Actions from '../Actions.js'; import type { ApiResponse } from '../Actions.js'; diff --git a/src/core/mixins/MediaInfo.ts b/src/core/mixins/MediaInfo.ts index 1506f49234..3d0d5bd1a2 100644 --- a/src/core/mixins/MediaInfo.ts +++ b/src/core/mixins/MediaInfo.ts @@ -1,14 +1,15 @@ import type { ApiResponse } from '../Actions.js'; import type Actions from '../Actions.js'; import * as Constants from '../../utils/Constants.js'; -import type { DownloadOptions, FormatFilter, FormatOptions, URLTransformer } from '../../utils/FormatUtils.js'; -import FormatUtils from '../../utils/FormatUtils.js'; +import type { DownloadOptions, FormatFilter, FormatOptions, URLTransformer } from '../../types/FormatUtils.js'; +import * as FormatUtils from '../../utils/FormatUtils.js'; import { InnertubeError } from '../../utils/Utils.js'; import type Format from '../../parser/classes/misc/Format.js'; import type { INextResponse, IPlayerResponse } from '../../parser/index.js'; import Parser from '../../parser/index.js'; import type { DashOptions } from '../../types/DashOptions.js'; import PlayerStoryboardSpec from '../../parser/classes/PlayerStoryboardSpec.js'; +import { getStreamingInfo } from '../../utils/StreamingInfo.js'; export default class MediaInfo { #page: [IPlayerResponse, INextResponse?]; @@ -52,6 +53,21 @@ export default class MediaInfo { return FormatUtils.toDash(this.streaming_data, url_transformer, format_filter, this.#cpn, this.#actions.session.player, this.#actions, storyboards); } + /** + * Get a cleaned up representation of the adaptive_formats + */ + getStreamingInfo(url_transformer?: URLTransformer, format_filter?: FormatFilter) { + return getStreamingInfo( + this.streaming_data, + url_transformer, + format_filter, + this.cpn, + this.#actions.session.player, + this.#actions, + this.#page[0].storyboards?.is(PlayerStoryboardSpec) ? this.#page[0].storyboards : undefined + ); + } + /** * Selects the format that best matches the given options. * @param options - Options diff --git a/src/parser/README.md b/src/parser/README.md index 402eec09f7..d2541bfefb 100644 --- a/src/parser/README.md +++ b/src/parser/README.md @@ -310,7 +310,7 @@ const example_data = { // The first argument is the name of the class, the second is the data you have for the node. // It will return a class that extends YTNode. -const Example = Generator.YTNodeGenerator.generateRuntimeClass('Example', example_data); +const Example = Generator.generateRuntimeClass('Example', example_data); // You may now use this class as you would any other node. const example = new Example(example_data); diff --git a/src/parser/classes/GridShow.ts b/src/parser/classes/GridShow.ts index c028859640..dc44d4839d 100644 --- a/src/parser/classes/GridShow.ts +++ b/src/parser/classes/GridShow.ts @@ -1,6 +1,6 @@ import { YTNode, type ObservedArray } from '../helpers.js'; import type { RawNode } from '../index.js'; -import Parser from '../parser.js'; +import * as Parser from '../parser.js'; import Author from './misc/Author.js'; import Text from './misc/Text.js'; import NavigationEndpoint from './NavigationEndpoint.js'; diff --git a/src/parser/classes/GuideCollapsibleEntry.ts b/src/parser/classes/GuideCollapsibleEntry.ts index 855fe42658..6e77907b4a 100644 --- a/src/parser/classes/GuideCollapsibleEntry.ts +++ b/src/parser/classes/GuideCollapsibleEntry.ts @@ -1,4 +1,4 @@ -import Parser from '../parser.js'; +import * as Parser from '../parser.js'; import GuideEntry from './GuideEntry.js'; import type { RawNode } from '../index.js'; import { type ObservedArray, YTNode } from '../helpers.js'; diff --git a/src/parser/classes/GuideCollapsibleSectionEntry.ts b/src/parser/classes/GuideCollapsibleSectionEntry.ts index 65f03d0737..7f68af534b 100644 --- a/src/parser/classes/GuideCollapsibleSectionEntry.ts +++ b/src/parser/classes/GuideCollapsibleSectionEntry.ts @@ -1,4 +1,4 @@ -import Parser from '../parser.js'; +import * as Parser from '../parser.js'; import type { RawNode } from '../index.js'; import { type ObservedArray, YTNode } from '../helpers.js'; diff --git a/src/parser/classes/GuideSection.ts b/src/parser/classes/GuideSection.ts index d0591211b9..ff2d6eeeda 100644 --- a/src/parser/classes/GuideSection.ts +++ b/src/parser/classes/GuideSection.ts @@ -1,5 +1,5 @@ import Text from './misc/Text.js'; -import Parser from '../parser.js'; +import * as Parser from '../parser.js'; import { type ObservedArray, YTNode } from '../helpers.js'; import type { RawNode } from '../index.js'; diff --git a/src/parser/classes/PlayerStoryboardSpec.ts b/src/parser/classes/PlayerStoryboardSpec.ts index 74928b1433..29e4058ef3 100644 --- a/src/parser/classes/PlayerStoryboardSpec.ts +++ b/src/parser/classes/PlayerStoryboardSpec.ts @@ -1,19 +1,21 @@ import { YTNode } from '../helpers.js'; import type { RawNode } from '../index.js'; +export interface StoryboardData { + template_url: string; + thumbnail_width: number; + thumbnail_height: number; + thumbnail_count: number; + interval: number; + columns: number; + rows: number; + storyboard_count: number; +} + export default class PlayerStoryboardSpec extends YTNode { static type = 'PlayerStoryboardSpec'; - boards: { - template_url: string; - thumbnail_width: number; - thumbnail_height: number; - thumbnail_count: number; - interval: number; - columns: number; - rows: number; - storyboard_count: number; - }[]; + boards: StoryboardData[]; constructor(data: RawNode) { super(); diff --git a/src/parser/classes/SharedPost.ts b/src/parser/classes/SharedPost.ts index cb957fcbc3..d3c8027d3c 100644 --- a/src/parser/classes/SharedPost.ts +++ b/src/parser/classes/SharedPost.ts @@ -1,6 +1,6 @@ import { YTNode } from '../helpers.js'; import type { RawNode } from '../index.js'; -import Parser from '../parser.js'; +import * as Parser from '../parser.js'; import BackstagePost from './BackstagePost.js'; import Button from './Button.js'; import Menu from './menus/Menu.js'; diff --git a/src/parser/classes/actions/AppendContinuationItemsAction.ts b/src/parser/classes/actions/AppendContinuationItemsAction.ts index 9e5f515944..78aa787225 100644 --- a/src/parser/classes/actions/AppendContinuationItemsAction.ts +++ b/src/parser/classes/actions/AppendContinuationItemsAction.ts @@ -1,16 +1,17 @@ import Parser from '../../index.js'; import type { RawNode } from '../../index.js'; -import { type SuperParsedResult, YTNode } from '../../helpers.js'; +import type { ObservedArray } from '../../helpers.js'; +import { YTNode } from '../../helpers.js'; export default class AppendContinuationItemsAction extends YTNode { static type = 'AppendContinuationItemsAction'; - items: SuperParsedResult; + contents: ObservedArray | null; target: string; constructor(data: RawNode) { super(); - this.items = Parser.parse(data.continuationItems); + this.contents = Parser.parseArray(data.continuationItems); this.target = data.target; } } \ No newline at end of file diff --git a/src/parser/classes/comments/Comment.ts b/src/parser/classes/comments/Comment.ts index ab98fb0703..0fc0e2e1aa 100644 --- a/src/parser/classes/comments/Comment.ts +++ b/src/parser/classes/comments/Comment.ts @@ -11,7 +11,7 @@ import CommentReplyDialog from './CommentReplyDialog.js'; import PdgCommentChip from './PdgCommentChip.js'; import SponsorCommentBadge from './SponsorCommentBadge.js'; -import Proto from '../../../proto/index.js'; +import * as Proto from '../../../proto/index.js'; import { InnertubeError } from '../../../utils/Utils.js'; import { YTNode } from '../../helpers.js'; diff --git a/src/parser/continuations.ts b/src/parser/continuations.ts new file mode 100644 index 0000000000..85f4c99a16 --- /dev/null +++ b/src/parser/continuations.ts @@ -0,0 +1,211 @@ +import type { ObservedArray} from './helpers.js'; +import { YTNode, observe } from './helpers.js'; +import type { RawNode } from './index.js'; +import { Thumbnail } from './misc.js'; +import { NavigationEndpoint, LiveChatItemList, LiveChatHeader, LiveChatParticipantsList, Message } from './nodes.js'; +import * as Parser from './parser.js'; + +export class ItemSectionContinuation extends YTNode { + static readonly type = 'itemSectionContinuation'; + + contents: ObservedArray | null; + continuation?: string; + + constructor(data: RawNode) { + super(); + this.contents = Parser.parseArray(data.contents); + if (Array.isArray(data.continuations)) { + this.continuation = data.continuations?.at(0)?.nextContinuationData?.continuation; + } + } +} + +export class NavigateAction extends YTNode { + static readonly type = 'navigateAction'; + + endpoint: NavigationEndpoint; + + constructor(data: RawNode) { + super(); + this.endpoint = new NavigationEndpoint(data.endpoint); + } +} + +export class ShowMiniplayerCommand extends YTNode { + static readonly type = 'showMiniplayerCommand'; + + miniplayer_command: NavigationEndpoint; + show_premium_branding: boolean; + + constructor(data: RawNode) { + super(); + this.miniplayer_command = new NavigationEndpoint(data.miniplayerCommand); + this.show_premium_branding = data.showPremiumBranding; + } +} + +export { default as AppendContinuationItemsAction } from './classes/actions/AppendContinuationItemsAction.js'; + +export class ReloadContinuationItemsCommand extends YTNode { + static readonly type = 'reloadContinuationItemsCommand'; + + target_id: string; + contents: ObservedArray | null; + slot?: string; + + constructor(data: RawNode) { + super(); + this.target_id = data.targetId; + this.contents = Parser.parse(data.continuationItems, true); + this.slot = data?.slot; + } +} + +export class SectionListContinuation extends YTNode { + static readonly type = 'sectionListContinuation'; + + continuation: string; + contents: ObservedArray | null; + + constructor(data: RawNode) { + super(); + this.contents = Parser.parse(data.contents, true); + this.continuation = + data.continuations?.[0]?.nextContinuationData?.continuation || + data.continuations?.[0]?.reloadContinuationData?.continuation || null; + } +} + +export class MusicPlaylistShelfContinuation extends YTNode { + static readonly type = 'musicPlaylistShelfContinuation'; + + continuation: string; + contents: ObservedArray | null; + + constructor(data: RawNode) { + super(); + this.contents = Parser.parse(data.contents, true); + this.continuation = data.continuations?.[0].nextContinuationData.continuation || null; + } +} + +export class MusicShelfContinuation extends YTNode { + static readonly type = 'musicShelfContinuation'; + + continuation: string; + contents: ObservedArray | null; + + constructor(data: RawNode) { + super(); + this.contents = Parser.parseArray(data.contents); + this.continuation = + data.continuations?.[0].nextContinuationData?.continuation || + data.continuations?.[0].reloadContinuationData?.continuation || null; + } +} + +export class GridContinuation extends YTNode { + static readonly type = 'gridContinuation'; + + continuation: string; + items: ObservedArray | null; + + constructor(data: RawNode) { + super(); + this.items = Parser.parse(data.items, true); + this.continuation = data.continuations?.[0].nextContinuationData.continuation || null; + } + + get contents() { + return this.items; + } +} + +export class PlaylistPanelContinuation extends YTNode { + static readonly type = 'playlistPanelContinuation'; + + continuation: string; + contents: ObservedArray | null; + + constructor(data: RawNode) { + super(); + this.contents = Parser.parseArray(data.contents); + this.continuation = data.continuations?.[0]?.nextContinuationData?.continuation || + data.continuations?.[0]?.nextRadioContinuationData?.continuation || null; + } +} + +export class Continuation extends YTNode { + static readonly type = 'continuation'; + + continuation_type: string; + timeout_ms?: number; + time_until_last_message_ms?: number; + token: string; + + constructor(data: RawNode) { + super(); + this.continuation_type = data.type; + this.timeout_ms = data.continuation?.timeoutMs; + this.time_until_last_message_ms = data.continuation?.timeUntilLastMessageMsec; + this.token = data.continuation?.continuation; + } +} + +export class LiveChatContinuation extends YTNode { + static readonly type = 'liveChatContinuation'; + + actions: ObservedArray; + action_panel: YTNode | null; + item_list: LiveChatItemList | null; + header: LiveChatHeader | null; + participants_list: LiveChatParticipantsList | null; + popout_message: Message | null; + emojis: { + emoji_id: string; + shortcuts: string[]; + search_terms: string[]; + image: Thumbnail[]; + }[]; + continuation: Continuation; + viewer_name: string; + + constructor(data: RawNode) { + super(); + this.actions = Parser.parse(data.actions?.map((action: any) => { + delete action.clickTrackingParams; + return action; + }), true) || observe([]); + + this.action_panel = Parser.parseItem(data.actionPanel); + this.item_list = Parser.parseItem(data.itemList, LiveChatItemList); + this.header = Parser.parseItem(data.header, LiveChatHeader); + this.participants_list = Parser.parseItem(data.participantsList, LiveChatParticipantsList); + this.popout_message = Parser.parseItem(data.popoutMessage, Message); + + this.emojis = data.emojis?.map((emoji: any) => ({ + emoji_id: emoji.emojiId, + shortcuts: emoji.shortcuts, + search_terms: emoji.searchTerms, + image: Thumbnail.fromResponse(emoji.image), + is_custom_emoji: emoji.isCustomEmoji + })) || []; + + let continuation, type; + + if (data.continuations?.[0].timedContinuationData) { + type = 'timed'; + continuation = data.continuations?.[0].timedContinuationData; + } else if (data.continuations?.[0].invalidationContinuationData) { + type = 'invalidation'; + continuation = data.continuations?.[0].invalidationContinuationData; + } else if (data.continuations?.[0].liveChatReplayContinuationData) { + type = 'replay'; + continuation = data.continuations?.[0].liveChatReplayContinuationData; + } + + this.continuation = new Continuation({ continuation, type }); + + this.viewer_name = data.viewerName; + } +} diff --git a/src/parser/generator.ts b/src/parser/generator.ts index dab2d457cf..3bf8ad0043 100644 --- a/src/parser/generator.ts +++ b/src/parser/generator.ts @@ -1,12 +1,12 @@ /* eslint-disable no-cond-assign */ -import { InnertubeError, Platform } from '../utils/Utils.js'; +import { InnertubeError } from '../utils/Utils.js'; import Author from './classes/misc/Author.js'; import Text from './classes/misc/Text.js'; import Thumbnail from './classes/misc/Thumbnail.js'; import NavigationEndpoint from './classes/NavigationEndpoint.js'; import type { YTNodeConstructor } from './helpers.js'; import { YTNode } from './helpers.js'; -import Parser from './parser.js'; +import * as Parser from './parser.js'; export type MiscInferenceType = { type: 'misc', @@ -53,641 +53,668 @@ export type InferenceType = { export type KeyInfo = (readonly [string, InferenceType])[]; -export class YTNodeGenerator { - static #ignored_keys = new Set([ - 'trackingParams', 'accessibility', 'accessibilityData' - ]); - static #renderers_examples: Record = {}; - static #camelToSnake(str: string) { - return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`); +const IGNORED_KEYS = new Set([ + 'trackingParams', 'accessibility', 'accessibilityData' +]); + +const RENDERER_EXAMPLES: Record = {}; + +export function camelToSnake(str: string) { + return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`); +} + +/** + * Infer the type of a key given its value + * @param key - The key to infer the type of + * @param value - The value of the key + * @returns The inferred type + */ +export function inferType(key: string, value: unknown): InferenceType { + let return_value: string | Record | boolean | MiscInferenceType = false; + if (typeof value === 'object' && value != null) { + if (return_value = isRenderer(value)) { + RENDERER_EXAMPLES[return_value] = Reflect.get(value, Reflect.ownKeys(value)[0]); + return { + type: 'renderer', + renderers: [ return_value ], + optional: false + }; + } + if (return_value = isRendererList(value)) { + for (const [ key, value ] of Object.entries(return_value)) { + RENDERER_EXAMPLES[key] = value; + } + return { + type: 'renderer_list', + renderers: Object.keys(return_value), + optional: false + }; + } + if (return_value = isMiscType(key, value)) { + return return_value as MiscInferenceType; + } } - static #logNewClass(classname: string, key_info: KeyInfo) { - console.warn( - new InnertubeError(`${classname} not found!\nThis is a bug, want to help us fix it? Follow the instructions at ${Platform.shim.info.repo_url}/blob/main/docs/updating-the-parser.md or report it at ${Platform.shim.info.bugs_url}!\nIntrospected and JIT generated this class in the meantime:\n${this.generateTypescriptClass(classname, key_info)}`) - ); + const primative_type = typeof value; + if (primative_type === 'object') + return { + type: 'object', + keys: Object.entries(value as object).map(([ key, value ]) => [ key, inferType(key, value) ]), + optional: false + }; + return { + type: 'primative', + typeof: [ primative_type ], + optional: false + }; +} + +/** + * Checks if the given value is an array of renderers + * @param value - The value to check + * @returns If it is a renderer list, return an object with keys being the classnames, and values being an example of that class. + * Otherwise, return false. + */ +export function isRendererList(value: unknown) { + const arr = Array.isArray(value); + const is_list = arr && value.every((item) => isRenderer(item)); + return ( + is_list ? + Object.fromEntries(value.map((item) => { + const key = Reflect.ownKeys(item)[0].toString(); + return [ Parser.sanitizeClassName(key), item[key] ]; + })) : + false + ); +} + +/** + * Check if the given value is a misc type. + * @param key - The key of the value + * @param value - The value to check + * @returns If it is a misc type, return the InferenceType. Otherwise, return false. + */ +export function isMiscType(key: string, value: unknown): MiscInferenceType | false { + // NavigationEndpoint + if ((key.endsWith('Endpoint') || key.endsWith('Command') || key === 'endpoint') && typeof value === 'object' && value !== null) { + return { + type: 'misc', + endpoint: new NavigationEndpoint(value), + optional: false, + misc_type: 'NavigationEndpoint' + }; } - static #logChangedKeys(classname: string, key_info: KeyInfo, changed_keys: KeyInfo) { - console.warn(`${classname} changed!\nThe following keys where altered: ${changed_keys.map(([ key ]) => this.#camelToSnake(key)).join(', ')}\nThe class has changed to:\n${this.generateTypescriptClass(classname, key_info)}`); + // Text + if (typeof value === 'object' && value !== null && (Reflect.has(value, 'simpleText') || Reflect.has(value, 'runs'))) { + const textNode = new Text(value); + return { + type: 'misc', + misc_type: 'Text', + optional: false, + endpoint: textNode.endpoint, + text: textNode.toString() + }; } - /** - * Is this key ignored by the parser? - * @param key - The key to check - * @returns Whether or not the key is ignored - */ - static isIgnoredKey(key: string | symbol) { - return typeof key === 'string' && this.#ignored_keys.has(key); + // Thumbnail + if (typeof value === 'object' && value !== null && Reflect.has(value, 'thumbnails') && Array.isArray(Reflect.get(value, 'thumbnails'))) { + return { + type: 'misc', + misc_type: 'Thumbnail', + optional: false + }; } - /** - * Merges two sets of key info, resolving any conflicts - * @param key_info - The current key info - * @param new_key_info - The new key info - * @returns The merged key info - */ - static mergeKeyInfo(key_info: KeyInfo, new_key_info: KeyInfo) { - const changed_keys = new Map(); - const current_keys = new Set(key_info.map(([ key ]) => key)); - const new_keys = new Set(new_key_info.map(([ key ]) => key)); - - const added_keys = new_key_info.filter(([ key ]) => !current_keys.has(key)); - const removed_keys = key_info.filter(([ key ]) => !new_keys.has(key)); - - const common_keys = key_info.filter(([ key ]) => new_keys.has(key)); - - const new_key_map = new Map(new_key_info); - - for (const [ key, type ] of common_keys) { - const new_type = new_key_map.get(key); - if (!new_type) continue; - if (type.type !== new_type.type) { - // We've got a type mismatch, this is unknown, we do not resolve unions - changed_keys.set(key, { - type: 'unknown', - optional: true - }); - continue; - } - // We've got the same type, so we can now resolve the changes - switch (type.type) { - case 'object': - { - if (new_type.type !== 'object') continue; - const { resolved_key_info } = this.mergeKeyInfo(type.keys, new_type.keys); - const resolved_key: InferenceType = { - type: 'object', - keys: resolved_key_info, - optional: type.optional || new_type.optional - }; - const did_change = JSON.stringify(resolved_key) !== JSON.stringify(type); - if (did_change) changed_keys.set(key, resolved_key); - } - break; - case 'renderer': - { - if (new_type.type !== 'renderer') continue; - const union_map = { - ...type.renderers, - ...new_type.renderers - }; - const either_optional = type.optional || new_type.optional; - const resolved_key: InferenceType = { - type: 'renderer', - renderers: union_map, - optional: either_optional - }; - const did_change = JSON.stringify({ - ...resolved_key, - renderers: Object.keys(resolved_key.renderers) - }) !== JSON.stringify({ - ...type, - renderers: Object.keys(type.renderers) - }); - if (did_change) changed_keys.set(key, resolved_key); - } - break; - case 'renderer_list': - { - if (new_type.type !== 'renderer_list') continue; - const union_map = { - ...type.renderers, - ...new_type.renderers - }; - const either_optional = type.optional || new_type.optional; - const resolved_key: InferenceType = { - type: 'renderer_list', - renderers: union_map, - optional: either_optional - }; - const did_change = JSON.stringify({ - ...resolved_key, - renderers: Object.keys(resolved_key.renderers) - }) !== JSON.stringify({ - ...type, - renderers: Object.keys(type.renderers) - }); - if (did_change) changed_keys.set(key, resolved_key); - } - break; - case 'misc': - { - if (new_type.type !== 'misc') continue; - if (type.misc_type !== new_type.misc_type) { - // We've got a type mismatch, this is unknown, we do not resolve unions - changed_keys.set(key, { - type: 'unknown', - optional: true - }); - } - switch (type.misc_type) { - case 'Author': - { - if (new_type.misc_type !== 'Author') break; - const had_optional_param = type.params[1] || new_type.params[1]; - const either_optional = type.optional || new_type.optional; - const resolved_key: MiscInferenceType = { - type: 'misc', - misc_type: 'Author', - optional: either_optional, - params: [ new_type.params[0], had_optional_param ] - }; - const did_change = JSON.stringify(resolved_key) !== JSON.stringify(type); - if (did_change) changed_keys.set(key, resolved_key); - } - break; - // Other cases can not change - } - } - break; - case 'primative': - { - if (new_type.type !== 'primative') continue; - const resolved_key: InferenceType = { - type: 'primative', - typeof: Array.from(new Set([ ...new_type.typeof, ...type.typeof ])), - optional: type.optional || new_type.optional - }; - const did_change = JSON.stringify(resolved_key) !== JSON.stringify(type); - if (did_change) changed_keys.set(key, resolved_key); - } - break; - } - } + return false; +} - for (const [ key, type ] of added_keys) { - changed_keys.set(key, { - ...type, - optional: true - }); - } +/** + * Check if the given value is a renderer + * @param value - The value to check + * @returns If it is a renderer, return the class name. Otherwise, return false. + */ +export function isRenderer(value: unknown) { + const is_object = typeof value === 'object'; + if (!is_object) return false; + const keys = Reflect.ownKeys(value as object); + if (keys.length === 1 && keys[0].toString().includes('Renderer')) { + return Parser.sanitizeClassName(keys[0].toString()); + } + return false; +} - for (const [ key, type ] of removed_keys) { - changed_keys.set(key, { - ...type, - optional: true - }); - } +function introspectKeysFirstPass(classdata: unknown): KeyInfo { + if (typeof classdata !== 'object' || classdata === null) { + throw new InnertubeError('Generator: Cannot introspect non-object', { + classdata + }); + } - const unchanged_keys = key_info.filter(([ key ]) => !changed_keys.has(key)); + const keys = Reflect.ownKeys(classdata) + .filter((key) => !isIgnoredKey(key)) + .filter((key): key is string => typeof key === 'string'); - const resolved_key_info_map = new Map([ ...unchanged_keys, ...changed_keys ]); - const resolved_key_info = [ ...resolved_key_info_map.entries() ]; + return keys.map((key) => { + const value = Reflect.get(classdata, key) as unknown; + const inferred_type = inferType(key, value); + return [ key, inferred_type ] as const; + }); +} - return { - resolved_key_info, - changed_keys: [ ...changed_keys.entries() ] +function introspectKeysSecondPass(key_info: KeyInfo) { + // The second pass will detect Author + const channel_nav = key_info.filter(([ , value ]) => { + if (value.type !== 'misc') return false; + if (!(value.misc_type === 'NavigationEndpoint' || value.misc_type === 'Text')) return false; + return value.endpoint?.metadata.page_type === 'WEB_PAGE_TYPE_CHANNEL'; + }); + + // Whichever one has the longest text is the most probable match + const most_probable_match = channel_nav.sort(([ , a ], [ , b ]) => { + if (a.type !== 'misc' || b.type !== 'misc') return 0; + if (a.misc_type !== 'Text' || b.misc_type !== 'Text') return 0; + return b.text.length - a.text.length; + }); + + const excluded_keys = new Set(); + + const cannonical_channel_nav = most_probable_match[0]; + + let author: MiscInferenceType | undefined; + // We've found an author + if (cannonical_channel_nav) { + excluded_keys.add(cannonical_channel_nav[0]); + // Now to locate its metadata + // We'll first get all the keys in the classdata + const keys = key_info.map(([ key ]) => key); + // Check for anything ending in 'Badges' equals 'badges' + const badges = keys.filter((key) => key.endsWith('Badges') || key === 'badges'); + // The likely candidate is the one with some prefix (owner, author) + const likely_badges = badges.filter((key) => key.startsWith('owner') || key.startsWith('author')); + // If we have a likely candidate, we'll use that + const cannonical_badges = likely_badges[0] ?? badges[0]; + // Now we have the author and its badges + // Verify that its actually badges + const badge_key_info = key_info.find(([ key ]) => key === cannonical_badges); + const is_badges = badge_key_info ? + badge_key_info[1].type === 'renderer_list' && Reflect.has(badge_key_info[1].renderers, 'MetadataBadge') : + false; + + if (is_badges && cannonical_badges) excluded_keys.add(cannonical_badges); + // TODO: next we check for the author's thumbnail + author = { + type: 'misc', + misc_type: 'Author', + optional: false, + params: [ + cannonical_channel_nav[0], + is_badges ? cannonical_badges : undefined + ] }; } - /** - * Given a classname and its resolved key info, create a new class - * @param classname - The name of the class - * @param key_info - The resolved key info - * @returns Class based on the key info extending YTNode - */ - static createRuntimeClass(classname: string, key_info: KeyInfo): YTNodeConstructor { - this.#logNewClass(classname, key_info); - const node = class extends YTNode { - static type = classname; - static #key_info = new Map(); - static set key_info(key_info: KeyInfo) { - this.#key_info = new Map(key_info); - } - static get key_info() { - return [ ...this.#key_info.entries() ]; - } - constructor(data: any) { - super(); - const { - key_info, - unimplemented_dependencies - } = YTNodeGenerator.introspect(data); - - const { - resolved_key_info, - changed_keys - } = YTNodeGenerator.mergeKeyInfo(node.key_info, key_info); - const did_change = changed_keys.length > 0; + if (author) { + key_info.push([ 'author', author ]); + } - if (did_change) { - node.key_info = resolved_key_info; - YTNodeGenerator.#logChangedKeys(classname, node.key_info, changed_keys); - } + return key_info.filter(([ key ]) => !excluded_keys.has(key)); +} - for (const [ name, data ] of unimplemented_dependencies) - YTNodeGenerator.generateRuntimeClass(name, data); +function introspect2(classdata: unknown) { + const key_info = introspectKeysFirstPass(classdata); + return introspectKeysSecondPass(key_info); +} - for (const [ key, value ] of key_info) { - let snake_key = YTNodeGenerator.#camelToSnake(key); - if (value.type === 'misc' && value.misc_type === 'NavigationEndpoint') - snake_key = 'endpoint'; - Reflect.set(this, snake_key, YTNodeGenerator.parse(key, value, data)); - } +/** + * Introspect an example of a class in order to determine its key info and dependencies + * @param classdata - The example of the class + * @returns The key info and any unimplemented dependencies + */ +export function introspect(classdata: unknown) { + const key_info = introspect2(classdata); + const dependencies = new Map(); + for (const [ , value ] of key_info) { + if (value.type === 'renderer' || value.type === 'renderer_list') + for (const renderer of value.renderers) { + const example = RENDERER_EXAMPLES[renderer]; + if (example) + dependencies.set(renderer, example); } - }; - node.key_info = key_info; - Object.defineProperty(node, 'name', { value: classname, writable: false }); - return node; } - /** - * Introspect an example of a class in order to determine its key info and dependencies - * @param classdata - The example of the class - * @returns The key info and any unimplemented dependencies - */ - static introspect(classdata: string) { - const key_info = this.#introspect(classdata); - const dependencies = new Map(); - for (const [ , value ] of key_info) { - if (value.type === 'renderer' || value.type === 'renderer_list') - for (const renderer of value.renderers) { - const example = this.#renderers_examples[renderer]; - if (example) - dependencies.set(renderer, example); - } + const unimplemented_dependencies = Array.from(dependencies).filter(([ classname ]) => !Parser.hasParser(classname)); + + return { + key_info, + unimplemented_dependencies + }; +} + +/** + * Is this key ignored by the parser? + * @param key - The key to check + * @returns Whether or not the key is ignored + */ +export function isIgnoredKey(key: string | symbol) { + return typeof key === 'string' && IGNORED_KEYS.has(key); +} + +/** + * Given a classname and its resolved key info, create a new class + * @param classname - The name of the class + * @param key_info - The resolved key info + * @returns Class based on the key info extending YTNode + */ +export function createRuntimeClass(classname: string, key_info: KeyInfo, logger: Parser.ParserErrorHandler): YTNodeConstructor { + logger({ + error_type: 'class_not_found', + classname, + key_info + }); + + const node = class extends YTNode { + static type = classname; + static #key_info = new Map(); + static set key_info(key_info: KeyInfo) { + this.#key_info = new Map(key_info); + } + static get key_info() { + return [ ...this.#key_info.entries() ]; } - const unimplemented_dependencies = Array.from(dependencies).filter(([ classname ]) => !Parser.hasParser(classname)); + constructor(data: unknown) { + super(); + const { + key_info, + unimplemented_dependencies + } = introspect(data); - return { - key_info, - unimplemented_dependencies - }; - } - /** - * Given example data for a class, introspect, implement dependencies, and create a new class - * @param classname - The name of the class - * @param classdata - The example of the class - * @returns Class based on the example classdata extending YTNode - */ - static generateRuntimeClass(classname: string, classdata: any) { - const { - key_info, - unimplemented_dependencies - } = this.introspect(classdata); + const { + resolved_key_info, + changed_keys + } = mergeKeyInfo(node.key_info, key_info); + + const did_change = changed_keys.length > 0; + + if (did_change) { + node.key_info = resolved_key_info; + logger({ + error_type: 'class_changed', + classname, + key_info: node.key_info, + changed_keys + }); + } + + for (const [ name, data ] of unimplemented_dependencies) + generateRuntimeClass(name, data, logger); + + for (const [ key, value ] of key_info) { + let snake_key = camelToSnake(key); + if (value.type === 'misc' && value.misc_type === 'NavigationEndpoint') + snake_key = 'endpoint'; + Reflect.set(this, snake_key, parse(key, value, data)); + } + } + }; + node.key_info = key_info; + Object.defineProperty(node, 'name', { value: classname, writable: false }); + return node; +} + +/** + * Given example data for a class, introspect, implement dependencies, and create a new class + * @param classname - The name of the class + * @param classdata - The example of the class + * @returns Class based on the example classdata extending YTNode + */ +export function generateRuntimeClass(classname: string, classdata: unknown, logger: Parser.ParserErrorHandler) { + const { + key_info, + unimplemented_dependencies + } = introspect(classdata); - const JITNode = this.createRuntimeClass(classname, key_info); - Parser.addRuntimeParser(classname, JITNode); + const JITNode = createRuntimeClass(classname, key_info, logger); + Parser.addRuntimeParser(classname, JITNode); - for (const [ name, data ] of unimplemented_dependencies) - this.generateRuntimeClass(name, data); + for (const [ name, data ] of unimplemented_dependencies) + generateRuntimeClass(name, data, logger); - return JITNode; + return JITNode; +} + +/** + * Generate a typescript class based on the key info + * @param classname - The name of the class + * @param key_info - The key info, as returned by {@link introspect} + * @returns Typescript class file + */ +export function generateTypescriptClass(classname: string, key_info: KeyInfo) { + const props: string[] = []; + const constructor_lines = [ + 'super();' + ]; + for (const [ key, value ] of key_info) { + let snake_key = camelToSnake(key); + if (value.type === 'misc' && value.misc_type === 'NavigationEndpoint') + snake_key = 'endpoint'; + props.push(`${snake_key}${value.optional ? '?' : ''}: ${toTypeDeclaration(value)};`); + constructor_lines.push(`this.${snake_key} = ${toParser(key, value)};`); } - /** - * Generate a typescript class based on the key info - * @param classname - The name of the class - * @param key_info - The key info, as returned by {@link YTNodeGenerator.introspect} - * @returns Typescript class file - */ - static generateTypescriptClass(classname: string, key_info: KeyInfo) { - const props: string[] = []; - const constructor_lines = [ - 'super();' - ]; - for (const [ key, value ] of key_info) { - let snake_key = this.#camelToSnake(key); - if (value.type === 'misc' && value.misc_type === 'NavigationEndpoint') - snake_key = 'endpoint'; - props.push(`${snake_key}${value.optional ? '?' : ''}: ${this.toTypeDeclaration(value)};`); - constructor_lines.push(`this.${snake_key} = ${this.toParser(key, value)};`); + return `class ${classname} extends YTNode {\n static type = '${classname}';\n\n ${props.join('\n ')}\n\n constructor(data: RawNode) {\n ${constructor_lines.join('\n ')}\n }\n}\n`; +} + +/** + * For a given inference type, get the typescript type declaration + * @param inference_type - The inference type to get the declaration for + * @param indentation - The indentation level (used for objects) + * @returns Typescript type declaration + */ +export function toTypeDeclaration(inference_type: InferenceType, indentation = 0): string { + switch (inference_type.type) { + case 'renderer': + { + return `${inference_type.renderers.map((type) => `YTNodes.${type}`).join(' | ')} | null`; + } + case 'renderer_list': + { + return `ObservedArray<${inference_type.renderers.map((type) => `YTNodes.${type}`).join(' | ')}> | null`; } - return `class ${classname} extends YTNode {\n static type = '${classname}';\n\n ${props.join('\n ')}\n\n constructor(data: RawNode) {\n ${constructor_lines.join('\n ')}\n }\n}\n`; + case 'object': + { + return `{\n${inference_type.keys.map(([ key, value ]) => `${' '.repeat((indentation + 2) * 2)}${camelToSnake(key)}${value.optional ? '?' : ''}: ${toTypeDeclaration(value, indentation + 1)}`).join(',\n')}\n${' '.repeat((indentation + 1) * 2)}}`; + } + case 'misc': + switch (inference_type.misc_type) { + case 'Thumbnail': + return 'Thumbnail[]'; + default: + return inference_type.misc_type; + } + case 'primative': + return inference_type.typeof.join(' | '); + case 'unknown': + return '/* TODO: determine correct type */ unknown'; } - /** - * For a given inference type, get the typescript type declaration - * @param inference_type - The inference type to get the declaration for - * @param indentation - The indentation level (used for objects) - * @returns Typescript type declaration - */ - static toTypeDeclaration(inference_type: InferenceType, indentation = 0): string { - switch (inference_type.type) { - case 'renderer': +} + +/** + * Generate statements to parse a given inference type + * @param key - The key to parse + * @param inference_type - The inference type to parse + * @param key_path - The path to the key (excluding the key itself) + * @param indentation - The indentation level (used for objects) + * @returns Statement to parse the given key + */ +export function toParser(key: string, inference_type: InferenceType, key_path: string[] = [ 'data' ], indentation = 1) { + let parser = 'undefined'; + switch (inference_type.type) { + case 'renderer': { - return `${inference_type.renderers.map((type) => `YTNodes.${type}`).join(' | ')} | null`; + parser = `Parser.parseItem(${key_path.join('.')}.${key}, [ ${inference_type.renderers.map((type) => `YTNodes.${type}`).join(', ')} ])`; } - case 'renderer_list': + break; + case 'renderer_list': { - return `ObservedArray<${inference_type.renderers.map((type) => `YTNodes.${type}`).join(' | ')}> | null`; + parser = `Parser.parse(${key_path.join('.')}.${key}, true, [ ${inference_type.renderers.map((type) => `YTNodes.${type}`).join(', ')} ])`; } - case 'object': + break; + case 'object': { - return `{\n${inference_type.keys.map(([ key, value ]) => `${' '.repeat((indentation + 2) * 2)}${this.#camelToSnake(key)}${value.optional ? '?' : ''}: ${this.toTypeDeclaration(value, indentation + 1)}`).join(',\n')}\n${' '.repeat((indentation + 1) * 2)}}`; + const new_keypath = [ ...key_path, key ]; + parser = `{\n${inference_type.keys.map(([ key, value ]) => `${' '.repeat((indentation + 2) * 2)}${camelToSnake(key)}: ${toParser(key, value, new_keypath, indentation + 1)}`).join(',\n')}\n${' '.repeat((indentation + 1) * 2)}}`; } - case 'misc': - switch (inference_type.misc_type) { - case 'Thumbnail': - return 'Thumbnail[]'; - default: - return inference_type.misc_type; + break; + case 'misc': + switch (inference_type.misc_type) { + case 'Thumbnail': + parser = `Thumbnail.fromResponse(${key_path.join('.')}.${key})`; + break; + case 'Author': + { + const author_parser = `new Author(${key_path.join('.')}.${inference_type.params[0]}, ${inference_type.params[1] ? `${key_path.join('.')}.${inference_type.params[1]}` : 'undefined'})`; + if (inference_type.optional) + return `Reflect.has(${key_path.join('.')}, '${inference_type.params[0]}') ? ${author_parser} : undefined`; + return author_parser; } - case 'primative': - return inference_type.typeof.join(' | '); - case 'unknown': - return '/* TODO: determine correct type */ unknown'; + default: + parser = `new ${inference_type.misc_type}(${key_path.join('.')}.${key})`; + break; + } + if (parser === 'undefined') + throw new Error('Unreachable code reached! Switch missing case!'); + break; + case 'primative': + case 'unknown': + parser = `${key_path.join('.')}.${key}`; + break; + } + if (inference_type.optional) + return `Reflect.has(${key_path.join('.')}, '${key}') ? ${parser} : undefined`; + return parser; +} + +function accessDataFromKeyPath(root: any, key_path: string[]) { + let data = root; + for (const key of key_path) + data = data[key]; + return data; +} + +function hasDataFromKeyPath(root: any, key_path: string[]) { + let data = root; + for (const key of key_path) + if (!Reflect.has(data, key)) + return false; + else + data = data[key]; + return true; +} + +/** + * Parse a value from a given key path using the given inference type + * @param key - The key to parse + * @param inference_type - The inference type to parse + * @param data - The data to parse from + * @param key_path - The path to the key (excluding the key itself) + * @returns The parsed value + */ +export function parse(key: string, inference_type: InferenceType, data: unknown, key_path: string[] = [ 'data' ]) { + const should_optional = !inference_type.optional || hasDataFromKeyPath({ data }, [ ...key_path, key ]); + switch (inference_type.type) { + case 'renderer': + { + return should_optional ? Parser.parseItem(accessDataFromKeyPath({ data }, [ ...key_path, key ]), inference_type.renderers.map((type) => Parser.getParserByName(type))) : undefined; + } + case 'renderer_list': + { + return should_optional ? Parser.parse(accessDataFromKeyPath({ data }, [ ...key_path, key ]), true, inference_type.renderers.map((type) => Parser.getParserByName(type))) : undefined; } + case 'object': + { + const obj: any = {}; + const new_key_path = [ ...key_path, key ]; + for (const [ key, value ] of inference_type.keys) { + obj[key] = should_optional ? parse(key, value, data, new_key_path) : undefined; + } + return obj; + } + case 'misc': + switch (inference_type.misc_type) { + case 'NavigationEndpoint': + return should_optional ? new NavigationEndpoint(accessDataFromKeyPath({ data }, [ ...key_path, key ])) : undefined; + case 'Text': + return should_optional ? new Text(accessDataFromKeyPath({ data }, [ ...key_path, key ])) : undefined; + case 'Thumbnail': + return should_optional ? Thumbnail.fromResponse(accessDataFromKeyPath({ data }, [ ...key_path, key ])) : undefined; + case 'Author': + { + const author_should_optional = !inference_type.optional || hasDataFromKeyPath({ data }, [ ...key_path, inference_type.params[0] ]); + return author_should_optional ? new Author( + accessDataFromKeyPath({ data }, [ ...key_path, inference_type.params[0] ]), + inference_type.params[1] ? + accessDataFromKeyPath({ data }, [ ...key_path, inference_type.params[1] ]) : undefined + ) : undefined; + } + } + throw new Error('Unreachable code reached! Switch missing case!'); + case 'primative': + case 'unknown': + return accessDataFromKeyPath({ data }, [ ...key_path, key ]); } - /** - * Generate statements to parse a given inference type - * @param key - The key to parse - * @param inference_type - The inference type to parse - * @param key_path - The path to the key (excluding the key itself) - * @param indentation - The indentation level (used for objects) - * @returns Statement to parse the given key +} + +/** + * Merges two sets of key info, resolving any conflicts + * @param key_info - The current key info + * @param new_key_info - The new key info + * @returns The merged key info */ - static toParser(key: string, inference_type: InferenceType, key_path: string[] = [ 'data' ], indentation = 1) { - let parser = 'undefined'; - switch (inference_type.type) { - case 'renderer': +export function mergeKeyInfo(key_info: KeyInfo, new_key_info: KeyInfo) { + const changed_keys = new Map(); + const current_keys = new Set(key_info.map(([ key ]) => key)); + const new_keys = new Set(new_key_info.map(([ key ]) => key)); + + const added_keys = new_key_info.filter(([ key ]) => !current_keys.has(key)); + const removed_keys = key_info.filter(([ key ]) => !new_keys.has(key)); + + const common_keys = key_info.filter(([ key ]) => new_keys.has(key)); + + const new_key_map = new Map(new_key_info); + + for (const [ key, type ] of common_keys) { + const new_type = new_key_map.get(key); + if (!new_type) continue; + if (type.type !== new_type.type) { + // We've got a type mismatch, this is unknown, we do not resolve unions + changed_keys.set(key, { + type: 'unknown', + optional: true + }); + continue; + } + // We've got the same type, so we can now resolve the changes + switch (type.type) { + case 'object': { - parser = `Parser.parseItem(${key_path.join('.')}.${key}, [ ${inference_type.renderers.map((type) => `YTNodes.${type}`).join(', ')} ])`; + if (new_type.type !== 'object') continue; + const { resolved_key_info } = mergeKeyInfo(type.keys, new_type.keys); + const resolved_key: InferenceType = { + type: 'object', + keys: resolved_key_info, + optional: type.optional || new_type.optional + }; + const did_change = JSON.stringify(resolved_key) !== JSON.stringify(type); + if (did_change) changed_keys.set(key, resolved_key); } break; - case 'renderer_list': + case 'renderer': { - parser = `Parser.parse(${key_path.join('.')}.${key}, true, [ ${inference_type.renderers.map((type) => `YTNodes.${type}`).join(', ')} ])`; + if (new_type.type !== 'renderer') continue; + const union_map = { + ...type.renderers, + ...new_type.renderers + }; + const either_optional = type.optional || new_type.optional; + const resolved_key: InferenceType = { + type: 'renderer', + renderers: union_map, + optional: either_optional + }; + const did_change = JSON.stringify({ + ...resolved_key, + renderers: Object.keys(resolved_key.renderers) + }) !== JSON.stringify({ + ...type, + renderers: Object.keys(type.renderers) + }); + if (did_change) changed_keys.set(key, resolved_key); } break; - case 'object': + case 'renderer_list': { - const new_keypath = [ ...key_path, key ]; - parser = `{\n${inference_type.keys.map(([ key, value ]) => `${' '.repeat((indentation + 2) * 2)}${this.#camelToSnake(key)}: ${this.toParser(key, value, new_keypath, indentation + 1)}`).join(',\n')}\n${' '.repeat((indentation + 1) * 2)}}`; + if (new_type.type !== 'renderer_list') continue; + const union_map = { + ...type.renderers, + ...new_type.renderers + }; + const either_optional = type.optional || new_type.optional; + const resolved_key: InferenceType = { + type: 'renderer_list', + renderers: union_map, + optional: either_optional + }; + const did_change = JSON.stringify({ + ...resolved_key, + renderers: Object.keys(resolved_key.renderers) + }) !== JSON.stringify({ + ...type, + renderers: Object.keys(type.renderers) + }); + if (did_change) changed_keys.set(key, resolved_key); } break; case 'misc': - switch (inference_type.misc_type) { - case 'Thumbnail': - parser = `Thumbnail.fromResponse(${key_path.join('.')}.${key})`; - break; - case 'Author': - { - const author_parser = `new Author(${key_path.join('.')}.${inference_type.params[0]}, ${inference_type.params[1] ? `${key_path.join('.')}.${inference_type.params[1]}` : 'undefined'})`; - if (inference_type.optional) - return `Reflect.has(${key_path.join('.')}, '${inference_type.params[0]}') ? ${author_parser} : undefined`; - return author_parser; + { + if (new_type.type !== 'misc') continue; + if (type.misc_type !== new_type.misc_type) { + // We've got a type mismatch, this is unknown, we do not resolve unions + changed_keys.set(key, { + type: 'unknown', + optional: true + }); + } + switch (type.misc_type) { + case 'Author': + { + if (new_type.misc_type !== 'Author') break; + const had_optional_param = type.params[1] || new_type.params[1]; + const either_optional = type.optional || new_type.optional; + const resolved_key: MiscInferenceType = { + type: 'misc', + misc_type: 'Author', + optional: either_optional, + params: [ new_type.params[0], had_optional_param ] + }; + const did_change = JSON.stringify(resolved_key) !== JSON.stringify(type); + if (did_change) changed_keys.set(key, resolved_key); + } + break; + // Other cases can not change } - default: - parser = `new ${inference_type.misc_type}(${key_path.join('.')}.${key})`; - break; } - if (parser === 'undefined') - throw new Error('Unreachable code reached! Switch missing case!'); break; case 'primative': - case 'unknown': - parser = `${key_path.join('.')}.${key}`; - break; - } - if (inference_type.optional) - return `Reflect.has(${key_path.join('.')}, '${key}') ? ${parser} : undefined`; - return parser; - } - static #accessDataFromKeyPath(root: any, key_path: string[]) { - let data = root; - for (const key of key_path) - data = data[key]; - return data; - } - static #hasDataFromKeyPath(root: any, key_path: string[]) { - let data = root; - for (const key of key_path) - if (!Reflect.has(data, key)) - return false; - else - data = data[key]; - return true; - } - /** - * Parse a value from a given key path using the given inference type - * @param key - The key to parse - * @param inference_type - The inference type to parse - * @param data - The data to parse from - * @param key_path - The path to the key (excluding the key itself) - * @returns The parsed value - */ - static parse(key: string, inference_type: InferenceType, data: any, key_path: string[] = [ 'data' ]) { - const should_optional = !inference_type.optional || this.#hasDataFromKeyPath({ data }, [ ...key_path, key ]); - switch (inference_type.type) { - case 'renderer': - { - return should_optional ? Parser.parseItem(this.#accessDataFromKeyPath({ data }, [ ...key_path, key ]), inference_type.renderers.map((type) => Parser.getParserByName(type))) : undefined; - } - case 'renderer_list': - { - return should_optional ? Parser.parse(this.#accessDataFromKeyPath({ data }, [ ...key_path, key ]), true, inference_type.renderers.map((type) => Parser.getParserByName(type))) : undefined; - } - case 'object': - { - const obj: any = {}; - const new_key_path = [ ...key_path, key ]; - for (const [ key, value ] of inference_type.keys) { - obj[key] = should_optional ? this.parse(key, value, data, new_key_path) : undefined; - } - return obj; - } - case 'misc': - switch (inference_type.misc_type) { - case 'NavigationEndpoint': - return should_optional ? new NavigationEndpoint(this.#accessDataFromKeyPath({ data }, [ ...key_path, key ])) : undefined; - case 'Text': - return should_optional ? new Text(this.#accessDataFromKeyPath({ data }, [ ...key_path, key ])) : undefined; - case 'Thumbnail': - return should_optional ? Thumbnail.fromResponse(this.#accessDataFromKeyPath({ data }, [ ...key_path, key ])) : undefined; - case 'Author': - { - const author_should_optional = !inference_type.optional || this.#hasDataFromKeyPath({ data }, [ ...key_path, inference_type.params[0] ]); - return author_should_optional ? new Author( - this.#accessDataFromKeyPath({ data }, [ ...key_path, inference_type.params[0] ]), - inference_type.params[1] ? - this.#accessDataFromKeyPath({ data }, [ ...key_path, inference_type.params[1] ]) : undefined - ) : undefined; - } + { + if (new_type.type !== 'primative') continue; + const resolved_key: InferenceType = { + type: 'primative', + typeof: Array.from(new Set([ ...new_type.typeof, ...type.typeof ])), + optional: type.optional || new_type.optional + }; + const did_change = JSON.stringify(resolved_key) !== JSON.stringify(type); + if (did_change) changed_keys.set(key, resolved_key); } - throw new Error('Unreachable code reached! Switch missing case!'); - case 'primative': - case 'unknown': - return this.#accessDataFromKeyPath({ data }, [ ...key_path, key ]); + break; } } - static #passOne(classdata: any) { - const keys = Reflect.ownKeys(classdata).filter((key) => !this.isIgnoredKey(key)).filter((key) => typeof key === 'string') as string[]; - const key_info = keys.map((key) => { - const value = classdata[key]; - const inferred_type = this.inferType(key as string, value); - return [ - key, - inferred_type - ] as const; + + for (const [ key, type ] of added_keys) { + changed_keys.set(key, { + ...type, + optional: true }); - return key_info; } - static #passTwo(key_info: KeyInfo) { - // The second pass will detect Author - const channel_nav = key_info.filter(([ , value ]) => { - if (value.type !== 'misc') return false; - if (!(value.misc_type === 'NavigationEndpoint' || value.misc_type === 'Text')) return false; - return value.endpoint?.metadata.page_type === 'WEB_PAGE_TYPE_CHANNEL'; - }); - // Whichever one has the longest text is the most probable match - const most_probable_match = channel_nav.sort(([ , a ], [ , b ]) => { - if (a.type !== 'misc' || b.type !== 'misc') return 0; - if (a.misc_type !== 'Text' || b.misc_type !== 'Text') return 0; - return b.text.length - a.text.length; + for (const [ key, type ] of removed_keys) { + changed_keys.set(key, { + ...type, + optional: true }); + } - const excluded_keys = new Set(); - - const cannonical_channel_nav = most_probable_match[0]; - - let author: MiscInferenceType | undefined; - // We've found an author - if (cannonical_channel_nav) { - excluded_keys.add(cannonical_channel_nav[0]); - // Now to locate its metadata - // We'll first get all the keys in the classdata - const keys = key_info.map(([ key ]) => key); - // Check for anything ending in 'Badges' equals 'badges' - const badges = keys.filter((key) => key.endsWith('Badges') || key === 'badges'); - // The likely candidate is the one with some prefix (owner, author) - const likely_badges = badges.filter((key) => key.startsWith('owner') || key.startsWith('author')); - // If we have a likely candidate, we'll use that - const cannonical_badges = likely_badges[0] ?? badges[0]; - // Now we have the author and its badges - // Verify that its actually badges - const badge_key_info = key_info.find(([ key ]) => key === cannonical_badges); - const is_badges = badge_key_info ? - badge_key_info[1].type === 'renderer_list' && Reflect.has(badge_key_info[1].renderers, 'MetadataBadge') : - false; - - if (is_badges && cannonical_badges) excluded_keys.add(cannonical_badges); - // TODO: next we check for the author's thumbnail - author = { - type: 'misc', - misc_type: 'Author', - optional: false, - params: [ - cannonical_channel_nav[0], - is_badges ? cannonical_badges : undefined - ] - }; - } + const unchanged_keys = key_info.filter(([ key ]) => !changed_keys.has(key)); - if (author) { - key_info.push([ 'author', author ]); - } + const resolved_key_info_map = new Map([ ...unchanged_keys, ...changed_keys ]); + const resolved_key_info = [ ...resolved_key_info_map.entries() ]; - return key_info.filter(([ key ]) => !excluded_keys.has(key)); - } - static #introspect(classdata: any) { - const key_info = this.#passOne(classdata); - return this.#passTwo(key_info); - } - /** - * Infer the type of a key given its value - * @param key - The key to infer the type of - * @param value - The value of the key - * @returns The inferred type - */ - static inferType(key: string, value: any): InferenceType { - let return_value: string | Record | boolean | MiscInferenceType = false; - if (return_value = this.isRenderer(value)) { - this.#renderers_examples[return_value] = value[Reflect.ownKeys(value)[0]]; - return { - type: 'renderer', - renderers: [ return_value ], - optional: false - }; - } - if (return_value = this.isRendererList(value)) { - for (const [ key, value ] of Object.entries(return_value)) { - this.#renderers_examples[key] = value; - } - return { - type: 'renderer_list', - renderers: Object.keys(return_value), - optional: false - }; - } - if (return_value = this.isMiscType(key, value)) { - return return_value as MiscInferenceType; - } - const primative_type = typeof value; - if (primative_type === 'object') - return { - type: 'object', - keys: Object.entries(value).map(([ key, value ]) => [ key, this.inferType(key, value) ]), - optional: false - }; - return { - type: 'primative', - typeof: [ primative_type ], - optional: false - }; - } - /** - * Checks if the given value is an array of renderers - * @param value - The value to check - * @returns If it is a renderer list, return an object with keys being the classnames, and values being an example of that class. - * Otherwise, return false. - */ - static isRendererList(value: any) { - const arr = Array.isArray(value); - const is_list = arr && value.every((item) => this.isRenderer(item)); - return ( - is_list ? - Object.fromEntries(value.map((item) => { - const key = Reflect.ownKeys(item)[0].toString(); - return [ Parser.sanitizeClassName(key), item[key] ]; - })) : - false - ); - } - /** - * Check if the given value is a misc type. - * @param key - The key of the value - * @param value - The value to check - * @returns If it is a misc type, return the InferenceType. Otherwise, return false. - */ - static isMiscType(key: string, value: any): MiscInferenceType | false { - // NavigationEndpoint - if ((key.endsWith('Endpoint') || key.endsWith('Command') || key === 'endpoint') && typeof value === 'object') { - return { - type: 'misc', - endpoint: new NavigationEndpoint(value), - optional: false, - misc_type: 'NavigationEndpoint' - }; - } - // Text - if (typeof value === 'object' && (Reflect.has(value, 'simpleText') || Reflect.has(value, 'runs'))) { - const textNode = new Text(value); - return { - type: 'misc', - misc_type: 'Text', - optional: false, - endpoint: textNode.endpoint, - text: textNode.toString() - }; - } - // Thumbnail - if (typeof value === 'object' && Reflect.has(value, 'thumbnails') && Array.isArray(value.thumbnails)) { - return { - type: 'misc', - misc_type: 'Thumbnail', - optional: false - }; - } - return false; - } - /** - * Check if the given value is a renderer - * @param value - The value to check - * @returns If it is a renderer, return the class name. Otherwise, return false. - */ - static isRenderer(value: any) { - const is_object = typeof value === 'object'; - if (!is_object) return false; - const keys = Reflect.ownKeys(value); - if (keys.length === 1 && keys[0].toString().includes('Renderer')) { - return Parser.sanitizeClassName(keys[0].toString()); - } - return false; - } -} \ No newline at end of file + return { + resolved_key_info, + changed_keys: [ ...changed_keys.entries() ] + }; +} diff --git a/src/parser/index.ts b/src/parser/index.ts index 2b06e605a2..6c14a3b15e 100644 --- a/src/parser/index.ts +++ b/src/parser/index.ts @@ -1,5 +1,5 @@ -export { default as Parser } from './parser.js'; -export * from './parser.js'; +export * as Parser from './parser.js'; +export * from './continuations.js'; export * from './types/index.js'; export * as Misc from './misc.js'; export * as YTNodes from './nodes.js'; @@ -8,5 +8,5 @@ export * as YTMusic from './ytmusic/index.js'; export * as YTKids from './ytkids/index.js'; export * as Helpers from './helpers.js'; export * as Generator from './generator.js'; -import Parser from './parser.js'; +import * as Parser from './parser.js'; export default Parser; \ No newline at end of file diff --git a/src/parser/parser.ts b/src/parser/parser.ts index e4f66cc0f7..ccd8801cbf 100644 --- a/src/parser/parser.ts +++ b/src/parser/parser.ts @@ -5,10 +5,6 @@ import PlayerAnnotationsExpanded from './classes/PlayerAnnotationsExpanded.js'; import PlayerCaptionsTracklist from './classes/PlayerCaptionsTracklist.js'; import PlayerLiveStoryboardSpec from './classes/PlayerLiveStoryboardSpec.js'; import PlayerStoryboardSpec from './classes/PlayerStoryboardSpec.js'; -import Message from './classes/Message.js'; -import LiveChatParticipantsList from './classes/LiveChatParticipantsList.js'; -import LiveChatHeader from './classes/LiveChatHeader.js'; -import LiveChatItemList from './classes/LiveChatItemList.js'; import Alert from './classes/Alert.js'; import type { IParsedResponse, IRawResponse, RawData, RawNode } from './types/index.js'; @@ -17,749 +13,620 @@ import MusicMultiSelectMenuItem from './classes/menus/MusicMultiSelectMenuItem.j import Format from './classes/misc/Format.js'; import VideoDetails from './classes/misc/VideoDetails.js'; import NavigationEndpoint from './classes/NavigationEndpoint.js'; -import Thumbnail from './classes/misc/Thumbnail.js'; import { InnertubeError, ParsingError, Platform } from '../utils/Utils.js'; -import type { ObservedArray, YTNodeConstructor } from './helpers.js'; -import { Memo, observe, SuperParsedResult, YTNode } from './helpers.js'; +import type { ObservedArray, YTNodeConstructor, YTNode } from './helpers.js'; +import { Memo, observe, SuperParsedResult } from './helpers.js'; import * as YTNodes from './nodes.js'; -import { YTNodeGenerator } from './generator.js'; +import type { KeyInfo } from './generator.js'; +import { camelToSnake, generateRuntimeClass, generateTypescriptClass } from './generator.js'; +import { Continuation, ItemSectionContinuation, SectionListContinuation, LiveChatContinuation, MusicPlaylistShelfContinuation, MusicShelfContinuation, GridContinuation, PlaylistPanelContinuation, NavigateAction, ShowMiniplayerCommand, ReloadContinuationItemsCommand } from './continuations.js'; + +export type ParserError = { + classname: string, +} & ({ + error_type: 'typecheck', + classdata: RawNode, + expected: string | string[] +} | { + error_type: 'parse', + classdata: RawNode, + error: unknown +} | { + error_type: 'mutation_data_missing' +} | { + error_type: 'mutation_data_invalid', + total: number, + failed: number, + titles: string[] +} | { + error_type: 'class_not_found', + key_info: KeyInfo, +} | { + error_type: 'class_changed', + key_info: KeyInfo, + changed_keys: KeyInfo +}); -export type ParserError = { classname: string, classdata: any, err: any }; export type ParserErrorHandler = (error: ParserError) => void; -export default class Parser { - static #errorHandler: ParserErrorHandler = Parser.#printError; - static #memo: Memo | null = null; - - static setParserErrorHandler(handler: ParserErrorHandler) { - this.#errorHandler = handler; - } - - static #clearMemo() { - Parser.#memo = null; - } - - static #createMemo() { - Parser.#memo = new Memo(); - } - - static #addToMemo(classname: string, result: YTNode) { - if (!Parser.#memo) - return; - - const list = Parser.#memo.get(classname); - if (!list) - return Parser.#memo.set(classname, [ result ]); - - list.push(result); - } - - static #getMemo() { - if (!Parser.#memo) - throw new Error('Parser#getMemo() called before Parser#createMemo()'); - return Parser.#memo; +const IGNORED_LIST = new Set([ + 'AdSlot', + 'DisplayAd', + 'SearchPyv', + 'MealbarPromo', + 'PrimetimePromo', + 'BackgroundPromo', + 'PromotedSparklesWeb', + 'RunAttestationCommand', + 'CompactPromotedVideo', + 'BrandVideoShelf', + 'BrandVideoSingleton', + 'StatementBanner', + 'GuideSigninPromo', + 'AdsEngagementPanelContent' +]); + +const RUNTIME_NODES = new Map(Object.entries(YTNodes)); + +const DYNAMIC_NODES = new Map(); + +let MEMO: Memo | null = null; + +let ERROR_HANDLER: ParserErrorHandler = ({ classname, ...context }: ParserError) => { + switch (context.error_type) { + case 'parse': + if (context.error instanceof Error) { + console.warn( + new InnertubeError( + `Something went wrong at ${classname}!\n` + + `This is a bug, please report it at ${Platform.shim.info.bugs_url}`, { + stack: context.error.stack + } + ) + ); + } + break; + case 'typecheck': + console.warn( + new ParsingError( + `Type mismatch, got ${classname} expected ${Array.isArray(context.expected) ? context.expected.join(' | ') : context.expected} at ${context.classdata}` + ) + ); + break; + case 'mutation_data_missing': + console.warn( + new InnertubeError( + 'Mutation data required for processing MusicMultiSelectMenuItems, but none found.\n' + + `This is a bug, please report it at ${Platform.shim.info.bugs_url}` + ) + ); + break; + case 'mutation_data_invalid': + console.warn( + new InnertubeError( + `Mutation data missing or invalid for ${context.failed} out of ${context.total} MusicMultiSelectMenuItems. ` + + `The titles of the failed items are: ${context.titles.join(', ')}.\n` + + `This is a bug, please report it at ${Platform.shim.info.bugs_url}` + ) + ); + break; + case 'class_not_found': + console.warn( + new InnertubeError( + `${classname} not found!\n` + + `This is a bug, want to help us fix it? Follow the instructions at ${Platform.shim.info.repo_url}/blob/main/docs/updating-the-parser.md or report it at ${Platform.shim.info.bugs_url}!\n` + + `Introspected and JIT generated this class in the meantime:\n${generateTypescriptClass(classname, context.key_info)}` + ) + ); + break; + case 'class_changed': + console.warn( + `${classname} changed!\n` + + `The following keys where altered: ${context.changed_keys.map(([ key ]) => camelToSnake(key)).join(', ')}\n` + + `The class has changed to:\n${generateTypescriptClass(classname, context.key_info)}` + ); + break; + default: + console.warn( + 'Unreachable code reached at ParserErrorHandler' + ); + break; } +}; - /** - * Parses given InnerTube response. - * @param data - Raw data. - */ - static parseResponse(data: IRawResponse): T { - const parsed_data = {} as T; - - this.#createMemo(); - const contents = this.parse(data.contents); - const contents_memo = this.#getMemo(); - if (contents) { - parsed_data.contents = contents; - parsed_data.contents_memo = contents_memo; - } - this.#clearMemo(); - - this.#createMemo(); - const on_response_received_actions = data.onResponseReceivedActions ? this.parseRR(data.onResponseReceivedActions) : null; - const on_response_received_actions_memo = this.#getMemo(); - if (on_response_received_actions) { - parsed_data.on_response_received_actions = on_response_received_actions; - parsed_data.on_response_received_actions_memo = on_response_received_actions_memo; - } - this.#clearMemo(); - - this.#createMemo(); - const on_response_received_endpoints = data.onResponseReceivedEndpoints ? this.parseRR(data.onResponseReceivedEndpoints) : null; - const on_response_received_endpoints_memo = this.#getMemo(); - if (on_response_received_endpoints) { - parsed_data.on_response_received_endpoints = on_response_received_endpoints; - parsed_data.on_response_received_endpoints_memo = on_response_received_endpoints_memo; - } - this.#clearMemo(); - - this.#createMemo(); - const on_response_received_commands = data.onResponseReceivedCommands ? this.parseRR(data.onResponseReceivedCommands) : null; - const on_response_received_commands_memo = this.#getMemo(); - if (on_response_received_commands) { - parsed_data.on_response_received_commands = on_response_received_commands; - parsed_data.on_response_received_commands_memo = on_response_received_commands_memo; - } - this.#clearMemo(); - - this.#createMemo(); - const continuation_contents = data.continuationContents ? this.parseLC(data.continuationContents) : null; - const continuation_contents_memo = this.#getMemo(); - if (continuation_contents) { - parsed_data.continuation_contents = continuation_contents; - parsed_data.continuation_contents_memo = continuation_contents_memo; - } - this.#clearMemo(); - - this.#createMemo(); - const actions = data.actions ? this.parseActions(data.actions) : null; - const actions_memo = this.#getMemo(); - if (actions) { - parsed_data.actions = actions; - parsed_data.actions_memo = actions_memo; - } - this.#clearMemo(); - - this.#createMemo(); - const live_chat_item_context_menu_supported_renderers = data.liveChatItemContextMenuSupportedRenderers ? this.parseItem(data.liveChatItemContextMenuSupportedRenderers) : null; - const live_chat_item_context_menu_supported_renderers_memo = this.#getMemo(); - if (live_chat_item_context_menu_supported_renderers) { - parsed_data.live_chat_item_context_menu_supported_renderers = live_chat_item_context_menu_supported_renderers; - parsed_data.live_chat_item_context_menu_supported_renderers_memo = live_chat_item_context_menu_supported_renderers_memo; - } - this.#clearMemo(); - - this.#createMemo(); - const header = data.header ? this.parse(data.header) : null; - const header_memo = this.#getMemo(); - if (header) { - parsed_data.header = header; - parsed_data.header_memo = header_memo; - } - this.#clearMemo(); - - this.#createMemo(); - const sidebar = data.sidebar ? this.parseItem(data.sidebar) : null; - const sidebar_memo = this.#getMemo(); - if (sidebar) { - parsed_data.sidebar = sidebar; - parsed_data.sidebar_memo = sidebar_memo; - } - this.#clearMemo(); - - this.applyMutations(contents_memo, data.frameworkUpdates?.entityBatchUpdate?.mutations); - - const continuation = data.continuation ? this.parseC(data.continuation) : null; - if (continuation) { - parsed_data.continuation = continuation; - } +export function setParserErrorHandler(handler: ParserErrorHandler) { + ERROR_HANDLER = handler; +} - const metadata = this.parse(data.metadata); - if (metadata) { - parsed_data.metadata = metadata; - } +function _clearMemo() { + MEMO = null; +} - const microformat = this.parseItem(data.microformat); - if (microformat) { - parsed_data.microformat = microformat; - } +function _createMemo() { + MEMO = new Memo(); +} - const overlay = this.parseItem(data.overlay); - if (overlay) { - parsed_data.overlay = overlay; - } +function _addToMemo(classname: string, result: YTNode) { + if (!MEMO) + return; - const alerts = this.parseArray(data.alerts, Alert); - if (alerts.length) { - parsed_data.alerts = alerts; - } + const list = MEMO.get(classname); + if (!list) + return MEMO.set(classname, [ result ]); - const refinements = data.refinements; - if (refinements) { - parsed_data.refinements = refinements; - } + list.push(result); +} - const estimated_results = data.estimatedResults ? parseInt(data.estimatedResults) : null; - if (estimated_results) { - parsed_data.estimated_results = estimated_results; - } +function _getMemo() { + if (!MEMO) + throw new Error('Parser#getMemo() called before Parser#createMemo()'); + return MEMO; +} - const player_overlays = this.parse(data.playerOverlays); - if (player_overlays) { - parsed_data.player_overlays = player_overlays; - } +export function shouldIgnore(classname: string) { + return IGNORED_LIST.has(classname); +} - const playback_tracking = data.playbackTracking ? { - videostats_watchtime_url: data.playbackTracking.videostatsWatchtimeUrl.baseUrl, - videostats_playback_url: data.playbackTracking.videostatsPlaybackUrl.baseUrl - } : null; +export function sanitizeClassName(input: string) { + return (input.charAt(0).toUpperCase() + input.slice(1)) + .replace(/Renderer|Model/g, '') + .replace(/Radio/g, 'Mix').trim(); +} - if (playback_tracking) { - parsed_data.playback_tracking = playback_tracking; - } +export function getParserByName(classname: string) { + const ParserConstructor = RUNTIME_NODES.get(classname); - const playability_status = data.playabilityStatus ? { - status: data.playabilityStatus.status, - reason: data.playabilityStatus.reason || '', - embeddable: !!data.playabilityStatus.playableInEmbed || false, - audio_only_playablility: this.parseItem(data.playabilityStatus.audioOnlyPlayability, AudioOnlyPlayability), - error_screen: this.parseItem(data.playabilityStatus.errorScreen) - } : null; + if (!ParserConstructor) { + const error = new Error(`Module not found: ${classname}`); + (error as any).code = 'MODULE_NOT_FOUND'; + throw error; + } - if (playability_status) { - parsed_data.playability_status = playability_status; - } + return ParserConstructor; +} - const streaming_data = data.streamingData ? { - expires: new Date(Date.now() + parseInt(data.streamingData.expiresInSeconds) * 1000), - formats: Parser.parseFormats(data.streamingData.formats), - adaptive_formats: Parser.parseFormats(data.streamingData.adaptiveFormats), - dash_manifest_url: data.streamingData.dashManifestUrl || null, - hls_manifest_url: data.streamingData.hlsManifestUrl || null - } : undefined; +export function hasParser(classname: string) { + return RUNTIME_NODES.has(classname); +} - if (streaming_data) { - parsed_data.streaming_data = streaming_data; - } +export function addRuntimeParser(classname: string, ParserConstructor: YTNodeConstructor) { + RUNTIME_NODES.set(classname, ParserConstructor); + DYNAMIC_NODES.set(classname, ParserConstructor); +} - const current_video_endpoint = data.currentVideoEndpoint ? new NavigationEndpoint(data.currentVideoEndpoint) : null; - if (current_video_endpoint) { - parsed_data.current_video_endpoint = current_video_endpoint; - } +export function getDynamicParsers() { + return Object.fromEntries(DYNAMIC_NODES); +} - const endpoint = data.endpoint ? new NavigationEndpoint(data.endpoint) : null; - if (endpoint) { - parsed_data.endpoint = endpoint; - } +/** + * Parses given InnerTube response. + * @param data - Raw data. + */ +export function parseResponse(data: IRawResponse): T { + const parsed_data = {} as T; - const captions = this.parseItem(data.captions, PlayerCaptionsTracklist); - if (captions) { - parsed_data.captions = captions; - } + _createMemo(); + const contents = parse(data.contents); + const contents_memo = _getMemo(); + if (contents) { + parsed_data.contents = contents; + parsed_data.contents_memo = contents_memo; + } + _clearMemo(); - const video_details = data.videoDetails ? new VideoDetails(data.videoDetails) : null; - if (video_details) { - parsed_data.video_details = video_details; - } + _createMemo(); + const on_response_received_actions = data.onResponseReceivedActions ? parseRR(data.onResponseReceivedActions) : null; + const on_response_received_actions_memo = _getMemo(); + if (on_response_received_actions) { + parsed_data.on_response_received_actions = on_response_received_actions; + parsed_data.on_response_received_actions_memo = on_response_received_actions_memo; + } + _clearMemo(); - const annotations = this.parseArray(data.annotations, PlayerAnnotationsExpanded); - if (annotations.length) { - parsed_data.annotations = annotations; - } + _createMemo(); + const on_response_received_endpoints = data.onResponseReceivedEndpoints ? parseRR(data.onResponseReceivedEndpoints) : null; + const on_response_received_endpoints_memo = _getMemo(); + if (on_response_received_endpoints) { + parsed_data.on_response_received_endpoints = on_response_received_endpoints; + parsed_data.on_response_received_endpoints_memo = on_response_received_endpoints_memo; + } + _clearMemo(); - const storyboards = this.parseItem(data.storyboards, [ PlayerStoryboardSpec, PlayerLiveStoryboardSpec ]); - if (storyboards) { - parsed_data.storyboards = storyboards; - } + _createMemo(); + const on_response_received_commands = data.onResponseReceivedCommands ? parseRR(data.onResponseReceivedCommands) : null; + const on_response_received_commands_memo = _getMemo(); + if (on_response_received_commands) { + parsed_data.on_response_received_commands = on_response_received_commands; + parsed_data.on_response_received_commands_memo = on_response_received_commands_memo; + } + _clearMemo(); - const endscreen = this.parseItem(data.endscreen, Endscreen); - if (endscreen) { - parsed_data.endscreen = endscreen; - } + _createMemo(); + const continuation_contents = data.continuationContents ? parseLC(data.continuationContents) : null; + const continuation_contents_memo = _getMemo(); + if (continuation_contents) { + parsed_data.continuation_contents = continuation_contents; + parsed_data.continuation_contents_memo = continuation_contents_memo; + } + _clearMemo(); - const cards = this.parseItem(data.cards, CardCollection); - if (cards) { - parsed_data.cards = cards; - } + _createMemo(); + const actions = data.actions ? parseActions(data.actions) : null; + const actions_memo = _getMemo(); + if (actions) { + parsed_data.actions = actions; + parsed_data.actions_memo = actions_memo; + } + _clearMemo(); - const engagement_panels = data.engagementPanels?.map((e) => { - const item = this.parseItem(e, YTNodes.EngagementPanelSectionList) as YTNodes.EngagementPanelSectionList; - return item; - }); - if (engagement_panels) { - parsed_data.engagement_panels = engagement_panels; - } - this.#createMemo(); - const items = this.parse(data.items); - if (items) { - parsed_data.items = items; - parsed_data.items_memo = this.#getMemo(); - } - this.#clearMemo(); + _createMemo(); + const live_chat_item_context_menu_supported_renderers = data.liveChatItemContextMenuSupportedRenderers ? parseItem(data.liveChatItemContextMenuSupportedRenderers) : null; + const live_chat_item_context_menu_supported_renderers_memo = _getMemo(); + if (live_chat_item_context_menu_supported_renderers) { + parsed_data.live_chat_item_context_menu_supported_renderers = live_chat_item_context_menu_supported_renderers; + parsed_data.live_chat_item_context_menu_supported_renderers_memo = live_chat_item_context_menu_supported_renderers_memo; + } + _clearMemo(); - return parsed_data; + _createMemo(); + const header = data.header ? parse(data.header) : null; + const header_memo = _getMemo(); + if (header) { + parsed_data.header = header; + parsed_data.header_memo = header_memo; } + _clearMemo(); - /** - * Parses a single item. - * @param data - The data to parse. - * @param validTypes - YTNode types that are allowed to be parsed. - */ - static parseItem[]>(data: RawNode | undefined, validTypes: K): InstanceType | null; - static parseItem(data: RawNode | undefined, validTypes: YTNodeConstructor): T | null; - static parseItem(data?: RawNode) : YTNode; - static parseItem(data?: RawNode, validTypes?: YTNodeConstructor | YTNodeConstructor[]) { - if (!data) return null; + _createMemo(); + const sidebar = data.sidebar ? parseItem(data.sidebar) : null; + const sidebar_memo = _getMemo(); + if (sidebar) { + parsed_data.sidebar = sidebar; + parsed_data.sidebar_memo = sidebar_memo; + } + _clearMemo(); - const keys = Object.keys(data); + applyMutations(contents_memo, data.frameworkUpdates?.entityBatchUpdate?.mutations); - if (!keys.length) - return null; + const continuation = data.continuation ? parseC(data.continuation) : null; + if (continuation) { + parsed_data.continuation = continuation; + } - const classname = this.sanitizeClassName(keys[0]); + const metadata = parse(data.metadata); + if (metadata) { + parsed_data.metadata = metadata; + } - if (!this.shouldIgnore(classname)) { - try { - const has_target_class = this.hasParser(classname); + const microformat = parseItem(data.microformat); + if (microformat) { + parsed_data.microformat = microformat; + } - const TargetClass = has_target_class ? this.getParserByName(classname) : YTNodeGenerator.generateRuntimeClass(classname, data[keys[0]]); + const overlay = parseItem(data.overlay); + if (overlay) { + parsed_data.overlay = overlay; + } - if (validTypes) { - if (Array.isArray(validTypes)) { - if (!validTypes.some((type) => type.type === TargetClass.type)) - throw new ParsingError(`Type mismatch, got ${classname} but expected one of ${validTypes.map((type) => type.type).join(', ')}`); - } else if (TargetClass.type !== validTypes.type) - throw new ParsingError(`Type mismatch, got ${classname} but expected ${validTypes.type}`); - } + const alerts = parseArray(data.alerts, Alert); + if (alerts.length) { + parsed_data.alerts = alerts; + } - const result = new TargetClass(data[keys[0]]); - this.#addToMemo(classname, result); + const refinements = data.refinements; + if (refinements) { + parsed_data.refinements = refinements; + } - return result; - } catch (err) { - this.#errorHandler({ classname, classdata: data[keys[0]], err }); - return null; - } - } + const estimated_results = data.estimatedResults ? parseInt(data.estimatedResults) : null; + if (estimated_results) { + parsed_data.estimated_results = estimated_results; + } - return null; + const player_overlays = parse(data.playerOverlays); + if (player_overlays) { + parsed_data.player_overlays = player_overlays; } - /** - * Parses an array of items. - * @param data - The data to parse. - * @param validTypes - YTNode types that are allowed to be parsed. - */ - static parseArray[]>(data: RawNode[] | undefined, validTypes: K): ObservedArray>; - static parseArray(data: RawNode[] | undefined, validType: YTNodeConstructor): ObservedArray; - static parseArray(data: RawNode[] | undefined): ObservedArray; - static parseArray(data?: RawNode[], validTypes?: YTNodeConstructor | YTNodeConstructor[]) { - if (Array.isArray(data)) { - const results: YTNode[] = []; - - for (const item of data) { - const result = this.parseItem(item, validTypes as YTNodeConstructor); - if (result) { - results.push(result); - } - } + const playback_tracking = data.playbackTracking ? { + videostats_watchtime_url: data.playbackTracking.videostatsWatchtimeUrl.baseUrl, + videostats_playback_url: data.playbackTracking.videostatsPlaybackUrl.baseUrl + } : null; - return observe(results); - } else if (!data) { - return observe([] as YTNode[]); - } - throw new ParsingError('Expected array but got a single item'); + if (playback_tracking) { + parsed_data.playback_tracking = playback_tracking; } - /** - * Parses an item or an array of items. - * @param data - The data to parse. - * @param requireArray - Whether the data should be parsed as an array. - * @param validTypes - YTNode types that are allowed to be parsed. - */ - static parse[]>(data: RawData, requireArray: true, validTypes?: K): ObservedArray> | null; - static parse(data?: RawData, requireArray?: false | undefined, validTypes?: YTNodeConstructor | YTNodeConstructor[]): SuperParsedResult; - static parse(data?: RawData, requireArray?: boolean, validTypes?: YTNodeConstructor | YTNodeConstructor[]) { - if (!data) return null; - - if (Array.isArray(data)) { - const results: T[] = []; - - for (const item of data) { - const result = this.parseItem(item, validTypes as YTNodeConstructor); - if (result) { - results.push(result); - } - } + const playability_status = data.playabilityStatus ? { + status: data.playabilityStatus.status, + reason: data.playabilityStatus.reason || '', + embeddable: !!data.playabilityStatus.playableInEmbed || false, + audio_only_playablility: parseItem(data.playabilityStatus.audioOnlyPlayability, AudioOnlyPlayability), + error_screen: parseItem(data.playabilityStatus.errorScreen) + } : null; - const res = observe(results); + if (playability_status) { + parsed_data.playability_status = playability_status; + } - return requireArray ? res : new SuperParsedResult(observe(results)); - } else if (requireArray) { - throw new ParsingError('Expected array but got a single item'); - } + const streaming_data = data.streamingData ? { + expires: new Date(Date.now() + parseInt(data.streamingData.expiresInSeconds) * 1000), + formats: parseFormats(data.streamingData.formats), + adaptive_formats: parseFormats(data.streamingData.adaptiveFormats), + dash_manifest_url: data.streamingData.dashManifestUrl || null, + hls_manifest_url: data.streamingData.hlsManifestUrl || null + } : undefined; - return new SuperParsedResult(this.parseItem(data, validTypes as YTNodeConstructor)); + if (streaming_data) { + parsed_data.streaming_data = streaming_data; } - static parseC(data: RawNode) { - if (data.timedContinuationData) - return new Continuation({ continuation: data.timedContinuationData, type: 'timed' }); - return null; + const current_video_endpoint = data.currentVideoEndpoint ? new NavigationEndpoint(data.currentVideoEndpoint) : null; + if (current_video_endpoint) { + parsed_data.current_video_endpoint = current_video_endpoint; } - static parseLC(data: RawNode) { - if (data.itemSectionContinuation) - return new ItemSectionContinuation(data.itemSectionContinuation); - if (data.sectionListContinuation) - return new SectionListContinuation(data.sectionListContinuation); - if (data.liveChatContinuation) - return new LiveChatContinuation(data.liveChatContinuation); - if (data.musicPlaylistShelfContinuation) - return new MusicPlaylistShelfContinuation(data.musicPlaylistShelfContinuation); - if (data.musicShelfContinuation) - return new MusicShelfContinuation(data.musicShelfContinuation); - if (data.gridContinuation) - return new GridContinuation(data.gridContinuation); - if (data.playlistPanelContinuation) - return new PlaylistPanelContinuation(data.playlistPanelContinuation); - - return null; + const endpoint = data.endpoint ? new NavigationEndpoint(data.endpoint) : null; + if (endpoint) { + parsed_data.endpoint = endpoint; } - static parseRR(actions: RawNode[]) { - return observe(actions.map((action: any) => { - if (action.navigateAction) - return new NavigateAction(action.navigateAction); - if (action.showMiniplayerCommand) - return new ShowMiniplayerCommand(action.showMiniplayerCommand); - if (action.reloadContinuationItemsCommand) - return new ReloadContinuationItemsCommand(action.reloadContinuationItemsCommand); - if (action.appendContinuationItemsAction) - return new AppendContinuationItemsAction(action.appendContinuationItemsAction); - }).filter((item) => item) as (ReloadContinuationItemsCommand | AppendContinuationItemsAction)[]); - } - - static parseActions(data: RawData) { - if (Array.isArray(data)) { - return Parser.parse(data.map((action) => { - delete action.clickTrackingParams; - return action; - })); - } - return new SuperParsedResult(this.parseItem(data)); + const captions = parseItem(data.captions, PlayerCaptionsTracklist); + if (captions) { + parsed_data.captions = captions; } - static parseFormats(formats: RawNode[]): Format[] { - return formats?.map((format) => new Format(format)) || []; + const video_details = data.videoDetails ? new VideoDetails(data.videoDetails) : null; + if (video_details) { + parsed_data.video_details = video_details; } - static applyMutations(memo: Memo, mutations: RawNode[]) { - // Apply mutations to MusicMultiSelectMenuItems - const music_multi_select_menu_items = memo.getType(MusicMultiSelectMenuItem); - - if (music_multi_select_menu_items.length > 0 && !mutations) { - console.warn( - new InnertubeError( - 'Mutation data required for processing MusicMultiSelectMenuItems, but none found.\n' + - `This is a bug, please report it at ${Platform.shim.info.bugs_url}` - ) - ); - } else { - const missing_or_invalid_mutations = []; - - for (const menu_item of music_multi_select_menu_items) { - const mutation = mutations - .find((mutation) => mutation.payload?.musicFormBooleanChoice?.id === menu_item.form_item_entity_key); - - const choice = mutation?.payload.musicFormBooleanChoice; - - if (choice?.selected !== undefined && choice?.opaqueToken) { - menu_item.selected = choice.selected; - } else { - missing_or_invalid_mutations.push(`'${menu_item.title}'`); - } - } - if (missing_or_invalid_mutations.length > 0) { - console.warn( - new InnertubeError( - `Mutation data missing or invalid for ${missing_or_invalid_mutations.length} out of ${music_multi_select_menu_items.length} MusicMultiSelectMenuItems. ` + - `The titles of the failed items are: ${missing_or_invalid_mutations.join(', ')}.\n` + - `This is a bug, please report it at ${Platform.shim.info.bugs_url}` - ) - ); - } - } + const annotations = parseArray(data.annotations, PlayerAnnotationsExpanded); + if (annotations.length) { + parsed_data.annotations = annotations; } - static #printError({ classname, classdata, err }: ParserError) { - if (err.code == 'MODULE_NOT_FOUND') { - return console.warn( - new InnertubeError( - `${classname} not found!\n` + - `This is a bug, want to help us fix it? Follow the instructions at ${Platform.shim.info.repo_url.split('#')[0]}/blob/main/docs/updating-the-parser.md or report it at ${Platform.shim.info.bugs_url}!`, classdata - ) - ); - } - - console.warn( - new InnertubeError( - `Something went wrong at ${classname}!\n` + - `This is a bug, please report it at ${Platform.shim.info.bugs_url}`, { stack: err.stack } - ) - ); - } - - static sanitizeClassName(input: string) { - return (input.charAt(0).toUpperCase() + input.slice(1)) - .replace(/Renderer|Model/g, '') - .replace(/Radio/g, 'Mix').trim(); - } - - static ignore_list = new Set([ - 'AdSlot', - 'DisplayAd', - 'SearchPyv', - 'MealbarPromo', - 'PrimetimePromo', - 'BackgroundPromo', - 'PromotedSparklesWeb', - 'RunAttestationCommand', - 'CompactPromotedVideo', - 'BrandVideoShelf', - 'BrandVideoSingleton', - 'StatementBanner', - 'GuideSigninPromo', - 'AdsEngagementPanelContent' - ]); - - static shouldIgnore(classname: string) { - return this.ignore_list.has(classname); - } - - static #rt_nodes = new Map(Object.entries(YTNodes)); - static #dynamic_nodes = new Map(); - - static getParserByName(classname: string) { - const ParserConstructor = this.#rt_nodes.get(classname); - - if (!ParserConstructor) { - const error = new Error(`Module not found: ${classname}`); - (error as any).code = 'MODULE_NOT_FOUND'; - throw error; - } - - return ParserConstructor; + const storyboards = parseItem(data.storyboards, [ PlayerStoryboardSpec, PlayerLiveStoryboardSpec ]); + if (storyboards) { + parsed_data.storyboards = storyboards; } - static hasParser(classname: string) { - return this.#rt_nodes.has(classname); + const endscreen = parseItem(data.endscreen, Endscreen); + if (endscreen) { + parsed_data.endscreen = endscreen; } - static addRuntimeParser(classname: string, ParserConstructor: YTNodeConstructor) { - this.#rt_nodes.set(classname, ParserConstructor); - this.#dynamic_nodes.set(classname, ParserConstructor); + const cards = parseItem(data.cards, CardCollection); + if (cards) { + parsed_data.cards = cards; } - static getDynamicParsers() { - return Object.fromEntries(this.#dynamic_nodes); + const engagement_panels = data.engagementPanels?.map((e) => { + const item = parseItem(e, YTNodes.EngagementPanelSectionList) as YTNodes.EngagementPanelSectionList; + return item; + }); + if (engagement_panels) { + parsed_data.engagement_panels = engagement_panels; + } + _createMemo(); + const items = parse(data.items); + if (items) { + parsed_data.items = items; + parsed_data.items_memo = _getMemo(); } + _clearMemo(); + + return parsed_data; } -// Continuation +/** + * Parses a single item. + * @param data - The data to parse. + * @param validTypes - YTNode types that are allowed to be parsed. + */ +export function parseItem[]>(data: RawNode | undefined, validTypes: K): InstanceType | null; +export function parseItem(data: RawNode | undefined, validTypes: YTNodeConstructor): T | null; +export function parseItem(data?: RawNode) : YTNode; +export function parseItem(data?: RawNode, validTypes?: YTNodeConstructor | YTNodeConstructor[]) { + if (!data) return null; + + const keys = Object.keys(data); -export class ItemSectionContinuation extends YTNode { - static readonly type = 'itemSectionContinuation'; + if (!keys.length) + return null; - contents: ObservedArray | null; - continuation?: string; + const classname = sanitizeClassName(keys[0]); + + if (!shouldIgnore(classname)) { + try { + const has_target_class = hasParser(classname); + + const TargetClass = has_target_class ? + getParserByName(classname) : + generateRuntimeClass(classname, data[keys[0]], ERROR_HANDLER); + + if (validTypes) { + if (Array.isArray(validTypes)) { + if (!validTypes.some((type) => type.type === TargetClass.type)) { + ERROR_HANDLER({ + classdata: data[keys[0]], + classname, + error_type: 'typecheck', + expected: validTypes.map((type) => type.type) + }); + return null; + } + } else if (TargetClass.type !== validTypes.type) { + ERROR_HANDLER({ + classdata: data[keys[0]], + classname, + error_type: 'typecheck', + expected: validTypes.type + }); + return null; + } + } - constructor(data: RawNode) { - super(); - this.contents = Parser.parseArray(data.contents); - if (Array.isArray(data.continuations)) { - this.continuation = data.continuations?.at(0)?.nextContinuationData?.continuation; + const result = new TargetClass(data[keys[0]]); + _addToMemo(classname, result); + + return result; + } catch (err) { + ERROR_HANDLER({ + classname, + classdata: data[keys[0]], + error: err, + error_type: 'parse' + }); + return null; } } -} - -export class NavigateAction extends YTNode { - static readonly type = 'navigateAction'; - - endpoint: NavigationEndpoint; - constructor(data: RawNode) { - super(); - this.endpoint = new NavigationEndpoint(data.endpoint); - } + return null; } -export class ShowMiniplayerCommand extends YTNode { - static readonly type = 'showMiniplayerCommand'; - - miniplayer_command: NavigationEndpoint; - show_premium_branding: boolean; +/** + * Parses an array of items. + * @param data - The data to parse. + * @param validTypes - YTNode types that are allowed to be parsed. + */ +export function parseArray[]>(data: RawNode[] | undefined, validTypes: K): ObservedArray>; +export function parseArray(data: RawNode[] | undefined, validType: YTNodeConstructor): ObservedArray; +export function parseArray(data: RawNode[] | undefined): ObservedArray; +export function parseArray(data?: RawNode[], validTypes?: YTNodeConstructor | YTNodeConstructor[]) { + if (Array.isArray(data)) { + const results: YTNode[] = []; + + for (const item of data) { + const result = parseItem(item, validTypes as YTNodeConstructor); + if (result) { + results.push(result); + } + } - constructor(data: RawNode) { - super(); - this.miniplayer_command = new NavigationEndpoint(data.miniplayerCommand); - this.show_premium_branding = data.showPremiumBranding; + return observe(results); + } else if (!data) { + return observe([] as YTNode[]); } + throw new ParsingError('Expected array but got a single item'); } -export class AppendContinuationItemsAction extends YTNode { - static readonly type = 'appendContinuationItemsAction'; +/** + * Parses an item or an array of items. + * @param data - The data to parse. + * @param requireArray - Whether the data should be parsed as an array. + * @param validTypes - YTNode types that are allowed to be parsed. + */ +export function parse[]>(data: RawData, requireArray: true, validTypes?: K): ObservedArray> | null; +export function parse(data?: RawData, requireArray?: false | undefined, validTypes?: YTNodeConstructor | YTNodeConstructor[]): SuperParsedResult; +export function parse(data?: RawData, requireArray?: boolean, validTypes?: YTNodeConstructor | YTNodeConstructor[]) { + if (!data) return null; + + if (Array.isArray(data)) { + const results: T[] = []; + + for (const item of data) { + const result = parseItem(item, validTypes as YTNodeConstructor); + if (result) { + results.push(result); + } + } - contents: ObservedArray | null; + const res = observe(results); - constructor(data: RawNode) { - super(); - this.contents = Parser.parseArray(data.continuationItems); + return requireArray ? res : new SuperParsedResult(res); + } else if (requireArray) { + throw new ParsingError('Expected array but got a single item'); } -} - -export class ReloadContinuationItemsCommand extends YTNode { - static readonly type = 'reloadContinuationItemsCommand'; - target_id: string; - contents: ObservedArray | null; - slot?: string; - - constructor(data: RawNode) { - super(); - this.target_id = data.targetId; - this.contents = Parser.parse(data.continuationItems, true); - this.slot = data?.slot; - } + return new SuperParsedResult(parseItem(data, validTypes as YTNodeConstructor)); } -export class SectionListContinuation extends YTNode { - static readonly type = 'sectionListContinuation'; - - continuation: string; - contents: ObservedArray | null; - - constructor(data: RawNode) { - super(); - this.contents = Parser.parse(data.contents, true); - this.continuation = - data.continuations?.[0]?.nextContinuationData?.continuation || - data.continuations?.[0]?.reloadContinuationData?.continuation || null; - } +export function parseC(data: RawNode) { + if (data.timedContinuationData) + return new Continuation({ continuation: data.timedContinuationData, type: 'timed' }); + return null; } -export class MusicPlaylistShelfContinuation extends YTNode { - static readonly type = 'musicPlaylistShelfContinuation'; - - continuation: string; - contents: ObservedArray | null; - - constructor(data: RawNode) { - super(); - this.contents = Parser.parse(data.contents, true); - this.continuation = data.continuations?.[0].nextContinuationData.continuation || null; - } +export function parseLC(data: RawNode) { + if (data.itemSectionContinuation) + return new ItemSectionContinuation(data.itemSectionContinuation); + if (data.sectionListContinuation) + return new SectionListContinuation(data.sectionListContinuation); + if (data.liveChatContinuation) + return new LiveChatContinuation(data.liveChatContinuation); + if (data.musicPlaylistShelfContinuation) + return new MusicPlaylistShelfContinuation(data.musicPlaylistShelfContinuation); + if (data.musicShelfContinuation) + return new MusicShelfContinuation(data.musicShelfContinuation); + if (data.gridContinuation) + return new GridContinuation(data.gridContinuation); + if (data.playlistPanelContinuation) + return new PlaylistPanelContinuation(data.playlistPanelContinuation); + + return null; } -export class MusicShelfContinuation extends YTNode { - static readonly type = 'musicShelfContinuation'; - - continuation: string; - contents: ObservedArray | null; - - constructor(data: RawNode) { - super(); - this.contents = Parser.parseArray(data.contents); - this.continuation = - data.continuations?.[0].nextContinuationData?.continuation || - data.continuations?.[0].reloadContinuationData?.continuation || null; - } +export function parseRR(actions: RawNode[]) { + return observe(actions.map((action: any) => { + if (action.navigateAction) + return new NavigateAction(action.navigateAction); + if (action.showMiniplayerCommand) + return new ShowMiniplayerCommand(action.showMiniplayerCommand); + if (action.reloadContinuationItemsCommand) + return new ReloadContinuationItemsCommand(action.reloadContinuationItemsCommand); + if (action.appendContinuationItemsAction) + return new YTNodes.AppendContinuationItemsAction(action.appendContinuationItemsAction); + }).filter((item) => item) as (ReloadContinuationItemsCommand | YTNodes.AppendContinuationItemsAction)[]); } -export class GridContinuation extends YTNode { - static readonly type = 'gridContinuation'; - - continuation: string; - items: ObservedArray | null; - - constructor(data: RawNode) { - super(); - this.items = Parser.parse(data.items, true); - this.continuation = data.continuations?.[0].nextContinuationData.continuation || null; - } - - get contents() { - return this.items; +export function parseActions(data: RawData) { + if (Array.isArray(data)) { + return parse(data.map((action) => { + delete action.clickTrackingParams; + return action; + })); } + return new SuperParsedResult(parseItem(data)); } -export class PlaylistPanelContinuation extends YTNode { - static readonly type = 'playlistPanelContinuation'; - - continuation: string; - contents: ObservedArray | null; - - constructor(data: RawNode) { - super(); - this.contents = Parser.parseArray(data.contents); - this.continuation = data.continuations?.[0]?.nextContinuationData?.continuation || - data.continuations?.[0]?.nextRadioContinuationData?.continuation || null; - } +export function parseFormats(formats: RawNode[]): Format[] { + return formats?.map((format) => new Format(format)) || []; } -export class Continuation extends YTNode { - static readonly type = 'continuation'; - - continuation_type: string; - timeout_ms?: number; - time_until_last_message_ms?: number; - token: string; +export function applyMutations(memo: Memo, mutations: RawNode[]) { + // Apply mutations to MusicMultiSelectMenuItems + const music_multi_select_menu_items = memo.getType(MusicMultiSelectMenuItem); - constructor(data: RawNode) { - super(); - this.continuation_type = data.type; - this.timeout_ms = data.continuation?.timeoutMs; - this.time_until_last_message_ms = data.continuation?.timeUntilLastMessageMsec; - this.token = data.continuation?.continuation; - } -} + if (music_multi_select_menu_items.length > 0 && !mutations) { + ERROR_HANDLER({ + error_type: 'mutation_data_missing', + classname: 'MusicMultiSelectMenuItem' + }); + } else { + const missing_or_invalid_mutations = []; -export class LiveChatContinuation extends YTNode { - static readonly type = 'liveChatContinuation'; - - actions: ObservedArray; - action_panel: YTNode | null; - item_list: LiveChatItemList | null; - header: LiveChatHeader | null; - participants_list: LiveChatParticipantsList | null; - popout_message: Message | null; - emojis: { - emoji_id: string; - shortcuts: string[]; - search_terms: string[]; - image: Thumbnail[]; - }[]; - continuation: Continuation; - viewer_name: string; - - constructor(data: RawNode) { - super(); - this.actions = Parser.parse(data.actions?.map((action: any) => { - delete action.clickTrackingParams; - return action; - }), true) || observe([]); - - this.action_panel = Parser.parseItem(data.actionPanel); - this.item_list = Parser.parseItem(data.itemList, LiveChatItemList); - this.header = Parser.parseItem(data.header, LiveChatHeader); - this.participants_list = Parser.parseItem(data.participantsList, LiveChatParticipantsList); - this.popout_message = Parser.parseItem(data.popoutMessage, Message); - - this.emojis = data.emojis?.map((emoji: any) => ({ - emoji_id: emoji.emojiId, - shortcuts: emoji.shortcuts, - search_terms: emoji.searchTerms, - image: Thumbnail.fromResponse(emoji.image), - is_custom_emoji: emoji.isCustomEmoji - })) || []; - - let continuation, type; - - if (data.continuations?.[0].timedContinuationData) { - type = 'timed'; - continuation = data.continuations?.[0].timedContinuationData; - } else if (data.continuations?.[0].invalidationContinuationData) { - type = 'invalidation'; - continuation = data.continuations?.[0].invalidationContinuationData; - } else if (data.continuations?.[0].liveChatReplayContinuationData) { - type = 'replay'; - continuation = data.continuations?.[0].liveChatReplayContinuationData; - } + for (const menu_item of music_multi_select_menu_items) { + const mutation = mutations + .find((mutation) => mutation.payload?.musicFormBooleanChoice?.id === menu_item.form_item_entity_key); - this.continuation = new Continuation({ continuation, type }); + const choice = mutation?.payload.musicFormBooleanChoice; - this.viewer_name = data.viewerName; + if (choice?.selected !== undefined && choice?.opaqueToken) { + menu_item.selected = choice.selected; + } else { + missing_or_invalid_mutations.push(`'${menu_item.title}'`); + } + } + if (missing_or_invalid_mutations.length > 0) { + ERROR_HANDLER({ + error_type: 'mutation_data_invalid', + classname: 'MusicMultiSelectMenuItem', + total: music_multi_select_menu_items.length, + failed: missing_or_invalid_mutations.length, + titles: missing_or_invalid_mutations + }); + } } } diff --git a/src/parser/types/ParsedResponse.ts b/src/parser/types/ParsedResponse.ts index 9bcd6b18ce..29933ce772 100644 --- a/src/parser/types/ParsedResponse.ts +++ b/src/parser/types/ParsedResponse.ts @@ -1,7 +1,7 @@ import type { Memo, ObservedArray, SuperParsedResult, YTNode } from '../helpers.js'; import type { - ReloadContinuationItemsCommand, AppendContinuationItemsAction, Continuation, GridContinuation, + ReloadContinuationItemsCommand, Continuation, GridContinuation, ItemSectionContinuation, LiveChatContinuation, MusicPlaylistShelfContinuation, MusicShelfContinuation, PlaylistPanelContinuation, SectionListContinuation } from '../index.js'; @@ -18,6 +18,7 @@ import type Alert from '../classes/Alert.js'; import type NavigationEndpoint from '../classes/NavigationEndpoint.js'; import type PlayerAnnotationsExpanded from '../classes/PlayerAnnotationsExpanded.js'; import type EngagementPanelSectionList from '../classes/EngagementPanelSectionList.js'; +import type { AppendContinuationItemsAction } from '../nodes.js'; export interface IParsedResponse { actions?: SuperParsedResult; actions_memo?: Memo; @@ -51,20 +52,8 @@ export interface IParsedResponse { videostats_watchtime_url: string; videostats_playback_url: string; }; - playability_status?: { - status: string; - error_screen: YTNode | null; - audio_only_playablility: AudioOnlyPlayability | null; - embeddable: boolean; - reason: string; - }; - streaming_data?: { - expires: Date; - formats: Format[]; - adaptive_formats: Format[]; - dash_manifest_url: string | null; - hls_manifest_url: string | null; - }; + playability_status?: IPlayabilityStatus; + streaming_data?: IStreamingData; current_video_endpoint?: NavigationEndpoint; endpoint?: NavigationEndpoint; captions?: PlayerCaptionsTracklist; @@ -77,26 +66,22 @@ export interface IParsedResponse { items?: SuperParsedResult; } +export interface IStreamingData { + expires: Date; + formats: Format[]; + adaptive_formats: Format[]; + dash_manifest_url: string | null; + hls_manifest_url: string | null; +} + export interface IPlayerResponse { captions?: PlayerCaptionsTracklist; cards?: CardCollection; endscreen?: Endscreen; microformat?: YTNode; annotations?: ObservedArray; - playability_status: { - status: string; - error_screen: YTNode | null; - audio_only_playablility: AudioOnlyPlayability | null; - embeddable: boolean; - reason: string; - }; - streaming_data?: { - expires: Date; - formats: Format[]; - adaptive_formats: Format[]; - dash_manifest_url: string | null; - hls_manifest_url: string | null; - }; + playability_status: IPlayabilityStatus; + streaming_data?: IStreamingData; playback_tracking?: { videostats_watchtime_url: string; videostats_playback_url: string; @@ -105,6 +90,14 @@ export interface IPlayerResponse { video_details?: VideoDetails; } +export interface IPlayabilityStatus { + status: string; + error_screen: YTNode | null; + audio_only_playablility: AudioOnlyPlayability | null; + embeddable: boolean; + reason: string; +} + export interface INextResponse { contents?: SuperParsedResult; contents_memo?: Memo; diff --git a/src/parser/youtube/LiveChat.ts b/src/parser/youtube/LiveChat.ts index 7b9dde7b91..242cd88425 100644 --- a/src/parser/youtube/LiveChat.ts +++ b/src/parser/youtube/LiveChat.ts @@ -21,7 +21,7 @@ import type AddBannerToLiveChatCommand from '../classes/livechat/AddBannerToLive import type RemoveBannerForLiveChatCommand from '../classes/livechat/RemoveBannerForLiveChatCommand.js'; import type ShowLiveChatTooltipCommand from '../classes/livechat/ShowLiveChatTooltipCommand.js'; -import Proto from '../../proto/index.js'; +import * as Proto from '../../proto/index.js'; import { InnertubeError, Platform } from '../../utils/Utils.js'; import type { ObservedArray, YTNode } from '../helpers.js'; diff --git a/src/platform/README.md b/src/platform/README.md index abaf085a0f..d006a5ba10 100644 --- a/src/platform/README.md +++ b/src/platform/README.md @@ -24,7 +24,6 @@ If you wish to bring YouTube.js to another platform, you will need to provide th - `sha1hash(data: string)`: Function that takes a string and returns a SHA-1 hash of it. - `uuidv4()`: Function that returns a UUIDv4 string. - `eval(code: string, env: Record)`: Function to evaluate untrusted javascript script and return the result. -- `DOMParser`: DOMParser implementation. Used for generating DASH manifests. - `fetch`: WHATWG Fetch API implementation. - `Headers`: Headers implementation. - `Request`: Request implementation. diff --git a/src/platform/deno.ts b/src/platform/deno.ts index 82a3942520..f90913eb9f 100644 --- a/src/platform/deno.ts +++ b/src/platform/deno.ts @@ -1,7 +1,6 @@ // Deno Platform Support import type { ICache } from '../types/Cache.js'; import { Platform } from '../utils/Utils.js'; -import DOMParser from './polyfills/server-dom.js'; import evaluate from './jsruntime/jinter.js'; import sha1Hash from './polyfills/web-crypto.js'; import package_json from '../../package.json' assert { type: 'json' }; @@ -95,10 +94,6 @@ Platform.load({ return crypto.randomUUID(); }, eval: evaluate, - DOMParser, - serializeDOM(document) { - return document.toString(); - }, fetch: globalThis.fetch, Request: globalThis.Request, Response: globalThis.Response, diff --git a/src/platform/lib.ts b/src/platform/lib.ts index e8fcefbd5a..8219b3ac24 100644 --- a/src/platform/lib.ts +++ b/src/platform/lib.ts @@ -3,7 +3,7 @@ import Innertube from '../Innertube.js'; export * from '../core/index.js'; export * from '../parser/index.js'; export { default as Parser } from '../parser/index.js'; -export { default as Proto } from '../proto/index.js'; +export * as Proto from '../proto/index.js'; export * as Types from '../types/index.js'; export * from '../utils/index.js'; diff --git a/src/platform/node.ts b/src/platform/node.ts index 0454b51692..ccd958f5d3 100644 --- a/src/platform/node.ts +++ b/src/platform/node.ts @@ -16,7 +16,6 @@ import path from 'path'; import os from 'os'; import fs from 'fs/promises'; import { readFileSync } from 'fs'; -import DOMParser from './polyfills/server-dom.js'; import CustomEvent from './polyfills/node-custom-event.js'; import { fileURLToPath } from 'url'; import evaluate from './jsruntime/jinter.js'; @@ -114,11 +113,7 @@ Platform.load({ uuidv4() { return crypto.randomUUID(); }, - serializeDOM(document) { - return document.toString(); - }, eval: evaluate, - DOMParser, fetch: defaultFetch as unknown as FetchFunction, Request: Request as unknown as typeof globalThis.Request, Response: Response as unknown as typeof globalThis.Response, diff --git a/src/platform/polyfills/server-dom.ts b/src/platform/polyfills/server-dom.ts deleted file mode 100644 index 9d7fbfc4ea..0000000000 --- a/src/platform/polyfills/server-dom.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { DOMParser as DOMParserImpl } from 'linkedom'; - -export default DOMParserImpl as typeof globalThis.DOMParser; \ No newline at end of file diff --git a/src/platform/web.ts b/src/platform/web.ts index a050da310a..e585666d20 100644 --- a/src/platform/web.ts +++ b/src/platform/web.ts @@ -105,10 +105,6 @@ Platform.load({ }); }, eval: evaluate, - DOMParser: globalThis.DOMParser, - serializeDOM(document) { - return new XMLSerializer().serializeToString(document); - }, fetch: globalThis.fetch, Request: globalThis.Request, Response: globalThis.Response, diff --git a/src/proto/index.ts b/src/proto/index.ts index cd8d0af1ca..6dcc3c7a3b 100644 --- a/src/proto/index.ts +++ b/src/proto/index.ts @@ -15,321 +15,317 @@ import * as NotificationPreferences from './generated/messages/youtube/Notificat import * as InnertubePayload from './generated/messages/youtube/InnertubePayload.js'; import * as Hashtag from './generated/messages/youtube/Hashtag.js'; -class Proto { - static encodeVisitorData(id: string, timestamp: number): string { - const buf = VisitorData.encodeBinary({ id, timestamp }); - return encodeURIComponent(u8ToBase64(buf).replace(/\+/g, '-').replace(/\//g, '_')); - } - - static decodeVisitorData(visitor_data: string): VisitorData.Type { - const data = VisitorData.decodeBinary(base64ToU8(decodeURIComponent(visitor_data))); - return data; - } - - static encodeChannelAnalyticsParams(channel_id: string): string { - const buf = ChannelAnalytics.encodeBinary({ - params: { - channelId: channel_id - } - }); - return encodeURIComponent(u8ToBase64(buf)); - } - - static encodeSearchFilters(filters: { - upload_date?: 'all' | 'hour' | 'today' | 'week' | 'month' | 'year', - type?: 'all' | 'video' | 'channel' | 'playlist' | 'movie', - duration?: 'all' | 'short' | 'medium' | 'long', - sort_by?: 'relevance' | 'rating' | 'upload_date' | 'view_count', - features?: ('hd' | 'subtitles' | 'creative_commons' | '3d' | 'live' | 'purchased' | '4k' | '360' | 'location' | 'hdr' | 'vr180')[] - }): string { - const upload_date = { - all: undefined, - hour: 1, - today: 2, - week: 3, - month: 4, - year: 5 - }; - - const type = { - all: undefined, - video: 1, - channel: 2, - playlist: 3, - movie: 4 - }; - - const duration = { - all: undefined, - short: 1, - long: 2, - medium: 3 - }; - - const order = { - relevance: undefined, - rating: 1, - upload_date: 2, - view_count: 3 - }; - - const features = { - hd: 'featuresHd', - subtitles: 'featuresSubtitles', - creative_commons: 'featuresCreativeCommons', - '3d': 'features3D', - live: 'featuresLive', - purchased: 'featuresPurchased', - '4k': 'features4K', - '360': 'features360', - location: 'featuresLocation', - hdr: 'featuresHdr', - vr180: 'featuresVr180' - }; +export function encodeVisitorData(id: string, timestamp: number): string { + const buf = VisitorData.encodeBinary({ id, timestamp }); + return encodeURIComponent(u8ToBase64(buf).replace(/\+/g, '-').replace(/\//g, '_')); +} - const data: SearchFilter.Type = {}; +export function decodeVisitorData(visitor_data: string): VisitorData.Type { + const data = VisitorData.decodeBinary(base64ToU8(decodeURIComponent(visitor_data))); + return data; +} - if (filters) - data.filters = {}; - else - data.noFilter = 0; +export function encodeChannelAnalyticsParams(channel_id: string): string { + const buf = ChannelAnalytics.encodeBinary({ + params: { + channelId: channel_id + } + }); + return encodeURIComponent(u8ToBase64(buf)); +} - if (data.filters) { - if (filters.upload_date) { - data.filters.uploadDate = upload_date[filters.upload_date]; - } +export function encodeSearchFilters(filters: { + upload_date?: 'all' | 'hour' | 'today' | 'week' | 'month' | 'year', + type?: 'all' | 'video' | 'channel' | 'playlist' | 'movie', + duration?: 'all' | 'short' | 'medium' | 'long', + sort_by?: 'relevance' | 'rating' | 'upload_date' | 'view_count', + features?: ('hd' | 'subtitles' | 'creative_commons' | '3d' | 'live' | 'purchased' | '4k' | '360' | 'location' | 'hdr' | 'vr180')[] +}): string { + const upload_date = { + all: undefined, + hour: 1, + today: 2, + week: 3, + month: 4, + year: 5 + }; + + const type = { + all: undefined, + video: 1, + channel: 2, + playlist: 3, + movie: 4 + }; + + const duration = { + all: undefined, + short: 1, + long: 2, + medium: 3 + }; + + const order = { + relevance: undefined, + rating: 1, + upload_date: 2, + view_count: 3 + }; + + const features = { + hd: 'featuresHd', + subtitles: 'featuresSubtitles', + creative_commons: 'featuresCreativeCommons', + '3d': 'features3D', + live: 'featuresLive', + purchased: 'featuresPurchased', + '4k': 'features4K', + '360': 'features360', + location: 'featuresLocation', + hdr: 'featuresHdr', + vr180: 'featuresVr180' + }; + + const data: SearchFilter.Type = {}; + + if (filters) + data.filters = {}; + else + data.noFilter = 0; + + if (data.filters) { + if (filters.upload_date) { + data.filters.uploadDate = upload_date[filters.upload_date]; + } - if (filters.type) { - data.filters.type = type[filters.type]; - } + if (filters.type) { + data.filters.type = type[filters.type]; + } - if (filters.duration) { - data.filters.duration = duration[filters.duration]; - } + if (filters.duration) { + data.filters.duration = duration[filters.duration]; + } - if (filters.sort_by && filters.sort_by !== 'relevance') { - data.sortBy = order[filters.sort_by]; - } + if (filters.sort_by && filters.sort_by !== 'relevance') { + data.sortBy = order[filters.sort_by]; + } - if (filters.features) { - for (const feature of filters.features) { - data.filters[features[feature] as keyof SearchFilter_Filters.Type] = 1; - } + if (filters.features) { + for (const feature of filters.features) { + data.filters[features[feature] as keyof SearchFilter_Filters.Type] = 1; } } - - const buf = SearchFilter.encodeBinary(data); - return encodeURIComponent(u8ToBase64(buf)); } - static encodeMusicSearchFilters(filters: { - type?: 'all' | 'song' | 'video' | 'album' | 'playlist' | 'artist' - }): string { - const data: MusicSearchFilter.Type = { - filters: { - type: {} - } - }; + const buf = SearchFilter.encodeBinary(data); + return encodeURIComponent(u8ToBase64(buf)); +} - // TODO: See protobuf definition (protoc doesn't allow zero index: optional int32 all = 0;) - if (filters.type && filters.type !== 'all' && data.filters?.type) - data.filters.type[filters.type] = 1; +export function encodeMusicSearchFilters(filters: { + type?: 'all' | 'song' | 'video' | 'album' | 'playlist' | 'artist' +}): string { + const data: MusicSearchFilter.Type = { + filters: { + type: {} + } + }; - const buf = MusicSearchFilter.encodeBinary(data); - return encodeURIComponent(u8ToBase64(buf)); - } + // TODO: See protobuf definition (protoc doesn't allow zero index: optional int32 all = 0;) + if (filters.type && filters.type !== 'all' && data.filters?.type) + data.filters.type[filters.type] = 1; - static encodeMessageParams(channel_id: string, video_id: string): string { - const buf = LiveMessageParams.encodeBinary({ - params: { - ids: { - channelId: channel_id, videoId: video_id - } - }, - number0: 1, number1: 4 - }); + const buf = MusicSearchFilter.encodeBinary(data); + return encodeURIComponent(u8ToBase64(buf)); +} - return btoa(encodeURIComponent(u8ToBase64(buf))); - } +export function encodeMessageParams(channel_id: string, video_id: string): string { + const buf = LiveMessageParams.encodeBinary({ + params: { + ids: { + channelId: channel_id, videoId: video_id + } + }, + number0: 1, number1: 4 + }); - static encodeCommentsSectionParams(video_id: string, options: { - type?: number, - sort_by?: 'TOP_COMMENTS' | 'NEWEST_FIRST' - } = {}): string { - const sort_options = { - TOP_COMMENTS: 0, - NEWEST_FIRST: 1 - }; + return btoa(encodeURIComponent(u8ToBase64(buf))); +} - const buf = GetCommentsSectionParams.encodeBinary({ - ctx: { - videoId: video_id +export function encodeCommentsSectionParams(video_id: string, options: { + type?: number, + sort_by?: 'TOP_COMMENTS' | 'NEWEST_FIRST' +} = {}): string { + const sort_options = { + TOP_COMMENTS: 0, + NEWEST_FIRST: 1 + }; + + const buf = GetCommentsSectionParams.encodeBinary({ + ctx: { + videoId: video_id + }, + unkParam: 6, + params: { + opts: { + videoId: video_id, + sortBy: sort_options[options.sort_by || 'TOP_COMMENTS'], + type: options.type || 2 }, - unkParam: 6, - params: { - opts: { - videoId: video_id, - sortBy: sort_options[options.sort_by || 'TOP_COMMENTS'], - type: options.type || 2 - }, - target: 'comments-section' - } - }); + target: 'comments-section' + } + }); - return encodeURIComponent(u8ToBase64(buf)); - } + return encodeURIComponent(u8ToBase64(buf)); +} - static encodeCommentParams(video_id: string): string { - const buf = CreateCommentParams.encodeBinary({ - videoId: video_id, +export function encodeCommentParams(video_id: string): string { + const buf = CreateCommentParams.encodeBinary({ + videoId: video_id, + params: { + index: 0 + }, + number: 7 + }); + return encodeURIComponent(u8ToBase64(buf)); +} + +export function encodeCommentActionParams(type: number, args: { + comment_id?: string, + video_id?: string, + text?: string, + target_language?: string +} = {}): string { + const data: PeformCommentActionParams.Type = { + type, + commentId: args.comment_id || ' ', + videoId: args.video_id || ' ', + channelId: ' ', + unkNum: 2 + }; + + if (args.hasOwnProperty('text')) { + if (typeof args.target_language !== 'string') + throw new Error('target_language must be a string'); + args.comment_id && (delete data.unkNum); + data.translateCommentParams = { params: { - index: 0 + comment: { + text: args.text as string + } }, - number: 7 - }); - return encodeURIComponent(u8ToBase64(buf)); - } - - static encodeCommentActionParams(type: number, args: { - comment_id?: string, - video_id?: string, - text?: string, - target_language?: string - } = {}): string { - const data: PeformCommentActionParams.Type = { - type, commentId: args.comment_id || ' ', - videoId: args.video_id || ' ', - channelId: ' ', - unkNum: 2 + targetLanguage: args.target_language }; - - if (args.hasOwnProperty('text')) { - if (typeof args.target_language !== 'string') - throw new Error('target_language must be a string'); - args.comment_id && (delete data.unkNum); - data.translateCommentParams = { - params: { - comment: { - text: args.text as string - } - }, - commentId: args.comment_id || ' ', - targetLanguage: args.target_language - }; - } - - const buf = PeformCommentActionParams.encodeBinary(data); - return encodeURIComponent(u8ToBase64(buf)); } - static encodeNotificationPref(channel_id: string, index: number): string { - const buf = NotificationPreferences.encodeBinary({ - channelId: channel_id, - prefId: { - index - }, - number0: 0, number1: 4 - }); + const buf = PeformCommentActionParams.encodeBinary(data); + return encodeURIComponent(u8ToBase64(buf)); +} - return encodeURIComponent(u8ToBase64(buf)); - } +export function encodeNotificationPref(channel_id: string, index: number): string { + const buf = NotificationPreferences.encodeBinary({ + channelId: channel_id, + prefId: { + index + }, + number0: 0, number1: 4 + }); - static encodeVideoMetadataPayload(video_id: string, metadata: UpdateVideoMetadataOptions): Uint8Array { - const data: InnertubePayload.Type = { - context: { - client: { - unkparam: 14, - clientName: CLIENTS.ANDROID.NAME, - clientVersion: CLIENTS.YTSTUDIO_ANDROID.VERSION - } - }, - target: video_id - }; + return encodeURIComponent(u8ToBase64(buf)); +} - if (Reflect.has(metadata, 'title')) - data.title = { text: metadata.title || '' }; - - if (Reflect.has(metadata, 'description')) - data.description = { text: metadata.description || '' }; - - if (Reflect.has(metadata, 'license')) - data.license = { type: metadata.license || '' }; - - if (Reflect.has(metadata, 'tags')) - data.tags = { list: metadata.tags || [] }; - - if (Reflect.has(metadata, 'category')) - data.category = { id: metadata.category || 0 }; - - if (Reflect.has(metadata, 'privacy')) { - switch (metadata.privacy) { - case 'PUBLIC': - data.privacy = { type: 1 }; - break; - case 'UNLISTED': - data.privacy = { type: 2 }; - break; - case 'PRIVATE': - data.privacy = { type: 3 }; - break; - default: - throw new Error('Invalid visibility option'); +export function encodeVideoMetadataPayload(video_id: string, metadata: UpdateVideoMetadataOptions): Uint8Array { + const data: InnertubePayload.Type = { + context: { + client: { + unkparam: 14, + clientName: CLIENTS.ANDROID.NAME, + clientVersion: CLIENTS.YTSTUDIO_ANDROID.VERSION } + }, + target: video_id + }; + + if (Reflect.has(metadata, 'title')) + data.title = { text: metadata.title || '' }; + + if (Reflect.has(metadata, 'description')) + data.description = { text: metadata.description || '' }; + + if (Reflect.has(metadata, 'license')) + data.license = { type: metadata.license || '' }; + + if (Reflect.has(metadata, 'tags')) + data.tags = { list: metadata.tags || [] }; + + if (Reflect.has(metadata, 'category')) + data.category = { id: metadata.category || 0 }; + + if (Reflect.has(metadata, 'privacy')) { + switch (metadata.privacy) { + case 'PUBLIC': + data.privacy = { type: 1 }; + break; + case 'UNLISTED': + data.privacy = { type: 2 }; + break; + case 'PRIVATE': + data.privacy = { type: 3 }; + break; + default: + throw new Error('Invalid visibility option'); } + } - if (Reflect.has(metadata, 'made_for_kids')) { - data.madeForKids = { - unkparam: 1, - choice: metadata.made_for_kids ? 1 : 2 - }; - } - - if (Reflect.has(metadata, 'age_restricted')) { - data.ageRestricted = { - unkparam: 1, - choice: metadata.age_restricted ? 1 : 2 - }; - } - - const buf = InnertubePayload.encodeBinary(data); - - return buf; + if (Reflect.has(metadata, 'made_for_kids')) { + data.madeForKids = { + unkparam: 1, + choice: metadata.made_for_kids ? 1 : 2 + }; } - static encodeCustomThumbnailPayload(video_id: string, bytes: Uint8Array): Uint8Array { - const data: InnertubePayload.Type = { - context: { - client: { - unkparam: 14, - clientName: CLIENTS.ANDROID.NAME, - clientVersion: CLIENTS.YTSTUDIO_ANDROID.VERSION - } - }, - target: video_id, - videoThumbnail: { - type: 3, - thumbnail: { - imageData: bytes - } - } + if (Reflect.has(metadata, 'age_restricted')) { + data.ageRestricted = { + unkparam: 1, + choice: metadata.age_restricted ? 1 : 2 }; + } - const buf = InnertubePayload.encodeBinary(data); + const buf = InnertubePayload.encodeBinary(data); - return buf; - } + return buf; +} - static encodeHashtag(hashtag: string): string { - const buf = Hashtag.encodeBinary({ - params: { - hashtag, - type: 1 +export function encodeCustomThumbnailPayload(video_id: string, bytes: Uint8Array): Uint8Array { + const data: InnertubePayload.Type = { + context: { + client: { + unkparam: 14, + clientName: CLIENTS.ANDROID.NAME, + clientVersion: CLIENTS.YTSTUDIO_ANDROID.VERSION + } + }, + target: video_id, + videoThumbnail: { + type: 3, + thumbnail: { + imageData: bytes } - }); + } + }; - return encodeURIComponent(u8ToBase64(buf)); - } + const buf = InnertubePayload.encodeBinary(data); + + return buf; } -export default Proto; \ No newline at end of file +export function encodeHashtag(hashtag: string): string { + const buf = Hashtag.encodeBinary({ + params: { + hashtag, + type: 1 + } + }); + + return encodeURIComponent(u8ToBase64(buf)); +} \ No newline at end of file diff --git a/src/types/FormatUtils.ts b/src/types/FormatUtils.ts new file mode 100644 index 0000000000..aaad72e9a9 --- /dev/null +++ b/src/types/FormatUtils.ts @@ -0,0 +1,37 @@ +import type { Format } from '../parser/misc.js'; + +export type URLTransformer = (url: URL) => URL; +export type FormatFilter = (format: Format) => boolean; + +export interface FormatOptions { + /** + * Video quality; 360p, 720p, 1080p, etc... also accepts 'best' and 'bestefficiency'. + */ + quality?: string; + /** + * Download type, can be: video, audio or video+audio + */ + type?: 'video' | 'audio' | 'video+audio'; + /** + * Language code, defaults to 'original'. + */ + language?: string; + /** + * File format, use 'any' to download any format + */ + format?: string; + /** + * InnerTube client, can be ANDROID, WEB, YTMUSIC, YTMUSIC_ANDROID, YTSTUDIO_ANDROID or TV_EMBEDDED + */ + client?: 'WEB' | 'ANDROID' | 'YTMUSIC_ANDROID' | 'YTMUSIC' | 'YTSTUDIO_ANDROID' | 'TV_EMBEDDED'; +} + +export interface DownloadOptions extends FormatOptions { + /** + * Download range, indicates which bytes should be downloaded. + */ + range?: { + start: number; + end: number; + } +} \ No newline at end of file diff --git a/src/types/PlatformShim.ts b/src/types/PlatformShim.ts index 19f7b91d5e..38e6f9b989 100644 --- a/src/types/PlatformShim.ts +++ b/src/types/PlatformShim.ts @@ -18,8 +18,6 @@ interface PlatformShim { sha1Hash(data: string): Promise; uuidv4(): string; eval(code: string, env: Record): unknown; - DOMParser: typeof globalThis.DOMParser; - serializeDOM: (document: Document) => string; fetch: FetchFunction; Request: typeof Request; Response: typeof Response; diff --git a/src/types/index.ts b/src/types/index.ts index 63ea16ca70..47c9fe86c5 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -3,4 +3,5 @@ export type { default as PlatformShim } from './PlatformShim.js'; export * from './Cache.js'; export * from './PlatformShim.js'; export * from './Clients.js'; -export * from './Endpoints.js'; \ No newline at end of file +export * from './Endpoints.js'; +export * from './FormatUtils.js'; \ No newline at end of file diff --git a/src/utils/DashManifest.tsx b/src/utils/DashManifest.tsx new file mode 100644 index 0000000000..7062ec06fb --- /dev/null +++ b/src/utils/DashManifest.tsx @@ -0,0 +1,248 @@ +/** @jsxFactory DashUtils.createElement */ +/** @jsxFragmentFactory DashUtils.Fragment */ +import type Actions from '../core/Actions.js'; +import type Player from '../core/Player.js'; +import type { IStreamingData } from '../parser/index.js'; +import type { PlayerStoryboardSpec } from '../parser/nodes.js'; +import * as DashUtils from './DashUtils.js'; +import type { SegmentInfo as FSegmentInfo } from './StreamingInfo.js'; +import { getStreamingInfo } from './StreamingInfo.js'; +import type { FormatFilter, URLTransformer } from '../types/FormatUtils.js'; +import { InnertubeError } from './Utils.js'; + +interface DashManifestProps { + streamingData: IStreamingData; + transformURL?: URLTransformer; + rejectFormat?: FormatFilter; + cpn?: string; + player?: Player; + actions?: Actions; + storyboards?: PlayerStoryboardSpec; +} + +async function OTFSegmentInfo({ info }: { info: FSegmentInfo }) { + if (!info.is_oft) return null; + + const template = await info.getSegmentTemplate(); + + return + + { + template.timeline.map((segment_duration) => ( + + )) + } + + ; +} + +function SegmentInfo({ info }: { info: FSegmentInfo }) { + if (info.is_oft) { + return ; + } + return <> + + {info.base_url} + + + + + ; +} + +function DashManifest({ + streamingData, + transformURL, + rejectFormat, + cpn, + player, + actions, + storyboards +}: DashManifestProps) { + const { + duration, + audio_sets, + video_sets, + image_sets + } = getStreamingInfo(streamingData, transformURL, rejectFormat, cpn, player, actions, storyboards); + + // XXX: DASH spec: https://standards.iso.org/ittf/PubliclyAvailableStandards/c083314_ISO_IEC%2023009-1_2022(en).zip + + return + + { + audio_sets.map((set, index) => ( + + { + set.track_role && + + } + { + set.track_name && + + } + { + set.channels && + + } + { + set.representations.map((rep) => ( + + { + rep.channels && + + } + + + )) + } + + )) + } + { + video_sets.map((set, index) => ( + + { + set.color_info.primaries && + + } + { + set.color_info.transfer_characteristics && + + } + { + set.color_info.matrix_coefficients && + + } + { + set.representations.map((rep) => ( + + + + )) + } + + )) + } + { + image_sets.map(async (set, index) => { + return + { + set.representations.map(async (rep) => ( + + + + + )) + } + ; + }) + } + + ; +} + +export function toDash( + streaming_data?: IStreamingData, + url_transformer: URLTransformer = (url) => url, + format_filter?: FormatFilter, + cpn?: string, + player?: Player, + actions?: Actions, + storyboards?: PlayerStoryboardSpec +) { + if (!streaming_data) + throw new InnertubeError('Streaming data not available'); + + return DashUtils.renderToString( + + ); +} diff --git a/src/utils/DashUtils.ts b/src/utils/DashUtils.ts new file mode 100644 index 0000000000..869b2ba0bb --- /dev/null +++ b/src/utils/DashUtils.ts @@ -0,0 +1,104 @@ +/* eslint-disable @typescript-eslint/no-namespace */ +declare global { + namespace JSX { + interface IntrinsicElements { + [key: string]: DashProps; + } + } +} + +export type DashChild = (DashNode | (DashNode | Promise) | Promise); +export interface DashProps { + [key: string]: unknown, + children?: DashChild[] +} + +export interface DashNode { + type: string, + props: DashProps, +} + +const XML_CHARACTER_MAP = { + '&': '&', + '"': '"', + '\'': ''', + '<': '<', + '>': '>' +} as const; + +function escapeXMLString(str: string) { + return str.replace(/([&"<>'])/g, (_, item: keyof typeof XML_CHARACTER_MAP) => { + return XML_CHARACTER_MAP[item]; + }); +} + +function normalizeTag(tag: string) { + if (tag === 'mpd') return 'MPD'; + if (tag === 'base-url') return 'BaseURL'; + + const sections = tag.split('-'); + return sections.map((section) => section.charAt(0).toUpperCase() + section.slice(1)).join(''); +} + +export function createElement( + tagNameOrFunction: string | ((props: DashProps) => DashNode | Promise), + props: { [key: string] : unknown } | null | undefined, + ...children: DashChild[] +): DashNode | Promise { + const normalizedChildren = children.flat().map((child) => typeof child === 'string' ? createTextElement(child) : child); + + if (typeof tagNameOrFunction === 'function') { + return tagNameOrFunction({ ...props, children: normalizedChildren }); + } + + return { + type: normalizeTag(tagNameOrFunction), + props: { + ...props, + children: normalizedChildren + } + }; +} + +export function createTextElement(text: string): DashNode { + return { + type: 'TEXT_ELEMENT', + props: { nodeValue: text } + }; +} + +export async function renderElementToString(element: DashNode): Promise { + if (element.type === 'TEXT_ELEMENT') + return escapeXMLString(typeof element.props.nodeValue === 'string' ? element.props.nodeValue : ''); + + let dom = `<${element.type}`; + + if (element.props) { + const properties = Object.keys(element.props) + .filter((key) => ![ 'children', 'nodeValue' ].includes(key) && element.props[key] !== undefined) + .map((name) => `${name}="${escapeXMLString(`${element.props[name]}`)}"`); + + if (properties.length > 0) + dom += ` ${properties.join(' ')}`; + } + + if (element.props.children) { + const children = await Promise.all((await Promise.all(element.props.children.flat())).flat().filter((child) => !!child).map((child) => renderElementToString(child))); + if (children.length > 0) { + dom += `>${children.join('')}`; + return dom; + } + } + + return `${dom}/>`; +} + +export async function renderToString(root: DashNode | Promise) { + const dom = await renderElementToString(await root); + + return `${dom}`; +} + +export function Fragment(props: DashProps) { + return props.children; +} diff --git a/src/utils/FormatUtils.ts b/src/utils/FormatUtils.ts index 2ebba9093f..b674af8c7f 100644 --- a/src/utils/FormatUtils.ts +++ b/src/utils/FormatUtils.ts @@ -2,857 +2,193 @@ import type Player from '../core/Player.js'; import type Actions from '../core/Actions.js'; import type Format from '../parser/classes/misc/Format.js'; -import type AudioOnlyPlayability from '../parser/classes/AudioOnlyPlayability.js'; -import type PlayerStoryboardSpec from '../parser/classes/PlayerStoryboardSpec.js'; -import type { YTNode } from '../parser/helpers.js'; import * as Constants from './Constants.js'; -import { getStringBetweenStrings, InnertubeError, Platform, streamToIterable } from './Utils.js'; - -export type URLTransformer = (url: URL) => URL; -export type FormatFilter = (format: Format) => boolean; - -export interface FormatOptions { - /** - * Video quality; 360p, 720p, 1080p, etc... also accepts 'best' and 'bestefficiency'. - */ - quality?: string; - /** - * Download type, can be: video, audio or video+audio - */ - type?: 'video' | 'audio' | 'video+audio'; - /** - * Language code, defaults to 'original'. - */ - language?: string; - /** - * File format, use 'any' to download any format - */ - format?: string; - /** - * InnerTube client, can be ANDROID, WEB, YTMUSIC, YTMUSIC_ANDROID, YTSTUDIO_ANDROID or TV_EMBEDDED - */ - client?: 'WEB' | 'ANDROID' | 'YTMUSIC_ANDROID' | 'YTMUSIC' | 'YTSTUDIO_ANDROID' | 'TV_EMBEDDED'; -} - -export interface DownloadOptions extends FormatOptions { - /** - * Download range, indicates which bytes should be downloaded. - */ - range?: { - start: number; - end: number; - } -} - -class FormatUtils { - static async download(options: DownloadOptions, actions: Actions, playability_status?: { - status: string; - error_screen: YTNode | null; - audio_only_playablility: AudioOnlyPlayability | null; - embeddable: boolean; - reason: any; - }, streaming_data?: { - expires: Date; - formats: Format[]; - adaptive_formats: Format[]; - dash_manifest_url: string | null; - hls_manifest_url: string | null; - }, player?: Player, cpn?: string): Promise> { - if (playability_status?.status === 'UNPLAYABLE') - throw new InnertubeError('Video is unplayable', { error_type: 'UNPLAYABLE' }); - if (playability_status?.status === 'LOGIN_REQUIRED') - throw new InnertubeError('Video is login required', { error_type: 'LOGIN_REQUIRED' }); - if (!streaming_data) - throw new InnertubeError('Streaming data not available.', { error_type: 'NO_STREAMING_DATA' }); - - const opts: DownloadOptions = { - quality: '360p', - type: 'video+audio', - format: 'mp4', - range: undefined, - ...options - }; - - const format = this.chooseFormat(opts, streaming_data); - const format_url = format.decipher(player); - - // If we're not downloading the video in chunks, we just use fetch once. - if (opts.type === 'video+audio' && !options.range) { - const response = await actions.session.http.fetch_function(`${format_url}&cpn=${cpn}`, { - method: 'GET', - headers: Constants.STREAM_HEADERS, - redirect: 'follow' - }); - - // Throw if the response is not 2xx - if (!response.ok) - throw new InnertubeError('The server responded with a non 2xx status code', { error_type: 'FETCH_FAILED', response }); - - const body = response.body; - - if (!body) - throw new InnertubeError('Could not get ReadableStream from fetch Response.', { error_type: 'FETCH_FAILED', response }); - - return body; - } - - // We need to download in chunks. - - const chunk_size = 1048576 * 10; // 10MB - - let chunk_start = (options.range ? options.range.start : 0); - let chunk_end = (options.range ? options.range.end : chunk_size); - let must_end = false; - - let cancel: AbortController; - - const readable_stream = new Platform.shim.ReadableStream({ - // eslint-disable-next-line @typescript-eslint/no-empty-function - start() { }, - pull: async (controller) => { - if (must_end) { - controller.close(); - return; - } - - if ((chunk_end >= (format.content_length ? format.content_length : 0)) || options.range) { - must_end = true; - } - - return new Promise(async (resolve, reject) => { - try { - cancel = new AbortController(); - - const response = await actions.session.http.fetch_function(`${format_url}&cpn=${cpn}&range=${chunk_start}-${chunk_end || ''}`, { - method: 'GET', - headers: { - ...Constants.STREAM_HEADERS - // XXX: use YouTube's range parameter instead of a Range header. - // Range: `bytes=${chunk_start}-${chunk_end}` - }, - signal: cancel.signal - }); - - const body = response.body; - - if (!body) - throw new InnertubeError('Could not get ReadableStream from fetch Response.', { video: this, error_type: 'FETCH_FAILED', response }); - - for await (const chunk of streamToIterable(body)) { - controller.enqueue(chunk); - } - - chunk_start = chunk_end + 1; - chunk_end += chunk_size; - - resolve(); - - } catch (e: any) { - reject(e); - } - }); - }, - async cancel(reason) { - cancel.abort(reason); - } - }, { - highWaterMark: 1, // TODO: better value? - size(chunk) { - return chunk.byteLength; - } - }); - - return readable_stream; - } - - /** - * Selects the format that best matches the given options. - * @param options - Options - * @param streaming_data - Streaming data - */ - static chooseFormat(options: FormatOptions, streaming_data?: { - expires: Date; - formats: Format[]; - adaptive_formats: Format[]; - dash_manifest_url: string | null; - hls_manifest_url: string | null; - }): Format { - if (!streaming_data) - throw new InnertubeError('Streaming data not available'); - - const formats = [ - ...(streaming_data.formats || []), - ...(streaming_data.adaptive_formats || []) - ]; - - const requires_audio = options.type ? options.type.includes('audio') : true; - const requires_video = options.type ? options.type.includes('video') : true; - const language = options.language || 'original'; - const quality = options.quality || 'best'; - - let best_width = -1; - - const is_best = [ 'best', 'bestefficiency' ].includes(quality); - const use_most_efficient = quality !== 'best'; - - let candidates = formats.filter((format) => { - if (requires_audio && !format.has_audio) - return false; - if (requires_video && !format.has_video) - return false; - if (options.format !== 'any' && !format.mime_type.includes(options.format || 'mp4')) - return false; - if (!is_best && format.quality_label !== quality) - return false; - if (best_width < format.width) - best_width = format.width; - return true; +import { InnertubeError, Platform, streamToIterable } from './Utils.js'; +import type { IPlayabilityStatus, IStreamingData } from '../parser/index.js'; +import type { DownloadOptions, FormatOptions } from '../types/FormatUtils.js'; + +export async function download( + options: DownloadOptions, + actions: Actions, + playability_status?: IPlayabilityStatus, + streaming_data?: IStreamingData, + player?: Player, + cpn?: string +): Promise> { + if (playability_status?.status === 'UNPLAYABLE') + throw new InnertubeError('Video is unplayable', { error_type: 'UNPLAYABLE' }); + if (playability_status?.status === 'LOGIN_REQUIRED') + throw new InnertubeError('Video is login required', { error_type: 'LOGIN_REQUIRED' }); + if (!streaming_data) + throw new InnertubeError('Streaming data not available.', { error_type: 'NO_STREAMING_DATA' }); + + const opts: DownloadOptions = { + quality: '360p', + type: 'video+audio', + format: 'mp4', + range: undefined, + ...options + }; + + const format = chooseFormat(opts, streaming_data); + const format_url = format.decipher(player); + + // If we're not downloading the video in chunks, we just use fetch once. + if (opts.type === 'video+audio' && !options.range) { + const response = await actions.session.http.fetch_function(`${format_url}&cpn=${cpn}`, { + method: 'GET', + headers: Constants.STREAM_HEADERS, + redirect: 'follow' }); - if (!candidates.length) - throw new InnertubeError('No matching formats found', { options }); + // Throw if the response is not 2xx + if (!response.ok) + throw new InnertubeError('The server responded with a non 2xx status code', { error_type: 'FETCH_FAILED', response }); - if (is_best && requires_video) - candidates = candidates.filter((format) => format.width === best_width); - - if (requires_audio && !requires_video) { - const audio_only = candidates.filter((format) => { - if (language !== 'original') { - return !format.has_video && format.language === language; - } - return !format.has_video && format.is_original; - - }); - if (audio_only.length > 0) { - candidates = audio_only; - } - } + const body = response.body; - if (use_most_efficient) { - // Sort by bitrate (lower is better) - candidates.sort((a, b) => a.bitrate - b.bitrate); - } else { - // Sort by bitrate (higher is better) - candidates.sort((a, b) => b.bitrate - a.bitrate); - } + if (!body) + throw new InnertubeError('Could not get ReadableStream from fetch Response.', { error_type: 'FETCH_FAILED', response }); - return candidates[0]; + return body; } - static async toDash(streaming_data?: { - expires: Date; - formats: Format[]; - adaptive_formats: Format[]; - dash_manifest_url: string | null; - hls_manifest_url: string | null; - }, url_transformer: URLTransformer = (url) => url, format_filter?: FormatFilter, cpn?: string, player?: Player, actions?: Actions, storyboards?: PlayerStoryboardSpec): Promise { - if (!streaming_data) - throw new InnertubeError('Streaming data not available'); - - let adaptive_formats; - - if (format_filter) { - adaptive_formats = streaming_data.adaptive_formats.filter((fmt: Format) => !(format_filter(fmt))); - } else { - adaptive_formats = streaming_data.adaptive_formats; - } - - if (!adaptive_formats.length) - throw new InnertubeError('No adaptive formats found'); - - const length = adaptive_formats[0].approx_duration_ms / 1000; - - // DASH spec: https://standards.iso.org/ittf/PubliclyAvailableStandards/c083314_ISO_IEC%2023009-1_2022(en).zip + // We need to download in chunks. - const document = new Platform.shim.DOMParser().parseFromString('', 'application/xml'); - const mpd = document.querySelector('MPD') as HTMLElement; - const period = document.createElement('Period'); + const chunk_size = 1048576 * 10; // 10MB - mpd.replaceWith(this.#el(document, 'MPD', { - xmlns: 'urn:mpeg:dash:schema:mpd:2011', - minBufferTime: 'PT1.500S', - profiles: 'urn:mpeg:dash:profile:isoff-main:2011', - type: 'static', - mediaPresentationDuration: `PT${length}S`, - 'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance', - 'xsi:schemaLocation': 'urn:mpeg:dash:schema:mpd:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd' - }, [ - period - ])); + let chunk_start = (options.range ? options.range.start : 0); + let chunk_end = (options.range ? options.range.end : chunk_size); + let must_end = false; - await this.#generateAdaptationSet(document, period, adaptive_formats, url_transformer, cpn, player, actions, storyboards); + let cancel: AbortController; - return Platform.shim.serializeDOM(document); - } - - static #el(document: XMLDocument, tag: string, attrs: Record, children: Node[] = []) { - const el = document.createElement(tag); - for (const [ key, value ] of Object.entries(attrs)) { - value && el.setAttribute(key, value); - } - for (const child of children) { - if (typeof child === 'undefined') continue; - el.appendChild(child); - } - return el; - } - - static async #generateAdaptationSet( - document: XMLDocument, - period: Element, - formats: Format[], - url_transformer: URLTransformer, - cpn?: string, - player?: Player, - actions?: Actions, - storyboards?: PlayerStoryboardSpec - ) { - const group_ids: string[] = []; - const group_objects: Format[][] = [ [] ]; - - let has_multiple_audio_tracks = false; - - formats.forEach((format) => { - if ((!format.index_range || !format.init_range) && !format.is_type_otf) { + const readable_stream = new Platform.shim.ReadableStream({ + // eslint-disable-next-line @typescript-eslint/no-empty-function + start() { }, + pull: async (controller) => { + if (must_end) { + controller.close(); return; } - const mime_type = format.mime_type.split(';')[0]; - - // Codec without any profile or level information - const just_codec = getStringBetweenStrings(format.mime_type, 'codecs="', '"')?.split('.')[0]; - - let color_info = ''; - // HDR videos have both SDR and HDR vp9 formats, so we want to stick them in different groups - if (format.color_info) { - const { primaries, transfer_characteristics, matrix_coefficients } = format.color_info; - color_info = `${primaries}-${transfer_characteristics}-${matrix_coefficients}`; - } - - let audio_track_id = ''; - if (format.audio_track) { - audio_track_id = format.audio_track.id; - - if (!has_multiple_audio_tracks) { - has_multiple_audio_tracks = true; - } - } - - const group_id = `${mime_type}-${just_codec}-${color_info}-${audio_track_id}`; - - const group_index = group_ids.indexOf(group_id); - if (group_index > -1) { - group_objects[group_index].push(format); - } else { - group_ids.push(group_id); - group_objects.push([]); - group_objects[group_ids.length - 1].push(format); - } - }); - - - let set_id = 0; - - for (let i = 0; i < group_ids.length; i++) { - const group_formats = group_objects[i]; - const first_format = group_formats[0]; - - if (has_multiple_audio_tracks && first_format.has_audio && !first_format.audio_track) { - // Some videos with multiple audio tracks, have a broken one, that doesn't have any audio track information - // It seems to be the same as default audio track but broken - // We want to ignore it, as it messes up audio track selection in players and YouTube ignores it too - // At the time of writing, this video has a broken audio track: https://youtu.be/UJeSWbR6W04 - - continue; + if ((chunk_end >= (format.content_length ? format.content_length : 0)) || options.range) { + must_end = true; } - const set = this.#el(document, 'AdaptationSet', { - id: `${set_id}`, - mimeType: first_format.mime_type.split(';')[0], - startWithSAP: '1', - subsegmentAlignment: 'true' - }); - - const hoisted: string[] = []; - - this.#hoistCodecsIfPossible(set, group_formats, hoisted); - + return new Promise(async (resolve, reject) => { + try { + cancel = new AbortController(); - if (first_format.has_audio) { - this.#hoistNumberAttributeIfPossible(set, group_formats, 'audioSamplingRate', 'audio_sample_rate', hoisted); - - this.#hoistAudioChannelsIfPossible(document, set, group_formats, hoisted); - - const language = first_format.language; - if (language) { - set.setAttribute('lang', language); - } - - const audio_track = first_format.audio_track; - if (audio_track) { - let role; - if (audio_track.audio_is_default) { - role = 'main'; - } else if (first_format.is_dubbed) { - role = 'dub'; - } else if (first_format.is_descriptive) { - role = 'description'; - } else { - role = 'alternate'; - } - - set.appendChild(this.#el(document, 'Role', { - schemeIdUri: 'urn:mpeg:dash:role:2011', - value: role - })); - - set.appendChild( - this.#el(document, 'Label', { - id: set_id.toString() - }, [ - document.createTextNode(audio_track.display_name as string) - ]) - ); - } - - for (const format of group_formats) { - await this.#generateRepresentationAudio(document, set, format, url_transformer, hoisted, cpn, player, actions); - } - } else { - set.setAttribute('maxPlayoutRate', '1'); - - this.#hoistNumberAttributeIfPossible(set, group_formats, 'frameRate', 'fps', hoisted); - - const color_info = first_format.color_info; - if (color_info) { - // Section 5.5 Video source metadata signalling https://dashif.org/docs/IOP-Guidelines/DASH-IF-IOP-Part7-v5.0.0.pdf - // Section 8 Video code points https://www.itu.int/rec/T-REC-H.273-202107-I/en - // The player.js file was also helpful - - if (color_info.primaries) { - let primaries = ''; - - switch (color_info.primaries) { - case 'BT709': - primaries = '1'; - break; - case 'BT2020': - primaries = '9'; - break; - } - - if (primaries !== '') { - set.appendChild(this.#el(document, 'EssentialProperty', { - schemeIdUri: 'urn:mpeg:mpegB:cicp:ColourPrimaries', - value: primaries - })); - } - } + const response = await actions.session.http.fetch_function(`${format_url}&cpn=${cpn}&range=${chunk_start}-${chunk_end || ''}`, { + method: 'GET', + headers: { + ...Constants.STREAM_HEADERS + // XXX: use YouTube's range parameter instead of a Range header. + // Range: `bytes=${chunk_start}-${chunk_end}` + }, + signal: cancel.signal + }); - if (color_info.transfer_characteristics) { - let transfer_characteristics = ''; - - switch (color_info.transfer_characteristics) { - case 'BT709': - transfer_characteristics = '1'; - break; - case 'BT2020_10': - transfer_characteristics = '14'; - break; - case 'SMPTEST2084': - transfer_characteristics = '16'; - break; - case 'ARIB_STD_B67': - transfer_characteristics = '18'; - break; - } - - if (transfer_characteristics !== '') { - set.appendChild(this.#el(document, 'EssentialProperty', { - schemeIdUri: 'urn:mpeg:mpegB:cicp:TransferCharacteristics', - value: transfer_characteristics - })); - } - } + const body = response.body; + if (!body) + throw new InnertubeError('Could not get ReadableStream from fetch Response.', { error_type: 'FETCH_FAILED', response }); - if (color_info.matrix_coefficients) { - let matrix_coefficients = ''; - - // This list is incomplete, as the player.js doesn't currently have any code for matrix coefficients, - // So it doesn't have a list like with the other two, so this is just based on what we've seen in responses - switch (color_info.matrix_coefficients) { - case 'BT709': - matrix_coefficients = '1'; - break; - case 'BT2020_NCL': - matrix_coefficients = '14'; - break; - default: { - const url = new URL(first_format.url as string); - - const anonymisedFormat = JSON.parse(JSON.stringify(first_format)); - anonymisedFormat.url = 'REDACTED'; - anonymisedFormat.signature_cipher = 'REDACTED'; - anonymisedFormat.cipher = 'REDACTED'; - - console.warn(`YouTube.js toDash(): Unknown matrix coefficients "${color_info.matrix_coefficients}", the DASH manifest is still usuable without this.\n` - + `Please report it at ${Platform.shim.info.bugs_url} so we can add support for it.\n` - + `Innertube client: ${url.searchParams.get('c')}\nformat:`, anonymisedFormat); - } - } - - if (matrix_coefficients !== '') { - set.appendChild(this.#el(document, 'EssentialProperty', { - schemeIdUri: 'urn:mpeg:mpegB:cicp:MatrixCoefficients', - value: matrix_coefficients - })); - } + for await (const chunk of streamToIterable(body)) { + controller.enqueue(chunk); } - } - for (const format of group_formats) { - await this.#generateRepresentationVideo(document, set, format, url_transformer, hoisted, cpn, player, actions); - } - } + chunk_start = chunk_end + 1; + chunk_end += chunk_size; - set_id++; - period.appendChild(set); - } + resolve(); - // We need to make requests to get the image sizes, so we'll skip the storyboards if we don't have an Actions instance - if (storyboards && actions) { - const mime_types: string[] = []; - const mime_objects: { - template_url: string; - thumbnail_width: number; - thumbnail_height: number; - thumbnail_count: number; - interval: number; - columns: number; - rows: number; - storyboard_count: number; - }[][] = [ [] ]; - - for (const storyboard of storyboards.boards) { - const extension = new URL(storyboard.template_url).pathname.split('.').at(-1); - - let mime_type = ''; - - switch (extension) { - case 'jpg': - mime_type = 'image/jpeg'; - break; - case 'png': - mime_type = 'image/png'; - break; - case 'webp': - mime_type = 'image/webp'; - break; + } catch (e: any) { + reject(e); } - - const mime_type_index = mime_types.indexOf(mime_type); - if (mime_type_index > -1) { - mime_objects[mime_type_index].push(storyboard); - } else { - mime_types.push(mime_type); - mime_objects.push([]); - mime_objects[mime_types.length - 1].push(storyboard); - } - } - - const duration = formats[0].approx_duration_ms / 1000; - - for (let i = 0; i < mime_types.length; i++) { - const set = this.#el(document, 'AdaptationSet', { - id: `${set_id++}`, - mimeType: mime_types[i], - contentType: 'image' - }); - - period.appendChild(set); - - for (const storyboard of mime_objects[i]) { - await this.#generateRepresentationImage(document, set, storyboard, duration, url_transformer, actions); - } - } - } - } - - static #hoistCodecsIfPossible(set: Element, formats: Format[], hoisted: string[]) { - if (formats.length > 1 && new Set(formats.map((format) => getStringBetweenStrings(format.mime_type, 'codecs="', '"'))).size === 1) { - set.setAttribute('codecs', getStringBetweenStrings(formats[0].mime_type, 'codecs="', '"') as string); - hoisted.push('codecs'); - } - } - - static #hoistNumberAttributeIfPossible(set: Element, formats: Format[], attribute: 'audioSamplingRate' | 'frameRate', property: 'audio_sample_rate' | 'fps', hoisted: string[]) { - if (formats.length > 1 && new Set(formats.map((format) => format.fps)).size === 1) { - set.setAttribute(attribute, formats[0][property]?.toString() as string); - hoisted.push(attribute); - } - } - - static #hoistAudioChannelsIfPossible(document: XMLDocument, set: Element, formats: Format[], hoisted: string[]) { - if (formats.length > 1 && new Set(formats.map((format) => format.audio_channels?.toString() || '2')).size === 1) { - set.appendChild( - this.#el(document, 'AudioChannelConfiguration', { - schemeIdUri: 'urn:mpeg:dash:23003:3:audio_channel_configuration:2011', - value: formats[0].audio_channels?.toString() || '2' - }) - ); - hoisted.push('AudioChannelConfiguration'); - } - } - - static async #generateRepresentationVideo(document: XMLDocument, set: Element, format: Format, url_transformer: URLTransformer, hoisted: string[], cpn?: string, player?: Player, actions?: Actions) { - const url = new URL(format.decipher(player)); - url.searchParams.set('cpn', cpn || ''); - - const representation = this.#el(document, 'Representation', { - id: format.itag?.toString(), - bandwidth: format.bitrate?.toString(), - width: format.width?.toString(), - height: format.height?.toString() - }); - - if (!hoisted.includes('codecs')) { - const codecs = getStringBetweenStrings(format.mime_type, 'codecs="', '"'); - representation.setAttribute('codecs', codecs as string); + }); + }, + async cancel(reason) { + cancel.abort(reason); } - - if (!hoisted.includes('frameRate')) { - representation.setAttribute('frameRate', format.fps?.toString() as string); + }, { + highWaterMark: 1, // TODO: better value? + size(chunk) { + return chunk.byteLength; } + }); - set.appendChild(representation); - - await this.#generateSegmentInformation(document, representation, format, url_transformer(url)?.toString(), actions); - } + return readable_stream; +} - static async #generateRepresentationAudio(document: XMLDocument, set: Element, format: Format, url_transformer: URLTransformer, hoisted: string[], cpn?: string, player?: Player, actions?: Actions) { - const url = new URL(format.decipher(player)); - url.searchParams.set('cpn', cpn || ''); +/** + * Selects the format that best matches the given options. + * @param options - Options + * @param streaming_data - Streaming data + */ +export function chooseFormat(options: FormatOptions, streaming_data?: IStreamingData): Format { + if (!streaming_data) + throw new InnertubeError('Streaming data not available'); + + const formats = [ + ...(streaming_data.formats || []), + ...(streaming_data.adaptive_formats || []) + ]; + + const requires_audio = options.type ? options.type.includes('audio') : true; + const requires_video = options.type ? options.type.includes('video') : true; + const language = options.language || 'original'; + const quality = options.quality || 'best'; + + let best_width = -1; + + const is_best = [ 'best', 'bestefficiency' ].includes(quality); + const use_most_efficient = quality !== 'best'; + + let candidates = formats.filter((format) => { + if (requires_audio && !format.has_audio) + return false; + if (requires_video && !format.has_video) + return false; + if (options.format !== 'any' && !format.mime_type.includes(options.format || 'mp4')) + return false; + if (!is_best && format.quality_label !== quality) + return false; + if (best_width < format.width) + best_width = format.width; + return true; + }); + + if (!candidates.length) + throw new InnertubeError('No matching formats found', { options }); + + if (is_best && requires_video) + candidates = candidates.filter((format) => format.width === best_width); + + if (requires_audio && !requires_video) { + const audio_only = candidates.filter((format) => { + if (language !== 'original') { + return !format.has_video && format.language === language; + } + return !format.has_video && format.is_original; - let id; - if (format.audio_track) { - id = `${format.itag?.toString()}-${format.audio_track.id}`; - } else { - id = format.itag?.toString(); - } - - const representation = this.#el(document, 'Representation', { - id, - bandwidth: format.bitrate?.toString() }); - - if (!hoisted.includes('codecs')) { - const codecs = getStringBetweenStrings(format.mime_type, 'codecs="', '"'); - representation.setAttribute('codecs', codecs as string); - } - - if (!hoisted.includes('audioSamplingRate')) { - representation.setAttribute('audioSamplingRate', format.audio_sample_rate?.toString() as string); - } - - if (!hoisted.includes('AudioChannelConfiguration')) { - representation.appendChild( - this.#el(document, 'AudioChannelConfiguration', { - schemeIdUri: 'urn:mpeg:dash:23003:3:audio_channel_configuration:2011', - value: format.audio_channels?.toString() || '2' - }) - ); - } - - set.appendChild(representation); - - await this.#generateSegmentInformation(document, representation, format, url_transformer(url)?.toString(), actions); - } - - static async #generateRepresentationImage(document: XMLDocument, set: Element, storyboard: { - template_url: string; - thumbnail_width: number; - thumbnail_height: number; - thumbnail_count: number; - interval: number; - columns: number; - rows: number; - storyboard_count: number; - }, duration: number, url_transformer: URLTransformer, actions: Actions) { - const url = storyboard.template_url; - - const response_promises: Promise[] = []; - - // Set a limit so we don't take forever for long videos - const requestLimit = storyboard.storyboard_count > 10 ? 10 : storyboard.storyboard_count; - for (let i = 0; i < requestLimit; i++) { - const response_promise = actions.session.http.fetch_function(new URL(url.replace('$M', i.toString())), { - method: 'HEAD', - headers: Constants.STREAM_HEADERS - }); - - response_promises.push(response_promise); + if (audio_only.length > 0) { + candidates = audio_only; } - - // Run the requests in parallel to avoid causing too much delay - const responses = await Promise.all(response_promises); - - const content_lengths = []; - - for (const response of responses) { - content_lengths.push(parseInt(response.headers.get('Content-Length') || '0', 10)); - - const content_type = response.headers.get('Content-Type'); - - // Sometimes youtube returns webp instead of jpg despite the file extension being jpg - // So we need to update the mime type to reflect the actual mime type of the response - - if (content_type && content_type.length > 0) { - if (set.getAttribute('mimeType') !== content_type) { - set.setAttribute('mimeType', content_type); - } - } - } - - // This is a rough estimate, so it probably won't reflect that actual peak bitrate - // Hopefully it's close enough, because figuring out the actual peak bitrate would require downloading and analysing all storyboard tiles - const bandwidth = Math.ceil((Math.max(...content_lengths) / (storyboard.rows * storyboard.columns)) * 8); - - const representation = this.#el(document, 'Representation', { - id: `thumbnails_${storyboard.thumbnail_width}x${storyboard.thumbnail_height}`, - bandwidth: bandwidth.toString(), - width: (storyboard.thumbnail_width * storyboard.columns).toString(), - height: (storyboard.thumbnail_height * storyboard.rows).toString() - }, [ - this.#el(document, 'EssentialProperty', { - schemeIdUri: 'http://dashif.org/thumbnail_tile', - value: `${storyboard.columns}x${storyboard.rows}` - }), - this.#el(document, 'SegmentTemplate', { - media: url_transformer(new URL(url.replace('$M', '$Number$'))).toString(), - duration: (duration / storyboard.storyboard_count).toString(), - startNumber: '0' - }) - ]); - - set.appendChild(representation); } - static async #generateSegmentInformation(document: XMLDocument, representation: Element, format: Format, url: string, actions?: Actions) { - if (format.is_type_otf) { - if (!actions) { - throw new InnertubeError('Unable to get segment durations for this OTF stream without an Actions instance', { format }); - } - - const { resolved_url, segment_durations } = await this.#getOTFSegmentInformation(url, actions); - const segment_elements = []; - - for (const segment_duration of segment_durations) { - let attributes; - - if (typeof segment_duration.repeat_count === 'undefined') { - attributes = { - d: segment_duration.duration.toString() - }; - } else { - attributes = { - d: segment_duration.duration.toString(), - r: segment_duration.repeat_count.toString() - }; - } - segment_elements.push(this.#el(document, 'S', attributes)); - } - - representation.appendChild( - this.#el(document, 'SegmentTemplate', { - startNumber: '1', - timescale: '1000', - initialization: `${resolved_url}&sq=0`, - media: `${resolved_url}&sq=$Number$` - }, [ - this.#el(document, 'SegmentTimeline', {}, segment_elements) - ]) - ); - } else { - if (!format.index_range || !format.init_range) - throw new InnertubeError('Index and init ranges not available', { format }); - - representation.appendChild( - this.#el(document, 'BaseURL', {}, [ - document.createTextNode(url) - ]) - ); - representation.appendChild( - this.#el(document, 'SegmentBase', { - indexRange: `${format.index_range.start}-${format.index_range.end}` - }, [ - this.#el(document, 'Initialization', { - range: `${format.init_range.start}-${format.init_range.end}` - }) - ]) - ); - } + if (use_most_efficient) { + // Sort by bitrate (lower is better) + candidates.sort((a, b) => a.bitrate - b.bitrate); + } else { + // Sort by bitrate (higher is better) + candidates.sort((a, b) => b.bitrate - a.bitrate); } - static async #getOTFSegmentInformation(url: string, actions: Actions): Promise<{ - resolved_url: string, - segment_durations: { - duration: number, - repeat_count?: number - }[] - }> { - // Fetch the first segment as it contains the segment durations which we need to generate the manifest - const response = await actions.session.http.fetch_function(`${url}&rn=0&sq=0`, { - method: 'GET', - headers: Constants.STREAM_HEADERS, - redirect: 'follow' - }); - - // Example OTF video: https://www.youtube.com/watch?v=DJ8GQUNUXGM - - // There might have been redirects, if there were we want to write the resolved URL to the manifest - // So that the player doesn't have to follow the redirects every time it requests a segment - const resolved_url = response.url.replace('&rn=0', '').replace('&sq=0', ''); - - // In this function we only need the segment durations and how often the durations are repeated - // The segment count could be useful for other stuff though - // The response body contains a lot of junk but the useful stuff looks like this: - // Segment-Count: 922\r\n' + - // 'Segment-Durations-Ms: 5120(r=920),3600,\r\n' - const response_text = await response.text(); - - const segment_duration_strings = getStringBetweenStrings(response_text, 'Segment-Durations-Ms:', '\r\n')?.split(','); - - if (!segment_duration_strings) { - throw new InnertubeError('Failed to extract the segment durations from this OTF stream', { url }); - } - - const segment_durations = []; - for (const segment_duration_string of segment_duration_strings) { - const trimmed_segment_duration = segment_duration_string.trim(); - if (trimmed_segment_duration.length === 0) { - continue; - } - - let repeat_count; - - const repeat_count_string = getStringBetweenStrings(trimmed_segment_duration, '(r=', ')'); - if (repeat_count_string) { - repeat_count = parseInt(repeat_count_string); - } - - segment_durations.push({ - duration: parseInt(trimmed_segment_duration), - repeat_count - }); - } - - return { - resolved_url, - segment_durations - }; - } + return candidates[0]; } -export default FormatUtils; \ No newline at end of file +export { toDash } from './DashManifest.js'; diff --git a/src/utils/StreamingInfo.ts b/src/utils/StreamingInfo.ts new file mode 100644 index 0000000000..33d812a80f --- /dev/null +++ b/src/utils/StreamingInfo.ts @@ -0,0 +1,633 @@ +import type Actions from '../core/Actions.js'; +import type Player from '../core/Player.js'; +import type { StoryboardData } from '../parser/classes/PlayerStoryboardSpec.js'; +import type { IStreamingData } from '../parser/index.js'; +import type { Format } from '../parser/misc.js'; +import type { PlayerStoryboardSpec } from '../parser/nodes.js'; +import type { FormatFilter, URLTransformer } from '../types/FormatUtils.js'; +import { InnertubeError, Platform, getStringBetweenStrings } from './Utils.js'; +import { Constants } from './index.js'; + +export interface StreamingInfo { + duration: number; + audio_sets: AudioSet[]; + video_sets: VideoSet[]; + image_sets: ImageSet[]; +} + +export interface AudioSet { + mime_type: string; + language?: string; + codecs?: string; + audio_sample_rate?: number; + track_name?: string; + track_role?: 'main' | 'dub' | 'description' | 'alternate'; + channels?: number; + representations: AudioRepresentation[]; +} + +export interface Range { + start: number; + end: number; +} + +export type SegmentInfo = { + is_oft: false, + base_url: string; + index_range: Range; + init_range: Range; +} | { + is_oft: true, + getSegmentTemplate(): Promise +} + +export interface Segment { + duration: number, + repeat_count?: number +} + +export interface SegmentTemplate { + init_url: string, + media_url: string, + timeline: Segment[] +} + +export interface AudioRepresentation { + uid: string; + bitrate: number; + codecs?: string; + audio_sample_rate?: number; + channels?: number; + segment_info: SegmentInfo; +} + +export interface VideoSet { + mime_type: string; + color_info: ColorInfo; + codecs?: string; + fps?: number; + representations: VideoRepresentation[] +} + +export interface VideoRepresentation { + uid: string; + bitrate: number; + width: number; + height: number; + fps?: number; + codecs?: string; + segment_info: SegmentInfo; +} + +export interface ColorInfo { + primaries?: '1' | '9', + transfer_characteristics?: '1' | '14' | '16' | '18', + matrix_coefficients?: '1' | '14' +} + +export interface ImageSet { + probable_mime_type: string; + /** + * Sometimes youtube returns webp instead of jpg despite the file extension being jpg + * So we need to update the mime type to reflect the actual mime type of the response + */ + getMimeType(): Promise; + representations: ImageRepresentation[] +} + +export interface ImageRepresentation { + uid: string; + getBitrate(): Promise; + sheet_width: number; + sheet_height: number; + thumbnail_width: number; + thumbnail_height: number; + rows: number; + columns: number; + template_url: string; + template_duration: number; + getURL(n: number): string; +} + +function getFormatGroupings(formats: Format[]) { + const group_info = new Map(); + + const has_multiple_audio_tracks = formats.some((fmt) => !!fmt.audio_track); + + for (const format of formats) { + if ((!format.index_range || !format.init_range) && !format.is_type_otf) { + continue; + } + const mime_type = format.mime_type.split(';')[0]; + + // Codec without any profile or level information + const just_codec = getStringBetweenStrings(format.mime_type, 'codecs="', '"')?.split('.')[0]; + + // HDR videos have both SDR and HDR vp9 formats, so we want to stick them in different groups + const color_info = format.color_info ? Object.values(format.color_info).join('-') : ''; + + const audio_track_id = format.audio_track?.id || ''; + + const group_id = `${mime_type}-${just_codec}-${color_info}-${audio_track_id}`; + + if (!group_info.has(group_id)) { + group_info.set(group_id, []); + } + group_info.get(group_id)?.push(format); + } + + return { + groups: Array.from(group_info.values()), + has_multiple_audio_tracks + }; +} + +function hoistCodecsIfPossible(formats: Format[], hoisted: string[]) { + if ( + formats.length > 1 && + new Set(formats.map((format) => getStringBetweenStrings(format.mime_type, 'codecs="', '"'))).size === 1 + ) { + hoisted.push('codecs'); + return getStringBetweenStrings(formats[0].mime_type, 'codecs="', '"'); + } +} + +function hoistNumberAttributeIfPossible( + formats: Format[], + property: 'audio_sample_rate' | 'fps', + hoisted: string[] +) { + if (formats.length > 1 && new Set(formats.map((format) => format.fps)).size === 1) { + hoisted.push(property); + return Number(formats[0][property]); + } +} + +function hoistAudioChannelsIfPossible(formats: Format[], hoisted: string[]) { + if (formats.length > 1 && new Set(formats.map((format) => format.audio_channels || 2)).size === 1) { + hoisted.push('AudioChannelConfiguration'); + return formats[0].audio_channels; + } +} + +async function getOTFSegmentTemplate(url: string, actions: Actions): Promise { + // Fetch the first segment as it contains the segment durations which we need to generate the manifest + const response = await actions.session.http.fetch_function(`${url}&rn=0&sq=0`, { + method: 'GET', + headers: Constants.STREAM_HEADERS, + redirect: 'follow' + }); + + // Example OTF video: https://www.youtube.com/watch?v=DJ8GQUNUXGM + + // There might have been redirects, if there were we want to write the resolved URL to the manifest + // So that the player doesn't have to follow the redirects every time it requests a segment + const resolved_url = response.url.replace('&rn=0', '').replace('&sq=0', ''); + + // In this function we only need the segment durations and how often the durations are repeated + // The segment count could be useful for other stuff though + // The response body contains a lot of junk but the useful stuff looks like this: + // Segment-Count: 922\r\n' + + // 'Segment-Durations-Ms: 5120(r=920),3600,\r\n' + const response_text = await response.text(); + + const segment_duration_strings = getStringBetweenStrings(response_text, 'Segment-Durations-Ms:', '\r\n')?.split(','); + + if (!segment_duration_strings) { + throw new InnertubeError('Failed to extract the segment durations from this OTF stream', { url }); + } + + const segment_durations = []; + for (const segment_duration_string of segment_duration_strings) { + const trimmed_segment_duration = segment_duration_string.trim(); + if (trimmed_segment_duration.length === 0) { + continue; + } + + let repeat_count; + + const repeat_count_string = getStringBetweenStrings(trimmed_segment_duration, '(r=', ')'); + if (repeat_count_string) { + repeat_count = parseInt(repeat_count_string); + } + + segment_durations.push({ + duration: parseInt(trimmed_segment_duration), + repeat_count + }); + } + + return { + init_url: `${resolved_url}&sq=0`, + media_url: `${resolved_url}&sq=$Number$`, + timeline: segment_durations + }; +} + +function getSegmentInfo( + format: Format, + url_transformer: URLTransformer, + actions?: Actions, + player?: Player, + cpn?: string +) { + const url = new URL(format.decipher(player)); + url.searchParams.set('cpn', cpn || ''); + + const transformed_url = url_transformer(url).toString(); + + if (format.is_type_otf) { + if (!actions) + throw new InnertubeError('Unable to get segment durations for this OTF stream without an Actions instance', { format }); + + const info: SegmentInfo = { + is_oft: true, + getSegmentTemplate() { + return getOTFSegmentTemplate(transformed_url, actions); + } + }; + + return info; + } + + if (!format.index_range || !format.init_range) + throw new InnertubeError('Index and init ranges not available', { format }); + + const info: SegmentInfo = { + is_oft: false, + base_url: transformed_url, + index_range: format.index_range, + init_range: format.init_range + }; + + return info; +} + +function getAudioRepresentation( + format: Format, + hoisted: string[], + url_transformer: URLTransformer, + actions?: Actions, + player?: Player, + cpn?: string +) { + const url = new URL(format.decipher(player)); + url.searchParams.set('cpn', cpn || ''); + + const rep: AudioRepresentation = { + uid: format.audio_track ? `${format.itag}-${format.audio_track.id}` : format.itag.toString(), + bitrate: format.bitrate, + codecs: !hoisted.includes('codecs') ? getStringBetweenStrings(format.mime_type, 'codecs="', '"') : undefined, + audio_sample_rate: !hoisted.includes('audio_sample_rate') ? format.audio_sample_rate : undefined, + channels: !hoisted.includes('AudioChannelConfiguration') ? format.audio_channels || 2 : undefined, + segment_info: getSegmentInfo(format, url_transformer, actions, player, cpn) + }; + + return rep; +} + +function getTrackRole(format: Format) { + const { audio_track } = format; + + if (!audio_track) + return; + + if (audio_track.audio_is_default) + return 'main'; + + if (format.is_dubbed) + return 'dub'; + + if (format.is_descriptive) + return 'description'; + + return 'alternate'; +} + +function getAudioSet( + formats: Format[], + url_transformer: URLTransformer, + actions?: Actions, + player?: Player, + cpn?: string +) { + const first_format = formats[0]; + const { audio_track } = first_format; + const hoisted: string[] = []; + + const set: AudioSet = { + mime_type: first_format.mime_type.split(';')[0], + language: first_format.language ?? undefined, + codecs: hoistCodecsIfPossible(formats, hoisted), + audio_sample_rate: hoistNumberAttributeIfPossible(formats, 'audio_sample_rate', hoisted), + track_name: audio_track?.display_name, + track_role: getTrackRole(first_format), + channels: hoistAudioChannelsIfPossible(formats, hoisted), + representations: formats.map((format) => getAudioRepresentation(format, hoisted, url_transformer, actions, player, cpn)) + }; + + return set; +} + +const COLOR_PRIMARIES: Record = { + BT709: '1', + BT2020: '9' +}; + +const COLOR_TRANSFER_CHARACTERISTICS: Record = { + BT709: '1', + BT2020_10: '14', + SMPTEST2084: '16', + ARIB_STD_B67: '18' +}; + +// This list is incomplete, as the player.js doesn't currently have any code for matrix coefficients, +// So it doesn't have a list like with the other two, so this is just based on what we've seen in responses +const COLOR_MATRIX_COEFFICIENTS: Record = { + BT709: '1', + BT2020_NCL: '14' +}; + +function getColorInfo(format: Format) { + // Section 5.5 Video source metadata signalling https://dashif.org/docs/IOP-Guidelines/DASH-IF-IOP-Part7-v5.0.0.pdf + // Section 8 Video code points https://www.itu.int/rec/T-REC-H.273-202107-I/en + // The player.js file was also helpful + + const color_info = format.color_info; + const primaries = + color_info?.primaries ? COLOR_PRIMARIES[color_info.primaries] : undefined; + + const transfer_characteristics = + color_info?.transfer_characteristics ? COLOR_TRANSFER_CHARACTERISTICS[color_info.transfer_characteristics] : undefined; + + const matrix_coefficients = + color_info?.matrix_coefficients ? COLOR_MATRIX_COEFFICIENTS[color_info.matrix_coefficients] : undefined; + + if (color_info?.matrix_coefficients && !matrix_coefficients) { + const url = new URL(format.url as string); + + const anonymisedFormat = JSON.parse(JSON.stringify(format)); + anonymisedFormat.url = 'REDACTED'; + anonymisedFormat.signature_cipher = 'REDACTED'; + anonymisedFormat.cipher = 'REDACTED'; + + console.warn(`YouTube.js toDash(): Unknown matrix coefficients "${color_info.matrix_coefficients}", the DASH manifest is still usuable without this.\n` + + `Please report it at ${Platform.shim.info.bugs_url} so we can add support for it.\n` + + `Innertube client: ${url.searchParams.get('c')}\nformat:`, anonymisedFormat); + } + + const info: ColorInfo = { + primaries, + transfer_characteristics, + matrix_coefficients + }; + + return info; +} + +function getVideoRepresentation( + format: Format, + url_transformer: URLTransformer, + hoisted: string[], + player?: Player, + actions?: Actions, + cpn?: string +) { + const rep: VideoRepresentation = { + uid: format.itag.toString(), + bitrate: format.bitrate, + width: format.width, + height: format.height, + codecs: !hoisted.includes('codecs') ? getStringBetweenStrings(format.mime_type, 'codecs="', '"') : undefined, + fps: !hoisted.includes('fps') ? format.fps : undefined, + segment_info: getSegmentInfo(format, url_transformer, actions, player, cpn) + }; + + return rep; +} + +function getVideoSet( + formats: Format[], + url_transformer: URLTransformer, + player?: Player, + actions?: Actions, + cpn?: string +) { + const first_format = formats[0]; + const color_info = getColorInfo(first_format); + const hoisted: string[] = []; + + const set: VideoSet = { + mime_type: first_format.mime_type.split(';')[0], + color_info, + codecs: hoistCodecsIfPossible(formats, hoisted), + fps: hoistNumberAttributeIfPossible(formats, 'fps', hoisted), + representations: formats.map((format) => getVideoRepresentation(format, url_transformer, hoisted, player, actions, cpn)) + }; + + return set; +} + +function getStoryboardInfo( + storyboards: PlayerStoryboardSpec +) { + const mime_info = new Map(); + + for (const storyboard of storyboards.boards) { + const extension = new URL(storyboard.template_url).pathname.split('.').pop(); + + const mime_type = `image/${extension === 'jpg' ? 'jpeg' : extension}`; + + if (!mime_info.has(mime_type)) { + mime_info.set(mime_type, []); + } + mime_info.get(mime_type)?.push(storyboard); + } + + return mime_info; +} + +interface SharedStoryboardResponse { + response?: Promise +} + +async function getStoryboardMimeType( + actions: Actions, + board: StoryboardData, + transform_url: URLTransformer, + probable_mime_type: string, + shared_response: SharedStoryboardResponse +) { + const url = board.template_url; + + const req_url = transform_url(new URL(url.replace('$M', '0'))); + + const res_promise = shared_response.response ? shared_response.response : actions.session.http.fetch_function(req_url, { + method: 'HEAD', + headers: Constants.STREAM_HEADERS + }); + + shared_response.response = res_promise; + + const res = await res_promise; + + return res.headers.get('Content-Type') || probable_mime_type; +} + +async function getStoryboardBitrate( + actions: Actions, + board: StoryboardData, + shared_response: SharedStoryboardResponse +) { + const url = board.template_url; + + const response_promises: Promise[] = []; + + // Set a limit so we don't take forever for long videos + const request_limit = Math.min(board.storyboard_count, 10); + for (let i = 0; i < request_limit; i++) { + const req_url = new URL(url.replace('$M', i.toString())); + + const response_promise = + i === 0 && shared_response.response ? + shared_response.response : + actions.session.http.fetch_function(req_url, { + method: 'HEAD', + headers: Constants.STREAM_HEADERS + }); + + if (i === 0) + shared_response.response = response_promise; + + response_promises.push(response_promise); + } + + // Run the requests in parallel to avoid causing too much delay + const responses = await Promise.all(response_promises); + + const content_lengths = []; + + for (const response of responses) { + content_lengths.push(parseInt(response.headers.get('Content-Length') || '0')); + } + + // This is a rough estimate, so it probably won't reflect that actual peak bitrate + // Hopefully it's close enough, because figuring out the actual peak bitrate would require downloading and analysing all storyboard tiles + const bandwidth = Math.ceil((Math.max(...content_lengths) / (board.rows * board.columns)) * 8); + + return bandwidth; +} + +function getImageRepresentation( + duration: number, + actions: Actions, + board: StoryboardData, + transform_url: URLTransformer, + shared_response: SharedStoryboardResponse +) { + const url = board.template_url; + const template_url = new URL(url.replace('$M', '$Number$')); + + const rep: ImageRepresentation = { + uid: `thumbnails_${board.thumbnail_width}x${board.thumbnail_height}`, + getBitrate() { + return getStoryboardBitrate(actions, board, shared_response); + }, + sheet_width: board.thumbnail_width * board.columns, + sheet_height: board.thumbnail_height * board.rows, + thumbnail_height: board.thumbnail_height, + thumbnail_width: board.thumbnail_width, + rows: board.rows, + columns: board.columns, + template_duration: duration / board.storyboard_count, + template_url: transform_url(template_url).toString(), + getURL(n) { + return template_url.toString().replace('$Number$', n.toString()); + } + }; + + return rep; +} + +function getImageSets( + duration: number, + actions: Actions, + storyboards: PlayerStoryboardSpec, + transform_url: URLTransformer +) { + const mime_info = getStoryboardInfo(storyboards); + + const shared_response: SharedStoryboardResponse = {}; + + return Array.from(mime_info.entries()).map(([ type, boards ]) => ({ + probable_mime_type: type, + getMimeType() { + return getStoryboardMimeType(actions, boards[0], transform_url, type, shared_response); + }, + representations: boards.map((board) => getImageRepresentation(duration, actions, board, transform_url, shared_response)) + })); +} + +export function getStreamingInfo( + streaming_data?: IStreamingData, + url_transformer: URLTransformer = (url) => url, + format_filter?: FormatFilter, + cpn?: string, + player?: Player, + actions?: Actions, + storyboards?: PlayerStoryboardSpec +) { + if (!streaming_data) + throw new InnertubeError('Streaming data not available'); + + const formats = format_filter ? + streaming_data.adaptive_formats.filter((fmt) => !format_filter(fmt)) : + streaming_data.adaptive_formats; + + const duration = formats[0].approx_duration_ms / 1000; + + const { + groups, + has_multiple_audio_tracks + } = getFormatGroupings(formats); + + const { + video_groups, + audio_groups + } = groups.reduce((acc, formats) => { + if (formats[0].has_audio) { + // Some videos with multiple audio tracks, have a broken one, that doesn't have any audio track information + // It seems to be the same as default audio track but broken + // We want to ignore it, as it messes up audio track selection in players and YouTube ignores it too + // At the time of writing, this video has a broken audio track: https://youtu.be/UJeSWbR6W04 + if (has_multiple_audio_tracks && !formats[0].audio_track) + return acc; + + acc.audio_groups.push(formats); + return acc; + } + + acc.video_groups.push(formats); + + return acc; + }, { + video_groups: [] as Format[][], + audio_groups: [] as Format[][] + }); + + const audio_sets = audio_groups.map((formats) => getAudioSet(formats, url_transformer, actions, player, cpn)); + + const video_sets = video_groups.map((formats) => getVideoSet(formats, url_transformer, player, actions, cpn)); + + // XXX: We need to make requests to get the image sizes, so we'll skip the storyboards if we don't have an Actions instance + const image_sets = storyboards && actions ? getImageSets(duration, actions, storyboards, url_transformer) : []; + + const info : StreamingInfo = { + duration, + audio_sets, + video_sets, + image_sets + }; + + return info; +} diff --git a/src/utils/index.ts b/src/utils/index.ts index 04c5c86a55..e06a190372 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -4,8 +4,7 @@ export * as Constants from './Constants.js'; export { default as EventEmitter } from './EventEmitterLike.js'; -export { default as FormatUtils } from './FormatUtils.js'; -export * from './FormatUtils.js'; +export * as FormatUtils from './FormatUtils.js'; export { default as HTTPClient } from './HTTPClient.js'; export * from './HTTPClient.js'; diff --git a/tsconfig.json b/tsconfig.json index 7cdd64cb15..51c3cb95f9 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,11 +13,11 @@ /* Language and Environment */ "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ "lib": ["ESNext", "DOM", "DOM.Iterable"], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ - // "jsx": "preserve", /* Specify what JSX code is generated. */ + "jsx": "react", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ - // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ - // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ + "jsxFactory": "DashUtils.createElement", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ + "jsxFragmentFactory": "DashUtils.Fragment", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ @@ -102,6 +102,7 @@ }, "include": [ "src/**/*.ts", + "src/**/*.tsx", "src/**/*.js" ], "exclude": [