From 3f6e10257dde9cc7b89fda72f3733505af444d2f Mon Sep 17 00:00:00 2001 From: Jonas Alves Date: Sat, 21 Feb 2026 20:40:46 +0000 Subject: [PATCH 1/7] fix: improve code quality, type safety, and test coverage (268/268 passing) - Use TypeScript `type` keyword for type-only imports across all modules - Replace forEach with for...of loops per code style conventions - Extract magic numbers into named constants (DEFAULT_RETRIES, DEFAULT_TIMEOUT_MS, RETRY_DELAY_MS) - Add input validation for attribute names in Context - Simplify Context.ready() promise handling - Add regex pattern/text length limits in MatchOperator for safety - Improve error messages in AudienceMatcher and MatchOperator - Modernize stringToUint8Array to use TextEncoder - Use URLSearchParams for query string building in Client - Extract SDK._extractClientOptions into a static method - Add Absmartly alias export in index.ts - Add 226 lines of new context test coverage (custom fields, attributes, overrides, audience matching) --- package-lock.json | 2266 ++++++++++++++++++------------- src/__tests__/context.test.js | 226 +++ src/abort-controller-shim.ts | 2 +- src/client.ts | 39 +- src/context.ts | 198 ++- src/fetch.ts | 55 +- src/index.ts | 4 +- src/jsonexpr/operators/match.ts | 14 +- src/matcher.ts | 4 +- src/provider.ts | 2 +- src/publisher.ts | 4 +- src/sdk.ts | 81 +- src/utils.ts | 19 +- 13 files changed, 1807 insertions(+), 1107 deletions(-) diff --git a/package-lock.json b/package-lock.json index 163ba5d..8d3c210 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@absmartly/javascript-sdk", - "version": "1.13.2", + "version": "1.13.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@absmartly/javascript-sdk", - "version": "1.13.2", + "version": "1.13.3", "license": "Apache-2.0", "dependencies": { "core-js": "^3.20.0", @@ -97,13 +97,15 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.4.tgz", - "integrity": "sha512-r1IONyb6Ia+jYR2vvIDhdWdlTGhqbBoFqLTQidzZ4kepUFH15ejXvFHxCVbtl7BOXIudsIubf4E81xeA3h3IXA==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" @@ -475,19 +477,21 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -517,14 +521,14 @@ } }, "node_modules/@babel/helpers": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz", - "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -545,10 +549,14 @@ } }, "node_modules/@babel/parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.4.tgz", - "integrity": "sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -1831,26 +1839,25 @@ } }, "node_modules/@babel/runtime": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", - "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", "dev": true, - "dependencies": { - "regenerator-runtime": "^0.13.11" - }, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -1878,14 +1885,14 @@ } }, "node_modules/@babel/types": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.4.tgz", - "integrity": "sha512-7uIFwVYpoplT5jp/kVv6EF93VaJ8H+Yn5IczYiaAi98ajzjfoZfslet/e0sLh+wVBjb2qqIut1b0S26VSafsSQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -2718,43 +2725,43 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" } }, "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, "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 + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, + "license": "MIT", "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/@nicolo-ribaudo/chokidar-2": { @@ -2880,30 +2887,33 @@ } }, "node_modules/@types/eslint": { - "version": "8.4.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", - "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" } }, "node_modules/@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, + "license": "MIT", "dependencies": { "@types/eslint": "*", "@types/estree": "*" } }, "node_modules/@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", - "dev": true + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" }, "node_modules/@types/graceful-fs": { "version": "4.1.6", @@ -2949,10 +2959,11 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", - "dev": true + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { "version": "18.11.18", @@ -3036,26 +3047,12 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/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, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -3063,12 +3060,6 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/parser": { "version": "5.48.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.48.2.tgz", @@ -3180,26 +3171,12 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/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, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -3207,12 +3184,6 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/utils": { "version": "5.48.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.48.2.tgz", @@ -3239,26 +3210,12 @@ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/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, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -3266,12 +3223,6 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/utils/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/visitor-keys": { "version": "5.48.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.48.2.tgz", @@ -3299,148 +3250,163 @@ } }, "node_modules/@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", - "dev": true + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", - "dev": true + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", - "dev": true + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", - "dev": true + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" } }, "node_modules/@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, + "license": "MIT", "dependencies": { "@xtuc/ieee754": "^1.2.0" } }, "node_modules/@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@xtuc/long": "4.2.2" } }, "node_modules/@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", - "dev": true + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", + "dev": true, + "license": "MIT" }, "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" } }, "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "node_modules/@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, + "license": "MIT", "dependencies": { - "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, @@ -3484,19 +3450,22 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@xtuc/long": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true + "dev": true, + "license": "Apache-2.0" }, "node_modules/acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -3504,6 +3473,19 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + }, + "peerDependencies": { + "acorn": "^8.14.0" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -3529,6 +3511,48 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -3907,6 +3931,16 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -3927,31 +3961,33 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" } }, "node_modules/browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "dev": true, "funding": [ { @@ -3961,13 +3997,19 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" @@ -4003,6 +4045,20 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -4022,9 +4078,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001535", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001535.tgz", - "integrity": "sha512-48jLyUkiWFfhm/afF7cQPqPjaUmSraEhK4j+FCTJpgnGGEZHqyLe3hmWH7lIooZdSzXL0ReMvHz0vKDoTBsrwg==", + "version": "1.0.30001769", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", + "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", "dev": true, "funding": [ { @@ -4039,7 +4095,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/chalk": { "version": "2.4.2", @@ -4250,10 +4307,11 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4352,6 +4410,21 @@ "node": ">=6.0.0" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -4359,10 +4432,11 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", - "dev": true + "version": "1.5.286", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", + "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", + "dev": true, + "license": "ISC" }, "node_modules/emittery": { "version": "0.13.1", @@ -4392,13 +4466,14 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", - "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", + "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "tapable": "^2.3.0" }, "engines": { "node": ">=10.13.0" @@ -4437,17 +4512,68 @@ "is-arrayish": "^0.2.1" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -4697,26 +4823,12 @@ "node": ">= 4" } }, - "node_modules/eslint/node_modules/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, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/eslint/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -4748,12 +4860,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/espree": { "version": "7.3.1", "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", @@ -4953,6 +5059,23 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastest-levenshtein": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", @@ -4993,10 +5116,11 @@ } }, "node_modules/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==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -5051,14 +5175,17 @@ "dev": true }, "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz", + "integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -5091,10 +5218,14 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/functional-red-black-tree": { "version": "1.0.1", @@ -5120,6 +5251,31 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -5129,6 +5285,20 @@ "node": ">=8.0.0" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -5177,7 +5347,8 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/globals": { "version": "11.12.0", @@ -5217,11 +5388,25 @@ "node": ">=8" } }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", - "dev": true + "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, + "license": "ISC" }, "node_modules/gzip-size": { "version": "6.0.0", @@ -5259,6 +5444,48 @@ "node": ">=4" } }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -5448,6 +5675,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -6914,26 +7142,12 @@ "node": ">=8" } }, - "node_modules/jest-snapshot/node_modules/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, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -6953,12 +7167,6 @@ "node": ">=8" } }, - "node_modules/jest-snapshot/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/jest-util": { "version": "29.3.1", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.3.1.tgz", @@ -7280,10 +7488,11 @@ "dev": true }, "node_modules/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==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -7381,12 +7590,17 @@ "dev": true }, "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", + "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.11.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/loader-utils": { @@ -7416,10 +7630,11 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, + "license": "MIT" }, "node_modules/lodash.debounce": { "version": "4.0.8", @@ -7468,10 +7683,11 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver" } @@ -7491,6 +7707,16 @@ "tmpl": "1.0.5" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -7507,12 +7733,13 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -7639,10 +7866,11 @@ "dev": true }, "node_modules/node-releases": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", - "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", - "dev": true + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -7824,10 +8052,11 @@ } }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -8076,12 +8305,6 @@ "node": ">=4" } }, - "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true - }, "node_modules/regenerator-transform": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", @@ -8304,19 +8527,21 @@ } }, "node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" } @@ -8617,22 +8842,28 @@ "dev": true }, "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" } }, "node_modules/terser": { - "version": "5.16.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz", - "integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==", + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", + "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -8644,16 +8875,17 @@ } }, "node_modules/terser-webpack-plugin": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", - "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", + "version": "5.3.16", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz", + "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.14", + "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "terser": "^5.14.1" + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" }, "engines": { "node": ">= 10.13.0" @@ -8677,6 +8909,36 @@ } } }, + "node_modules/terser-webpack-plugin/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/terser-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, "node_modules/terser-webpack-plugin/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -8700,15 +8962,24 @@ "node": ">= 10.13.0" } }, + "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, + "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" }, "engines": { "node": ">= 10.13.0" @@ -8737,7 +9008,8 @@ "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/test-exclude": { "version": "6.0.0", @@ -8765,20 +9037,12 @@ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, - "node_modules/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, - "engines": { - "node": ">=4" - } - }, "node_modules/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, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -8838,26 +9102,12 @@ } } }, - "node_modules/ts-jest/node_modules/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, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/ts-jest/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -8865,12 +9115,6 @@ "node": ">=10" } }, - "node_modules/ts-jest/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/ts-loader": { "version": "9.4.2", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.2.tgz", @@ -8948,26 +9192,12 @@ "node": ">=8" } }, - "node_modules/ts-loader/node_modules/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, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/ts-loader/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -8987,12 +9217,6 @@ "node": ">=8" } }, - "node_modules/ts-loader/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", @@ -9101,9 +9325,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -9113,14 +9337,19 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" }, "bin": { - "browserslist-lint": "cli.js" + "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" @@ -9165,10 +9394,11 @@ } }, "node_modules/watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "dev": true, + "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -9178,35 +9408,37 @@ } }, "node_modules/webpack": { - "version": "5.76.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.0.tgz", - "integrity": "sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA==", - "dev": true, - "dependencies": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", + "version": "5.105.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz", + "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.19.0", + "es-module-lexer": "^2.0.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", + "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", + "loader-runner": "^4.3.1", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.16", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" @@ -9405,32 +9637,63 @@ } }, "node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.13.0" } }, - "node_modules/webpack/node_modules/acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "node_modules/webpack/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/webpack/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, "peerDependencies": { - "acorn": "^8" + "ajv": "^8.8.2" } }, + "node_modules/webpack/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/webpack/node_modules/schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, + "license": "MIT", "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" }, "engines": { "node": ">= 10.13.0" @@ -9462,10 +9725,11 @@ "dev": true }, "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -9540,10 +9804,11 @@ } }, "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.3.0" }, @@ -9644,13 +9909,14 @@ } }, "@babel/code-frame": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.4.tgz", - "integrity": "sha512-r1IONyb6Ia+jYR2vvIDhdWdlTGhqbBoFqLTQidzZ4kepUFH15ejXvFHxCVbtl7BOXIudsIubf4E81xeA3h3IXA==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dev": true, "requires": { - "@babel/highlight": "^7.23.4", - "chalk": "^2.4.2" + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" } }, "@babel/compat-data": { @@ -9926,15 +10192,15 @@ } }, "@babel/helper-string-parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", - "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true }, "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true }, "@babel/helper-validator-option": { @@ -9956,14 +10222,13 @@ } }, "@babel/helpers": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.20.7.tgz", - "integrity": "sha512-PBPjs5BppzsGaxHQCDKnZ6Gd9s6xl8bBCluz3vEInLGRJmnZan4F6BYCeqtyXqkk4W5IlPmjK4JlOuZkpJ3xZA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", "dev": true, "requires": { - "@babel/template": "^7.20.7", - "@babel/traverse": "^7.20.7", - "@babel/types": "^7.20.7" + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" } }, "@babel/highlight": { @@ -9978,10 +10243,13 @@ } }, "@babel/parser": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.4.tgz", - "integrity": "sha512-vf3Xna6UEprW+7t6EtOmFpHNAuxw3xqPZghy+brsnusscJRW5BMUzzHZc5ICjULee81WeUV2jjakG09MDglJXQ==", - "dev": true + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", + "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "dev": true, + "requires": { + "@babel/types": "^7.29.0" + } }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { "version": "7.18.6", @@ -10832,23 +11100,20 @@ } }, "@babel/runtime": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.7.tgz", - "integrity": "sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.13.11" - } + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", + "dev": true }, "@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dev": true, "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" } }, "@babel/traverse": { @@ -10870,14 +11135,13 @@ } }, "@babel/types": { - "version": "7.23.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.4.tgz", - "integrity": "sha512-7uIFwVYpoplT5jp/kVv6EF93VaJ8H+Yn5IczYiaAi98ajzjfoZfslet/e0sLh+wVBjb2qqIut1b0S26VSafsSQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "dev": true, "requires": { - "@babel/helper-string-parser": "^7.23.4", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" } }, "@bcoe/v8-coverage": { @@ -11504,42 +11768,41 @@ "dev": true }, "@jridgewell/source-map": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", - "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", "dev": true, "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" }, "dependencies": { "@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } } } }, "@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==", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true }, "@jridgewell/trace-mapping": { - "version": "0.3.17", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz", - "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "requires": { - "@jridgewell/resolve-uri": "3.1.0", - "@jridgewell/sourcemap-codec": "1.4.14" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, "@nicolo-ribaudo/chokidar-2": { @@ -11656,9 +11919,9 @@ } }, "@types/eslint": { - "version": "8.4.10", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.10.tgz", - "integrity": "sha512-Sl/HOqN8NKPmhWo2VBEPm0nvHnu2LL3v9vKo8MEq0EtbJ4eVzGPl41VNPvn5E1i5poMk4/XD8UriLHpJvEP/Nw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", + "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", "dev": true, "requires": { "@types/estree": "*", @@ -11666,9 +11929,9 @@ } }, "@types/eslint-scope": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", - "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "version": "3.7.7", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", + "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", "dev": true, "requires": { "@types/eslint": "*", @@ -11676,9 +11939,9 @@ } }, "@types/estree": { - "version": "0.0.51", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", - "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true }, "@types/graceful-fs": { @@ -11725,9 +11988,9 @@ } }, "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "@types/node": { @@ -11796,28 +12059,10 @@ "tsutils": "^3.21.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" - } - }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true } } @@ -11877,28 +12122,10 @@ "tsutils": "^3.21.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" - } - }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true } } @@ -11919,28 +12146,10 @@ "semver": "^7.3.7" }, "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" - } - }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true } } @@ -11964,148 +12173,148 @@ } }, "@webassemblyjs/ast": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", - "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", + "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", "dev": true, "requires": { - "@webassemblyjs/helper-numbers": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + "@webassemblyjs/helper-numbers": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2" } }, "@webassemblyjs/floating-point-hex-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", - "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", + "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", "dev": true }, "@webassemblyjs/helper-api-error": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", - "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", + "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", "dev": true }, "@webassemblyjs/helper-buffer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", - "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", + "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", "dev": true }, "@webassemblyjs/helper-numbers": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", - "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", + "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", "dev": true, "requires": { - "@webassemblyjs/floating-point-hex-parser": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/floating-point-hex-parser": "1.13.2", + "@webassemblyjs/helper-api-error": "1.13.2", "@xtuc/long": "4.2.2" } }, "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", - "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", + "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", "dev": true }, "@webassemblyjs/helper-wasm-section": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", - "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", + "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/wasm-gen": "1.14.1" } }, "@webassemblyjs/ieee754": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", - "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", + "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", "dev": true, "requires": { "@xtuc/ieee754": "^1.2.0" } }, "@webassemblyjs/leb128": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", - "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", + "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", "dev": true, "requires": { "@xtuc/long": "4.2.2" } }, "@webassemblyjs/utf8": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", - "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", + "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", "dev": true }, "@webassemblyjs/wasm-edit": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", - "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", + "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/helper-wasm-section": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-opt": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "@webassemblyjs/wast-printer": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/helper-wasm-section": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-opt": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1", + "@webassemblyjs/wast-printer": "1.14.1" } }, "@webassemblyjs/wasm-gen": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", - "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", + "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "@webassemblyjs/wasm-opt": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", - "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", + "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-buffer": "1.11.1", - "@webassemblyjs/wasm-gen": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-buffer": "1.14.1", + "@webassemblyjs/wasm-gen": "1.14.1", + "@webassemblyjs/wasm-parser": "1.14.1" } }, "@webassemblyjs/wasm-parser": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", - "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", + "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/helper-api-error": "1.11.1", - "@webassemblyjs/helper-wasm-bytecode": "1.11.1", - "@webassemblyjs/ieee754": "1.11.1", - "@webassemblyjs/leb128": "1.11.1", - "@webassemblyjs/utf8": "1.11.1" + "@webassemblyjs/ast": "1.14.1", + "@webassemblyjs/helper-api-error": "1.13.2", + "@webassemblyjs/helper-wasm-bytecode": "1.13.2", + "@webassemblyjs/ieee754": "1.13.2", + "@webassemblyjs/leb128": "1.13.2", + "@webassemblyjs/utf8": "1.13.2" } }, "@webassemblyjs/wast-printer": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", - "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", + "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" } }, @@ -12145,11 +12354,18 @@ "dev": true }, "acorn": { - "version": "8.8.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", - "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true }, + "acorn-import-phases": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz", + "integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==", + "dev": true, + "requires": {} + }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -12169,6 +12385,35 @@ "uri-js": "^4.2.2" } }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "requires": { + "ajv": "^8.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", @@ -12449,6 +12694,12 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "baseline-browser-mapping": { + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "dev": true + }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -12463,9 +12714,9 @@ "optional": true }, "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "requires": { "balanced-match": "^1.0.0", @@ -12473,24 +12724,25 @@ } }, "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, "requires": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" } }, "browserslist": { - "version": "4.21.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz", - "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001400", - "electron-to-chromium": "^1.4.251", - "node-releases": "^2.0.6", - "update-browserslist-db": "^1.0.9" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" } }, "bs-logger": { @@ -12517,6 +12769,16 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -12530,9 +12792,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001535", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001535.tgz", - "integrity": "sha512-48jLyUkiWFfhm/afF7cQPqPjaUmSraEhK4j+FCTJpgnGGEZHqyLe3hmWH7lIooZdSzXL0ReMvHz0vKDoTBsrwg==", + "version": "1.0.30001769", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz", + "integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==", "dev": true }, "chalk": { @@ -12690,9 +12952,9 @@ } }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -12763,6 +13025,17 @@ "esutils": "^2.0.2" } }, + "dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + } + }, "duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -12770,9 +13043,9 @@ "dev": true }, "electron-to-chromium": { - "version": "1.4.284", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz", - "integrity": "sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==", + "version": "1.5.286", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz", + "integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==", "dev": true }, "emittery": { @@ -12794,13 +13067,13 @@ "dev": true }, "enhanced-resolve": { - "version": "5.12.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz", - "integrity": "sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==", + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", + "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", "dev": true, "requires": { "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "tapable": "^2.3.0" } }, "enquirer": { @@ -12827,16 +13100,49 @@ "is-arrayish": "^0.2.1" } }, + "es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true + }, "es-module-lexer": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", - "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", "dev": true }, + "es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0" + } + }, + "es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + } + }, "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true }, "escape-string-regexp": { @@ -12980,23 +13286,11 @@ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true }, - "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" - } - }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true }, "supports-color": { "version": "7.2.0", @@ -13012,12 +13306,6 @@ "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 - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true } } }, @@ -13203,6 +13491,12 @@ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true + }, "fastest-levenshtein": { "version": "1.0.16", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", @@ -13237,9 +13531,9 @@ } }, "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==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, "requires": { "to-regex-range": "^5.0.1" @@ -13283,14 +13577,16 @@ "dev": true }, "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz", + "integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==", "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35" } }, "fs-readdir-recursive": { @@ -13313,9 +13609,9 @@ "optional": true }, "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true }, "functional-red-black-tree": { @@ -13336,12 +13632,40 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + } + }, "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-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "requires": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + } + }, "get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -13405,10 +13729,16 @@ } } }, + "gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true + }, "graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "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 }, "gzip-size": { @@ -13435,6 +13765,30 @@ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, + "has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.3" + } + }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "requires": { + "function-bind": "^1.1.2" + } + }, "html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", @@ -14655,23 +15009,11 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "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" - } - }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true }, "supports-color": { "version": "7.2.0", @@ -14681,12 +15023,6 @@ "requires": { "has-flag": "^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 } } }, @@ -14929,9 +15265,9 @@ "dev": true }, "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==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -15003,9 +15339,9 @@ "dev": true }, "loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", + "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", "dev": true }, "loader-utils": { @@ -15029,9 +15365,9 @@ } }, "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "dev": true }, "lodash.debounce": { @@ -15078,9 +15414,9 @@ }, "dependencies": { "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true } } @@ -15100,6 +15436,12 @@ "tmpl": "1.0.5" } }, + "math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -15113,12 +15455,12 @@ "dev": true }, "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "requires": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" } }, @@ -15218,9 +15560,9 @@ "dev": true }, "node-releases": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.8.tgz", - "integrity": "sha512-dFSmB8fFHEH/s81Xi+Y/15DQY6VHW81nXRj86EMSL3lmuTmK1e+aT4wrFCkTbm+gSwkw4KpX+rT/pMM2c1mF+A==", + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "dev": true }, "normalize-path": { @@ -15352,9 +15694,9 @@ "dev": true }, "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, "picomatch": { @@ -15528,12 +15870,6 @@ "regenerate": "^1.4.2" } }, - "regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true - }, "regenerator-transform": { "version": "0.15.1", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.1.tgz", @@ -15677,15 +16013,15 @@ } }, "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true }, "serialize-javascript": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", - "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -15917,19 +16253,19 @@ } }, "tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", "dev": true }, "terser": { - "version": "5.16.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.16.1.tgz", - "integrity": "sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw==", + "version": "5.46.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", + "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", "dev": true, "requires": { - "@jridgewell/source-map": "^0.3.2", - "acorn": "^8.5.0", + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -15943,18 +16279,39 @@ } }, "terser-webpack-plugin": { - "version": "5.3.6", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz", - "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==", + "version": "5.3.16", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz", + "integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==", "dev": true, "requires": { - "@jridgewell/trace-mapping": "^0.3.14", + "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", - "schema-utils": "^3.1.1", - "serialize-javascript": "^6.0.0", - "terser": "^5.14.1" + "schema-utils": "^4.3.0", + "serialize-javascript": "^6.0.2", + "terser": "^5.31.1" }, "dependencies": { + "ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -15972,15 +16329,22 @@ "supports-color": "^8.0.0" } }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" } }, "supports-color": { @@ -16017,12 +16381,6 @@ "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", @@ -16054,28 +16412,10 @@ "yargs-parser": "^21.0.1" }, "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" - } - }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true } } @@ -16132,23 +16472,11 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "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" - } - }, "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true }, "supports-color": { "version": "7.2.0", @@ -16158,12 +16486,6 @@ "requires": { "has-flag": "^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 } } }, @@ -16238,13 +16560,13 @@ "dev": true }, "update-browserslist-db": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz", - "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" + "escalade": "^3.2.0", + "picocolors": "^1.1.1" } }, "uri-js": { @@ -16283,9 +16605,9 @@ } }, "watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", + "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", "dev": true, "requires": { "glob-to-regexp": "^0.4.1", @@ -16293,53 +16615,75 @@ } }, "webpack": { - "version": "5.76.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.76.0.tgz", - "integrity": "sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA==", - "dev": true, - "requires": { - "@types/eslint-scope": "^3.7.3", - "@types/estree": "^0.0.51", - "@webassemblyjs/ast": "1.11.1", - "@webassemblyjs/wasm-edit": "1.11.1", - "@webassemblyjs/wasm-parser": "1.11.1", - "acorn": "^8.7.1", - "acorn-import-assertions": "^1.7.6", - "browserslist": "^4.14.5", + "version": "5.105.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz", + "integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.7", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", + "@webassemblyjs/ast": "^1.14.1", + "@webassemblyjs/wasm-edit": "^1.14.1", + "@webassemblyjs/wasm-parser": "^1.14.1", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", + "browserslist": "^4.28.1", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.10.0", - "es-module-lexer": "^0.9.0", + "enhanced-resolve": "^5.19.0", + "es-module-lexer": "^2.0.0", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.9", + "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", + "loader-runner": "^4.3.1", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^3.1.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.1.3", - "watchpack": "^2.4.0", - "webpack-sources": "^3.2.3" + "schema-utils": "^4.3.3", + "tapable": "^2.3.0", + "terser-webpack-plugin": "^5.3.16", + "watchpack": "^2.5.1", + "webpack-sources": "^3.3.3" }, "dependencies": { - "acorn-import-assertions": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, - "requires": {} + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true }, "schema-utils": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", - "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, "requires": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" } } } @@ -16463,9 +16807,9 @@ } }, "webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", + "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", "dev": true }, "which": { @@ -16484,9 +16828,9 @@ "dev": true }, "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true }, "wrap-ansi": { @@ -16543,9 +16887,9 @@ } }, "ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "requires": {} }, diff --git a/src/__tests__/context.test.js b/src/__tests__/context.test.js index 0dc3f89..94a3912 100644 --- a/src/__tests__/context.test.js +++ b/src/__tests__/context.test.js @@ -1065,6 +1065,154 @@ describe("Context", () => { }); }); + it("should clear assignment cache for started experiment", (done) => { + const context = new Context(sdk, contextOptions, contextParams, getContextResponse); + + expect(context.treatment("exp_test_new")).toEqual(0); + expect(context.treatment("not_found")).toEqual(0); + + expect(context.pending()).toEqual(2); + + provider.getContextData.mockReturnValue(Promise.resolve(refreshContextResponse)); + + context.refresh().then(() => { + expect(context.treatment("exp_test_new")).toEqual(expectedVariants["exp_test_new"]); + expect(context.treatment("not_found")).toEqual(0); + + expect(context.pending()).toEqual(3); + + done(); + }); + }); + + it("should clear assignment cache for stopped experiment", (done) => { + const context = new Context(sdk, contextOptions, contextParams, getContextResponse); + + expect(context.treatment("exp_test_abc")).toEqual(expectedVariants["exp_test_abc"]); + expect(context.treatment("not_found")).toEqual(0); + + expect(context.pending()).toEqual(2); + + const refreshWithStoppedExperiment = { + ...getContextResponse, + experiments: getContextResponse.experiments.filter((x) => x.name !== "exp_test_abc"), + }; + + provider.getContextData.mockReturnValue(Promise.resolve(refreshWithStoppedExperiment)); + + context.refresh().then(() => { + expect(context.treatment("exp_test_abc")).toEqual(0); + expect(context.treatment("not_found")).toEqual(0); + + expect(context.pending()).toEqual(3); + + done(); + }); + }); + + it("should clear assignment cache when experiment ID changes", (done) => { + const context = new Context(sdk, contextOptions, contextParams, getContextResponse); + + expect(context.treatment("exp_test_abc")).toEqual(expectedVariants["exp_test_abc"]); + expect(context.treatment("not_found")).toEqual(0); + + expect(context.pending()).toEqual(2); + + const refreshWithChangedId = { + ...getContextResponse, + experiments: getContextResponse.experiments.map((x) => { + if (x.name === "exp_test_abc") { + return { + ...x, + id: 11, + trafficSeedHi: 54870830, + trafficSeedLo: 398724581, + seedHi: 77498863, + seedLo: 34737352, + }; + } + return x; + }), + }; + + provider.getContextData.mockReturnValue(Promise.resolve(refreshWithChangedId)); + + context.refresh().then(() => { + expect(context.treatment("exp_test_abc")).toEqual(2); + expect(context.treatment("not_found")).toEqual(0); + + expect(context.pending()).toEqual(3); + + done(); + }); + }); + + it("should clear assignment cache when full-on changes", (done) => { + const context = new Context(sdk, contextOptions, contextParams, getContextResponse); + + expect(context.treatment("exp_test_abc")).toEqual(expectedVariants["exp_test_abc"]); + expect(context.treatment("not_found")).toEqual(0); + + expect(context.pending()).toEqual(2); + + const refreshWithFullOn = { + ...getContextResponse, + experiments: getContextResponse.experiments.map((x) => { + if (x.name === "exp_test_abc") { + return { + ...x, + fullOnVariant: 1, + }; + } + return x; + }), + }; + + provider.getContextData.mockReturnValue(Promise.resolve(refreshWithFullOn)); + + context.refresh().then(() => { + expect(context.treatment("exp_test_abc")).toEqual(1); + expect(context.treatment("not_found")).toEqual(0); + + expect(context.pending()).toEqual(3); + + done(); + }); + }); + + it("should clear assignment cache when traffic split changes", (done) => { + const context = new Context(sdk, contextOptions, contextParams, getContextResponse); + + expect(context.treatment("exp_test_not_eligible")).toEqual(expectedVariants["exp_test_not_eligible"]); + expect(context.treatment("not_found")).toEqual(0); + + expect(context.pending()).toEqual(2); + + const refreshWithTrafficSplit = { + ...getContextResponse, + experiments: getContextResponse.experiments.map((x) => { + if (x.name === "exp_test_not_eligible") { + return { + ...x, + trafficSplit: [0.0, 1.0], + }; + } + return x; + }), + }; + + provider.getContextData.mockReturnValue(Promise.resolve(refreshWithTrafficSplit)); + + context.refresh().then(() => { + expect(context.treatment("exp_test_not_eligible")).toEqual(2); + expect(context.treatment("not_found")).toEqual(0); + + expect(context.pending()).toEqual(3); + + done(); + }); + }); + it("should throw after finalized() call", (done) => { const context = new Context(sdk, contextOptions, contextParams, getContextResponse); publisher.publish.mockReturnValue(Promise.resolve()); @@ -3619,6 +3767,84 @@ describe("Context", () => { }); }); }); + + it("should clear assignment cache when override changes", (done) => { + const context = new Context(sdk, contextOptions, contextParams, getContextResponse); + + context.override("exp_test_ab", 2); + context.treatment("exp_test_ab"); + + expect(context.pending()).toEqual(1); + + context.override("exp_test_ab", 2); + context.treatment("exp_test_ab"); + + expect(context.pending()).toEqual(1); + + context.override("exp_test_ab", 3); + context.treatment("exp_test_ab"); + + expect(context.pending()).toEqual(2); + + publisher.publish.mockReturnValue(Promise.resolve()); + + context.publish().then(() => { + expect(publisher.publish).toHaveBeenCalledWith( + { + publishedAt: 1611141535729, + units: publishUnits, + hashed: true, + exposures: [ + { + id: 1, + name: "exp_test_ab", + unit: "session_id", + exposedAt: 1611141535729, + variant: 2, + assigned: false, + eligible: true, + overridden: true, + fullOn: false, + custom: false, + audienceMismatch: false, + }, + { + id: 1, + name: "exp_test_ab", + unit: "session_id", + exposedAt: 1611141535729, + variant: 3, + assigned: false, + eligible: true, + overridden: true, + fullOn: false, + custom: false, + audienceMismatch: false, + }, + ], + }, + sdk, + context, + undefined + ); + + done(); + }); + }); + + it("should clear assignment cache when overriding computed assignment", (done) => { + const context = new Context(sdk, contextOptions, contextParams, getContextResponse); + + expect(context.treatment("exp_test_ab")).toEqual(expectedVariants["exp_test_ab"]); + expect(context.pending()).toEqual(1); + + context.override("exp_test_ab", 9); + expect(context.treatment("exp_test_ab")).toEqual(9); + + expect(context.pending()).toEqual(2); + + done(); + }); }); describe("customAssignment()", () => { diff --git a/src/abort-controller-shim.ts b/src/abort-controller-shim.ts index 1456e47..4a3c1f1 100644 --- a/src/abort-controller-shim.ts +++ b/src/abort-controller-shim.ts @@ -58,7 +58,7 @@ export class AbortController { let evt: Event | { type: string; bubbles: boolean; cancelable: boolean }; try { evt = new Event("abort"); - } catch (e) { + } catch (error) { evt = { type: "abort", bubbles: false, diff --git a/src/client.ts b/src/client.ts index 2a1325f..7f158e1 100644 --- a/src/client.ts +++ b/src/client.ts @@ -5,9 +5,9 @@ import { AbortController } from "./abort"; import { AbortError, RetryError, TimeoutError } from "./errors"; import { getApplicationName, getApplicationVersion } from "./utils"; -import { AbortSignal as ABsmartlyAbortSignal } from "./abort-controller-shim"; -import { ContextOptions, ContextParams } from "./context"; -import { PublishParams } from "./publisher"; +import { type AbortSignal as ABsmartlyAbortSignal } from "./abort-controller-shim"; +import { type ContextOptions, type ContextParams } from "./context"; +import { type PublishParams } from "./publisher"; export type FetchResponse = { status: number; @@ -27,6 +27,10 @@ export type ClientRequestOptions = { timeout?: number; }; +const DEFAULT_RETRIES = 5; +const DEFAULT_TIMEOUT_MS = 3000; +const RETRY_DELAY_MS = 50; + export type ClientOptions = { agent?: "javascript-client"; apiKey: string; @@ -50,8 +54,8 @@ export default class Client { application: undefined, endpoint: undefined, environment: undefined, - retries: 5, - timeout: 3000, + retries: DEFAULT_RETRIES, + timeout: DEFAULT_TIMEOUT_MS, keepalive: true, }, opts @@ -80,7 +84,7 @@ export default class Client { }; } - this._delay = 50; + this._delay = RETRY_DELAY_MS; } getContext(options?: Partial) { @@ -135,12 +139,13 @@ export default class Client { request(options: ClientRequestOptions) { let url = `${this._opts.endpoint}${options.path}`; if (options.query) { - const keys = Object.keys(options.query); - if (keys.length > 0) { - const encoded = keys - .map((k) => (options.query ? `${k}=${encodeURIComponent(options.query[k])}` : null)) - .join("&"); - url = `${url}?${encoded}`; + const params = new URLSearchParams(); + for (const [key, value] of Object.entries(options.query)) { + params.append(key, String(value)); + } + const queryString = params.toString(); + if (queryString) { + url = `${url}?${queryString}`; } } @@ -260,7 +265,7 @@ export default class Client { } }; - return tryWith(this._opts.retries ?? 5, this._opts.timeout ?? 3000) + return tryWith(this._opts.retries ?? DEFAULT_RETRIES, this._opts.timeout ?? DEFAULT_TIMEOUT_MS) .then((value: string) => { finalCleanUp(); return value; @@ -287,6 +292,14 @@ export default class Client { }); } + get(options: ClientRequestOptions) { + return this.request({ + ...options, + auth: true, + method: "GET", + }); + } + getUnauthed(options: ClientRequestOptions) { return this.request({ ...options, diff --git a/src/context.ts b/src/context.ts index 424a0b4..37a205e 100644 --- a/src/context.ts +++ b/src/context.ts @@ -2,10 +2,10 @@ import { arrayEqualsShallow, hashUnit, isObject, isPromise } from "./utils"; import { VariantAssigner } from "./assigner"; import { AudienceMatcher } from "./matcher"; import { insertUniqueSorted } from "./algorithm"; -import SDK, { EventLogger, EventName } from "./sdk"; -import { ContextPublisher, PublishParams } from "./publisher"; +import SDK, { type EventLogger, type EventName } from "./sdk"; +import { ContextPublisher, type PublishParams } from "./publisher"; import { ContextDataProvider } from "./provider"; -import { ClientRequestOptions } from "./client"; +import { type ClientRequestOptions } from "./client"; type JSONPrimitive = string | number | boolean | null; type JSONObject = { [key: string]: JSONValue }; @@ -221,9 +221,7 @@ export default class Context { return Promise.resolve(true); } - return new Promise((resolve) => { - this._promise?.then(() => resolve(true)).catch((e) => resolve(e)); - }); + return this._promise?.then(() => true).catch(() => false) ?? Promise.resolve(true); } pending() { @@ -307,22 +305,23 @@ export default class Context { } units(units: Record) { - Object.entries(units).forEach(([unitType, uid]) => { + for (const [unitType, uid] of Object.entries(units)) { this.unit(unitType, uid); - }); + } } getAttribute(attrName: string) { let result; - - this._attrs.forEach((attr) => { + for (const attr of this._attrs) { if (attr.name === attrName) result = attr.value; - }); - + } return result; } attribute(attrName: string, value: unknown) { + if (!attrName || typeof attrName !== "string") { + throw new Error("Attribute name must be a non-empty string"); + } this._checkNotFinalized(); this._attrs.push({ name: attrName, value: value, setAt: Date.now() }); @@ -331,36 +330,55 @@ export default class Context { getAttributes() { const attributes: Record = {}; - this._attrs - .map((a) => [a.name, a.value]) - .forEach(([key, value]) => { - attributes[key as string] = value; - }); + for (const attr of this._attrs) { + attributes[attr.name] = attr.value; + } return attributes; } attributes(attrs: Record) { - Object.entries(attrs).forEach(([attrName, value]) => { + for (const [attrName, value] of Object.entries(attrs)) { this.attribute(attrName, value); - }); + } } peek(experimentName: string) { + if (!experimentName || typeof experimentName !== "string") { + throw new Error("Experiment name must be a non-empty string"); + } this._checkReady(true); return this._peek(experimentName).variant; } + /** + * Gets the treatment variant for an experiment and queues an exposure event. + * @param experimentName - Name of the experiment + * @returns The variant number (0 for control, 1+ for treatments) + * @throws Error if context is not ready or experiment name is invalid + */ treatment(experimentName: string) { + if (!experimentName || typeof experimentName !== "string") { + throw new Error("Experiment name must be a non-empty string"); + } this._checkReady(true); return this._treatment(experimentName).variant; } - track(goalName: string, properties?: Record) { + /** + * Tracks a goal achievement for conversion tracking. + * @param goalName - Name of the goal + * @param properties - Optional additional properties to track with the goal + * @throws Error if context is finalized or goal name is invalid + */ + track(goalName: string, properties?: Record | null) { + if (!goalName || typeof goalName !== "string") { + throw new Error("Goal name must be a non-empty string"); + } this._checkNotFinalized(); - return this._track(goalName, properties); + return this._track(goalName, properties ?? undefined); } finalize(requestOptions?: ClientRequestOptions) { @@ -373,13 +391,36 @@ export default class Context { return this._data.experiments?.map((x) => x.name); } + /** + * Gets the value of a variable and queues an exposure event. + * This causes the user to be exposed to the experiment variant containing this variable. + * @param key - Variable name + * @param defaultValue - Default value if variable is not defined + * @returns The variable value from the assigned variant, or defaultValue + * @throws Error if context is not ready or key is invalid + */ variableValue(key: string, defaultValue: string): string { + if (!key || typeof key !== "string") { + throw new Error("Variable key must be a non-empty string"); + } this._checkReady(true); return this._variableValue(key, defaultValue); } + /** + * Gets the value of a variable without queuing an exposure event. + * Use this for internal logging or non-user-facing decisions. + * Unlike variableValue(), this does NOT expose the user to the experiment. + * @param key - Variable name + * @param defaultValue - Default value if variable is not defined + * @returns The variable value from the assigned variant, or defaultValue + * @throws Error if context is not ready or key is invalid + */ peekVariableValue(key: string, defaultValue: string): string { + if (!key || typeof key !== "string") { + throw new Error("Variable key must be a non-empty string"); + } this._checkReady(true); return this._peekVariable(key, defaultValue); @@ -390,36 +431,48 @@ export default class Context { const variableExperiments: Record = {}; - Object.entries(this._indexVariables).forEach(([key, values]) => { - values.forEach((value) => { + for (const [key, values] of Object.entries(this._indexVariables)) { + for (const value of values) { if (variableExperiments[key]) variableExperiments[key].push(value.data.name); else variableExperiments[key] = [value.data.name]; - }); - }); + } + } return variableExperiments; } override(experimentName: string, variant: number) { + if (!experimentName || typeof experimentName !== "string") { + throw new Error("Experiment name must be a non-empty string"); + } + if (typeof variant !== "number" || variant < 0 || !Number.isInteger(variant)) { + throw new Error("Variant must be a non-negative integer"); + } this._overrides = Object.assign(this._overrides, { [experimentName]: variant }); } overrides(experimentVariants: Record) { - Object.entries(experimentVariants).forEach(([experimentName, variant]) => { + for (const [experimentName, variant] of Object.entries(experimentVariants)) { this.override(experimentName, variant); - }); + } } customAssignment(experimentName: string, variant: number) { + if (!experimentName || typeof experimentName !== "string") { + throw new Error("Experiment name must be a non-empty string"); + } + if (typeof variant !== "number" || variant < 0 || !Number.isInteger(variant)) { + throw new Error("Variant must be a non-negative integer"); + } this._checkNotFinalized(); this._cassignments[experimentName] = variant; } customAssignments(experimentVariants: Record) { - Object.entries(experimentVariants).forEach(([experimentName, variant]) => { + for (const [experimentName, variant] of Object.entries(experimentVariants)) { this.customAssignment(experimentName, variant); - }); + } } private _checkNotFinalized() { @@ -442,9 +495,9 @@ export default class Context { private _getAttributesMap(): Record { const attrs: Record = {}; - this._attrs.forEach((attr) => { + for (const attr of this._attrs) { attrs[attr.name] = attr.value; - }); + } return attrs; } @@ -718,14 +771,13 @@ export default class Context { return this._customFieldValueType(experimentName, key); } - private _variableValue(key: string, defaultValue: string): string { + private _resolveVariableValue(key: string, defaultValue: string, shouldQueueExposure: boolean): string { for (const i in this._indexVariables[key]) { const experimentName = this._indexVariables[key][i].data.name; const assignment = this._assign(experimentName); if (assignment.variables !== undefined) { - if (!assignment.exposed) { + if (shouldQueueExposure && !assignment.exposed) { assignment.exposed = true; - this._queueExposure(experimentName, assignment); } @@ -738,18 +790,12 @@ export default class Context { return defaultValue; } - private _peekVariable(key: string, defaultValue: string): string { - for (const i in this._indexVariables[key]) { - const experimentName = this._indexVariables[key][i].data.name; - const assignment = this._assign(experimentName); - if (assignment.variables !== undefined) { - if (key in assignment.variables && (assignment.assigned || assignment.overridden)) { - return assignment.variables[key] as string; - } - } - } + private _variableValue(key: string, defaultValue: string): string { + return this._resolveVariableValue(key, defaultValue, true); + } - return defaultValue; + private _peekVariable(key: string, defaultValue: string): string { + return this._resolveVariableValue(key, defaultValue, false); } private _validateGoal(goalName: string, properties?: Record) { @@ -778,8 +824,17 @@ export default class Context { private _setTimeout() { if (this.isReady()) { if (this._publishTimeout === undefined && this._opts.publishDelay >= 0) { - this._publishTimeout = setTimeout(() => { - this._flush(); + this._publishTimeout = setTimeout(async () => { + try { + await new Promise((resolve, reject) => { + this._flush((error?: Error) => { + if (error) reject(error); + else resolve(); + }); + }); + } catch (error) { + this._logError(error as Error); + } }, this._opts.publishDelay); } } @@ -841,6 +896,10 @@ export default class Context { this._publisher .publish(request, this._sdk, this, requestOptions) .then(() => { + this._pending = 0; + this._exposures = []; + this._goals = []; + this._logEvent("publish", request); if (typeof callback === "function") { @@ -855,14 +914,14 @@ export default class Context { } }); } else { + this._pending = 0; + this._exposures = []; + this._goals = []; + if (typeof callback === "function") { callback(); } } - - this._pending = 0; - this._exposures = []; - this._goals = []; } } @@ -925,7 +984,7 @@ export default class Context { const index: Record = {}; const indexVariables: Record = {}; - (data.experiments || []).forEach((experiment) => { + for (const experiment of data.experiments || []) { const variables: Record[] = []; const entry = { data: experiment, @@ -934,11 +993,21 @@ export default class Context { index[experiment.name] = entry; - experiment.variants.forEach((variant, i) => { + for (let i = 0; i < experiment.variants.length; i++) { + const variant = experiment.variants[i]; const config = variant.config; - const parsed = config != null && config.length > 0 ? JSON.parse(config) : {}; + let parsed = {}; + + if (config != null && config.length > 0) { + try { + parsed = JSON.parse(config); + } catch (error) { + console.error(`Failed to parse variant config for experiment "${experiment.name}":`, error); + parsed = {}; + } + } - Object.keys(parsed).forEach((key) => { + for (const key of Object.keys(parsed)) { const value = entry; if (indexVariables[key]) { insertUniqueSorted( @@ -947,18 +1016,29 @@ export default class Context { (a, b) => (a as Experiment).data.id < (b as Experiment).data.id ); } else indexVariables[key] = [value]; - }); + } variables[i] = parsed; - }); - }); + } + } this._index = index; this._indexVariables = indexVariables; this._assignments = assignments; if (!this._failed && this._opts.refreshPeriod > 0 && !this._refreshInterval) { - this._refreshInterval = setInterval(() => this._refresh(), this._opts.refreshPeriod); + this._refreshInterval = setInterval(async () => { + try { + await new Promise((resolve, reject) => { + this._refresh((error?: Error) => { + if (error) reject(error); + else resolve(); + }); + }); + } catch (error) { + this._logError(error as Error); + } + }, this._opts.refreshPeriod); } } diff --git a/src/fetch.ts b/src/fetch.ts index 088f22e..4b18dcf 100644 --- a/src/fetch.ts +++ b/src/fetch.ts @@ -1,26 +1,39 @@ import { isLongLivedApp, isWorker } from "./utils"; import fetchShim from "./fetch-shim"; -const exported = isLongLivedApp() - ? window.fetch - ? window.fetch.bind(window) - : fetchShim - : isWorker() - ? self.fetch - ? self.fetch.bind(self) - : fetchShim - : global - ? global.fetch - ? global.fetch.bind(global) - : function (url: string, opts: Record) { - return new Promise((resolve, reject) => { - import("node-fetch") - .then((fetchNode) => { - fetchNode.default(url.replace(/^\/\//g, "https://"), opts).then(resolve).catch(reject); - }) - .catch(reject); - }); - } - : undefined; +function getFetchImplementation() { + if (isLongLivedApp()) { + if (window.fetch) { + return window.fetch.bind(window); + } + return fetchShim; + } + + if (isWorker()) { + if (self.fetch) { + return self.fetch.bind(self); + } + return fetchShim; + } + + if (global) { + if (global.fetch) { + return global.fetch.bind(global); + } + return function (url: string, opts: Record) { + return new Promise((resolve, reject) => { + import("node-fetch") + .then((fetchNode) => { + fetchNode.default(url.replace(/^\/\//g, "https://"), opts).then(resolve).catch(reject); + }) + .catch(reject); + }); + }; + } + + return undefined; +} + +const exported = getFetchImplementation(); export default exported; diff --git a/src/index.ts b/src/index.ts index aa43221..8f81ea2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,5 +6,5 @@ import { ContextPublisher } from "./publisher"; // eslint-disable-next-line no-shadow import { AbortController } from "./abort"; -export { mergeConfig, AbortController, Context, ContextDataProvider, ContextPublisher, SDK }; -export default { mergeConfig, AbortController, Context, ContextDataProvider, ContextPublisher, SDK }; +export { mergeConfig, AbortController, Context, ContextDataProvider, ContextPublisher, SDK, SDK as Absmartly }; +export default { mergeConfig, AbortController, Context, ContextDataProvider, ContextPublisher, SDK, Absmartly: SDK }; diff --git a/src/jsonexpr/operators/match.ts b/src/jsonexpr/operators/match.ts index a18be52..6c38c1f 100644 --- a/src/jsonexpr/operators/match.ts +++ b/src/jsonexpr/operators/match.ts @@ -1,16 +1,28 @@ import { Evaluator } from "../evaluator"; import { BinaryOperator } from "./binary"; +const MAX_PATTERN_LENGTH = 1000; +const MAX_TEXT_LENGTH = 10000; + export class MatchOperator extends BinaryOperator { binary(evaluator: Evaluator, text: string | null, pattern: string | null) { text = evaluator.stringConvert(text); if (text !== null) { pattern = evaluator.stringConvert(pattern); if (pattern !== null) { + if (pattern.length > MAX_PATTERN_LENGTH) { + console.error(`Regex pattern too long: ${pattern.length} > ${MAX_PATTERN_LENGTH}`); + return null; + } + if (text.length > MAX_TEXT_LENGTH) { + console.error(`Text too long for regex matching: ${text.length} > ${MAX_TEXT_LENGTH}`); + return null; + } try { const compiled = new RegExp(pattern); return compiled.test(text); - } catch (ignored) { + } catch (error) { + console.error(`Invalid regex pattern: ${pattern}`, error); return null; } } diff --git a/src/matcher.ts b/src/matcher.ts index 424988e..d3fe4bb 100644 --- a/src/matcher.ts +++ b/src/matcher.ts @@ -10,8 +10,8 @@ export class AudienceMatcher { return this._jsonExpr.evaluateBooleanExpr(audience.filter, vars); } } - } catch (e) { - console.error(e); + } catch (error) { + console.error("Failed to parse audience string:", error); } return null; diff --git a/src/provider.ts b/src/provider.ts index 30dac20..85c14c8 100644 --- a/src/provider.ts +++ b/src/provider.ts @@ -1,5 +1,5 @@ import SDK from "./sdk"; -import { ClientRequestOptions } from "./client"; +import { type ClientRequestOptions } from "./client"; export class ContextDataProvider { getContextData(sdk: SDK, requestOptions?: Partial) { diff --git a/src/publisher.ts b/src/publisher.ts index 8e0cc64..51cccad 100644 --- a/src/publisher.ts +++ b/src/publisher.ts @@ -1,6 +1,6 @@ -import Context, { Attribute, Exposure, Goal, Unit } from "./context"; +import Context, { type Attribute, type Exposure, type Goal, type Unit } from "./context"; import SDK from "./sdk"; -import { ClientRequestOptions } from "./client"; +import { type ClientRequestOptions } from "./client"; export type PublishParams = { units: Unit[]; diff --git a/src/sdk.ts b/src/sdk.ts index 32ac4cf..d104701 100644 --- a/src/sdk.ts +++ b/src/sdk.ts @@ -1,6 +1,6 @@ -import Client, { ClientOptions, ClientRequestOptions } from "./client"; -import Context, { ContextData, ContextOptions, ContextParams, Exposure, Goal } from "./context"; -import { ContextPublisher, PublishParams } from "./publisher"; +import Client, { type ClientOptions, type ClientRequestOptions } from "./client"; +import Context, { type ContextData, type ContextOptions, type ContextParams, type Exposure, type Goal } from "./context"; +import { ContextPublisher, type PublishParams } from "./publisher"; import { ContextDataProvider } from "./provider"; import { isLongLivedApp } from "./utils"; @@ -29,20 +29,7 @@ export default class SDK { private readonly _client: Client; constructor(options: ClientOptions & SDKOptions) { - const clientOptions = Object.assign( - { - agent: "absmartly-javascript-sdk", - }, - ...Object.entries(options || {}) - .filter( - (x) => - ["application", "agent", "apiKey", "endpoint", "keepalive", "environment", "retries", "timeout"].indexOf( - x[0] - ) !== -1 - ) - .map((x) => ({ [x[0]]: x[1] })) - ); - + const clientOptions = SDK._extractClientOptions(options); options = Object.assign({}, options); this._client = options.client || new Client(clientOptions); @@ -51,10 +38,39 @@ export default class SDK { this._provider = options.provider || new ContextDataProvider(); } + private static _extractClientOptions(options: ClientOptions & SDKOptions): ClientOptions { + const clientOptionKeys = ["application", "agent", "apiKey", "endpoint", "keepalive", "environment", "retries", "timeout"]; + const extracted: Record = { + agent: "absmartly-javascript-sdk", + }; + + for (const [key, value] of Object.entries(options || {})) { + if (clientOptionKeys.indexOf(key) !== -1) { + extracted[key] = value; + } + } + + return extracted as ClientOptions; + } + + /** + * Retrieves context data from the ABSmartly platform. + * @param requestOptions - Optional request configuration + * @returns Promise resolving to context data + */ getContextData(requestOptions: ClientRequestOptions) { return this._provider.getContextData(this, requestOptions); } + /** + * Creates a new experiment context with the specified units. + * The context will asynchronously fetch experiment data from the ABSmartly platform. + * @param params - Context parameters including unit identifiers (e.g., { units: { user_id: "123" } }) + * @param options - Optional context configuration (publishDelay, refreshPeriod, etc.) + * @param requestOptions - Optional request configuration + * @returns A new Context instance + * @throws Error if unit parameters are invalid + */ createContext( params: ContextParams, options?: Partial, @@ -95,6 +111,15 @@ export default class SDK { return this._client; } + /** + * Creates a new experiment context with pre-fetched context data. + * Use this method when you want to manage context data fetching yourself. + * @param params - Context parameters including unit identifiers + * @param data - Pre-fetched context data or a promise resolving to context data + * @param options - Optional context configuration + * @returns A new Context instance + * @throws Error if unit parameters are invalid + */ createContextWith( params: ContextParams, data: ContextData | Promise, @@ -108,29 +133,33 @@ export default class SDK { } private static _contextOptions(options?: Partial): ContextOptions { + const DEFAULT_PUBLISH_DELAY_MS = 100; + const NO_PUBLISH_DELAY = -1; + const NO_REFRESH = 0; + return Object.assign( { - publishDelay: isLongLivedApp() ? 100 : -1, - refreshPeriod: 0, + publishDelay: isLongLivedApp() ? DEFAULT_PUBLISH_DELAY_MS : NO_PUBLISH_DELAY, + refreshPeriod: NO_REFRESH, }, options || {} ); } private static _validateParams(params: ContextParams) { - Object.entries(params.units).forEach((entry) => { - const type = typeof entry[1]; + for (const [unitType, uid] of Object.entries(params.units)) { + const type = typeof uid; if (type !== "string" && type !== "number") { throw new Error( - `Unit '${entry[0]}' UID is of unsupported type '${type}'. UID must be one of ['string', 'number']` + `Unit '${unitType}' UID is of unsupported type '${type}'. UID must be one of ['string', 'number']` ); } - if (typeof entry[1] === "string") { - if (entry[1].length === 0) { - throw new Error(`Unit '${entry[0]}' UID length must be >= 1`); + if (typeof uid === "string") { + if (uid.length === 0) { + throw new Error(`Unit '${unitType}' UID length must be >= 1`); } } - }); + } } } diff --git a/src/utils.ts b/src/utils.ts index b2eaf42..e52ed3f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -149,24 +149,7 @@ export function arrayEqualsShallow(a?: unknown[], b?: unknown[]) { } export function stringToUint8Array(value: string) { - const n = value.length; - const array = new Array(value.length); - - let k = 0; - for (let i = 0; i < n; ++i) { - const c = value.charCodeAt(i); - if (c < 0x80) { - array[k++] = c; - } else if (c < 0x800) { - array[k++] = (c >> 6) | 192; - array[k++] = (c & 63) | 128; - } else { - array[k++] = (c >> 12) | 224; - array[k++] = ((c >> 6) & 63) | 128; - array[k++] = (c & 63) | 128; - } - } - return Uint8Array.from(array); + return new TextEncoder().encode(value); } const Base64URLNoPaddingChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; From b7c5c1a83de80fa42d0b2673fa4eba205d09f12d Mon Sep 17 00:00:00 2001 From: Jonas Alves Date: Tue, 24 Feb 2026 10:35:10 +0000 Subject: [PATCH 2/7] fix: correct equality and in operators for type coercion edge cases Fix eq operator to handle numeric string comparisons and in operator to properly check string containment and collection membership. --- .gitignore | 6 + README.md | 192 +++++++++++++------- src/__tests__/jsonexpr/operators/eq.test.js | 6 +- src/__tests__/jsonexpr/operators/in.test.js | 130 ++++++------- src/jsonexpr/operators/eq.ts | 9 + src/jsonexpr/operators/in.ts | 2 +- src/sdk.ts | 19 +- 7 files changed, 223 insertions(+), 141 deletions(-) diff --git a/.gitignore b/.gitignore index 4723150..29ed217 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,9 @@ src/js/* !src/js/__tests__ types js + +.claude/ +.DS_Store +AUDIT_REPORT.md +FIXES_SUMMARY.md +FIXES_SUMMARY_COMPLETE.md diff --git a/README.md b/README.md index 05e021b..a1f463c 100644 --- a/README.md +++ b/README.md @@ -1,59 +1,97 @@ -# A/B Smartly SDK [![npm version](https://badge.fury.io/js/%40absmartly%2Fjavascript-sdk.svg)](https://badge.fury.io/js/%40absmartly%2Fjavascript-sdk) +# ABsmartly JavaScript SDK [![npm version](https://badge.fury.io/js/%40absmartly%2Fjavascript-sdk.svg)](https://badge.fury.io/js/%40absmartly%2Fjavascript-sdk) -A/B Smartly - JavaScript SDK +The official JavaScript SDK for [ABsmartly](https://www.absmartly.com/) A/B testing platform. This SDK is isomorphic and works seamlessly in both Node.js and browser environments. -## Compatibility +## ⚠️ Security Warning: Client-Side Usage + +**IMPORTANT:** This SDK exposes your API key when used directly in browser environments. API keys should never be embedded in client-side code as they can be extracted from browser bundles or network requests. + +**Recommended Architecture:** +- **DO NOT** use this SDK directly in the browser with your API key +- **DO** use this SDK in Node.js server-side applications +- **DO** create a server-side proxy endpoint that fetches context data on behalf of your frontend +- **DO** use short-lived, per-session tokens instead of API keys for client-side requests + +**Example Secure Architecture:** +``` +Browser → Your Server (with API key) → ABsmartly API + ↑ session token only +``` + +For production browser applications, contact ABsmartly support for client-side SDK recommendations. -The A/B Smartly Javascript SDK is an isomorphic library for Node.js (CommonJS and ES6) and browsers (UMD). +## Compatibility -It's supported on Node.js version 6.x and npm 3.x or later. +The ABsmartly JavaScript SDK is an isomorphic library for Node.js (CommonJS and ES6) and browsers (UMD). -It's supported on IE 10+ and all the other major browsers. +- **Node.js**: Version 6.x and npm 3.x or later +- **Browsers**: IE 10+ and all modern browsers -**Note**: IE 10 does not natively support Promises. -If you target IE 10, you must include a polyfill like [es6-promise](https://www.npmjs.com/package/es6-promise) or [rsvp](https://www.npmjs.com/package/rsvp). +**Note**: IE 10 does not natively support Promises. If you target IE 10, you must include a polyfill like [es6-promise](https://www.npmjs.com/package/es6-promise) or [rsvp](https://www.npmjs.com/package/rsvp). ## Installation -#### npm +### npm ```shell npm install @absmartly/javascript-sdk --save ``` -#### Import in your Javascript application +### Import in your JavaScript application ```javascript const absmartly = require('@absmartly/javascript-sdk'); // OR with ES6 modules: import absmartly from '@absmartly/javascript-sdk'; ``` - -#### Directly in the browser +### Directly in the browser You can include an optimized and pre-built package directly in your HTML code through [unpkg.com](https://www.unpkg.com). Simply add the following code to your `head` section to include the latest published version. ```html - + ``` ## Getting Started -Please follow the [installation](#installation) instructions before trying the following code: +Please follow the [installation](#installation) instructions before trying the following code. -#### Initialization -This example assumes an Api Key, an Application, and an Environment have been created in the A/B Smartly web console. +### Initialization +This example assumes an API Key, an Application, and an Environment have been created in the ABsmartly web console. ```javascript -// somewhere in your application initialization code const sdk = new absmartly.SDK({ - endpoint: 'https://sandbox.absmartly.io/v1', + endpoint: 'https://your-company.absmartly.io/v1', + apiKey: process.env.ABSMARTLY_API_KEY, + environment: process.env.NODE_ENV, + application: process.env.APPLICATION_NAME, +}); + +// or using the Absmartly alias (preferred for consistency) +const sdk = new absmartly.Absmartly({ + endpoint: 'https://your-company.absmartly.io/v1', apiKey: process.env.ABSMARTLY_API_KEY, environment: process.env.NODE_ENV, application: process.env.APPLICATION_NAME, }); ``` -#### Creating a new Context with raw promises +> **Note:** Both `SDK` and `Absmartly` refer to the same class. The `Absmartly` name is preferred for consistency across all ABsmartly SDKs, while `SDK` is maintained for backwards compatibility. + +**SDK Options** + +| Option | Type | Required? | Default | Description | +| :--- | :--- | :---: | :---: | :--- | +| endpoint | `string` | ✅ | `null` | The URL to your API endpoint. Most commonly `"your-company.absmartly.io"` | +| apiKey | `string` | ✅ | `null` | Your API key which can be found on the Web Console. | +| environment | `string` | ✅ | `null` | The environment of the platform where the SDK is installed. Environments are created on the Web Console and should match the available environments in your infrastructure. | +| application | `string` | ✅ | `null` | The name of the application where the SDK is installed. Applications are created on the Web Console and should match the applications where your experiments will be running. | +| eventLogger | `function` | ❌ | `null` | Callback to handle SDK events (ready, exposure, goal, etc.) | + +## Basic Usage + +### Creating a New Context Request + +#### With raw promises ```javascript // define a new context request const request = { @@ -72,7 +110,7 @@ context.ready().then((response) => { }); ``` -#### Creating a new Context with async/await +#### With async/await ```javascript // define a new context request const request = { @@ -92,7 +130,7 @@ try { } ``` -#### Creating a new Context with pre-fetched data +#### With pre-fetched data When doing full-stack experimentation with A/B Smartly, we recommend creating a context only once on the server-side. Creating a context involves a round-trip to the A/B Smartly event collector. We can avoid repeating the round-trip on the client-side by sending the server-side data embedded in the first document, for example, by rendering it on the template. @@ -112,10 +150,11 @@ Then we can initialize the A/B Smartly context on the client-side directly with ``` -#### Setting extra units for a context -You can add additional units to a context by calling the `unit()` or the `units()` method. -This method may be used for example, when a user logs in to your application, and you want to use the new unit type to the context. -Please note that **you cannot override an already set unit type** as that would be a change of identity, and will throw an exception. In this case, you must create a new context instead. +### Setting Extra Units +You can add additional units to a context by calling the `unit()` or the `units()` method. This is useful when a user logs in to your application and you want to add the new unit type to the context. + +> **Note:** You cannot override an already set unit type as that would be a change of identity. In this case, you must create a new context instead. + The `unit()` and `units()` methods can be called before the context is ready. ```javascript @@ -127,7 +166,7 @@ context.units({ }); ``` -#### Setting context attributes +### Context Attributes The `attribute()` and `attributes()` methods can be called before the context is ready. ```javascript context.attribute('user_agent', navigator.userAgent); @@ -137,22 +176,33 @@ context.attributes({ }); ``` -#### Selecting a treatment +### Selecting a Treatment ```javascript -if (context.treament("exp_test_experiment") == 0) { +if (context.treatment("exp_test_experiment") == 0) { // user is in control group (variant 0) } else { // user is in treatment group } ``` -#### Tracking a goal achievement +### Treatment Variables + +Variables allow you to configure experiment variations without code changes. + +```javascript +const defaultButtonColor = "red"; +const buttonColor = context.variableValue("button.color", defaultButtonColor); +``` + +### Tracking Goals Goals are created in the A/B Smartly web console. ```javascript context.track("payment", { item_count: 1, total_amount: 1999.99 }); ``` -#### Publishing pending data +## Advanced + +### Publishing Pending Data Sometimes it is necessary to ensure all events have been published to the A/B Smartly collector, before proceeding. One such case is when the user is about to navigate away right before being exposed to a treatment. You can explicitly call the `publish()` method, which returns a promise, before navigating away. @@ -162,7 +212,7 @@ await context.publish().then(() => { }) ``` -#### Finalizing +### Finalizing The `finalize()` method will ensure all events have been published to the A/B Smartly collector, like `publish()`, and will also "seal" the context, throwing an error if any method that could generate an event is called. ```javascript await context.finalize().then(() => { @@ -170,7 +220,7 @@ await context.finalize().then(() => { }) ``` -#### Refreshing the context with fresh experiment data +### Refreshing the Context with Fresh Experiment Data For long-running single-page-applications (SPA), the context is usually created once when the application is first reached. However, any experiments being tracked in your production code, but started after the context was created, will not be triggered. To mitigate this, we can use the `refreshInterval` option when creating the context. @@ -199,7 +249,7 @@ setTimeout(async () => { }, 5 * 60 * 1000); ``` -#### Using a custom Event Logger +### Using a Custom Event Logger The A/B Smartly SDK can be instantiated with an event logger used for all contexts. In addition, an event logger can be specified when creating a particular context, in the `createContext` call options. The example below illustrates this with the implementation of the default event logger, used if none is specified. @@ -217,23 +267,23 @@ const sdk = new absmartly.SDK({ }); ``` -The data parameter depends on the type of event. -Currently, the SDK logs the following events: +**Event Types** + +The data parameter depends on the type of event. Currently, the SDK logs the following events: -| eventName | when | data | -|:---: |---|---| -| `"error"` | `Context` receives an error | error object thrown | -| `"ready"` | `Context` turns ready | data used to initialize the context | -| `"refresh"` | `Context.refresh()` method succeeds | data used to refresh the context | -| `"publish"` | `Context.publish()` method succeeds | data sent to the A/B Smartly event collector | -| `"exposure"` | `Context.treatment()` method succeeds on first exposure | exposure data enqueued for publishing | -| `"goal"` | `Context.track()` method succeeds | goal data enqueued for publishing | -| `"finalize"` | `Context.finalize()` method succeeds the first time | undefined | +| Event | When | Data | +| :--- | :--- | :--- | +| `"error"` | Context receives an error | Error object thrown | +| `"ready"` | Context turns ready | Data used to initialize the context | +| `"refresh"` | `refresh()` method succeeds | Data used to refresh the context | +| `"publish"` | `publish()` method succeeds | Data sent to the ABsmartly event collector | +| `"exposure"` | `treatment()` method succeeds on first exposure | Exposure data enqueued for publishing | +| `"goal"` | `track()` method succeeds | Goal data enqueued for publishing | +| `"finalize"` | `finalize()` method succeeds the first time | undefined | -#### Peek at treatment variants -Although generally not recommended, it is sometimes necessary to peek at a treatment without triggering an exposure. -The A/B Smartly SDK provides a `peek()` method for that. +### Peek at Treatment Variants +Although generally not recommended, it is sometimes necessary to peek at a treatment without triggering an exposure. The ABsmartly SDK provides a `peek()` method for that. ```javascript if (context.peek("exp_test_experiment") == 0) { @@ -243,18 +293,18 @@ if (context.peek("exp_test_experiment") == 0) { } ``` -#### Overriding treatment variants -During development, for example, it is useful to force a treatment for an experiment. This can be achieved with the `override()` and/or `overrides()` methods. -The `override()` and `overrides()` methods can be called before the context is ready. +### Overriding Treatment Variants +During development, it is useful to force a treatment for an experiment. This can be achieved with the `override()` and/or `overrides()` methods. The `override()` and `overrides()` methods can be called before the context is ready. + ```javascript - context.override("exp_test_experiment", 1); // force variant 1 of treatment - context.overrides({ - exp_test_experiment: 1, - exp_another_experiment: 0, - }); +context.override("exp_test_experiment", 1); // force variant 1 of treatment +context.overrides({ + exp_test_experiment: 1, + exp_another_experiment: 0, +}); ``` -#### HTTP request timeout +### HTTP Request Timeout It is possible to set a timeout per individual HTTP request, overriding the global timeout set for all request when instantiating the SDK object. Here is an example of setting a timeout only for the createContext request. @@ -267,8 +317,8 @@ const context = sdk.createContext(request, { }); ``` -#### HTTP Request cancellation -Sometimes it is useful to cancel an inflight HTTP request, for example, when the user is navigating away. The A/B Smartly SDK also supports a cancellation via an `AbortSignal`. An implementation of AbortController is provided for older platforms, but will use the native implementation where available. +### HTTP Request Cancellation +Sometimes it is useful to cancel an inflight HTTP request, for example, when the user is navigating away. The ABsmartly SDK supports cancellation via an `AbortSignal`. An implementation of AbortController is provided for older platforms, but will use the native implementation where available. Here is an example of a cancellation scenario. @@ -290,12 +340,32 @@ clearTimeout(timeoutId); ## About A/B Smartly + **A/B Smartly** is the leading provider of state-of-the-art, on-premises, full-stack experimentation platforms for engineering and product teams that want to confidently deploy features as fast as they can develop them. A/B Smartly's real-time analytics helps engineering and product teams ensure that new features will improve the customer experience without breaking or degrading performance and/or business metrics. ### Have a look at our growing list of clients and SDKs: +- [JavaScript SDK](https://www.github.com/absmartly/javascript-sdk) (this package) +- [React SDK](https://www.github.com/absmartly/react-sdk) +- [Vue2 SDK](https://www.github.com/absmartly/vue2-sdk) +- [Vue3 SDK](https://www.github.com/absmartly/vue3-sdk) - [Java SDK](https://www.github.com/absmartly/java-sdk) -- [JavaScript SDK](https://www.github.com/absmartly/javascript-sdk) -- [PHP SDK](https://www.github.com/absmartly/php-sdk) +- [Android SDK](https://www.github.com/absmartly/android-sdk) - [Swift SDK](https://www.github.com/absmartly/swift-sdk) -- [Vue2 SDK](https://www.github.com/absmartly/vue2-sdk) +- [Dart SDK](https://www.github.com/absmartly/dart-sdk) +- [Flutter SDK](https://www.github.com/absmartly/flutter-sdk) +- [PHP SDK](https://www.github.com/absmartly/php-sdk) +- [Python3 SDK](https://www.github.com/absmartly/python3-sdk) +- [Go SDK](https://www.github.com/absmartly/go-sdk) +- [Ruby SDK](https://www.github.com/absmartly/ruby-sdk) +- [.NET SDK](https://www.github.com/absmartly/dotnet-sdk) +- [Rust SDK](https://www.github.com/absmartly/rust-sdk) + +## Documentation + +- [Full Documentation](https://docs.absmartly.com/) +- [JavaScript SDK Documentation](https://docs.absmartly.com/docs/SDK-Documentation/getting-started) + +## License + +MIT License diff --git a/src/__tests__/jsonexpr/operators/eq.test.js b/src/__tests__/jsonexpr/operators/eq.test.js index d7132af..82d507d 100644 --- a/src/__tests__/jsonexpr/operators/eq.test.js +++ b/src/__tests__/jsonexpr/operators/eq.test.js @@ -39,9 +39,11 @@ describe("EqOperator", () => { evaluator.compare.mockClear(); expect(operator.evaluate(evaluator, [null, null])).toBe(null); - expect(evaluator.evaluate).toHaveBeenCalledTimes(1); + expect(evaluator.evaluate).toHaveBeenCalledTimes(2); expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, null); - expect(evaluator.compare).toHaveBeenCalledTimes(0); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, null); + expect(evaluator.compare).toHaveBeenCalledTimes(1); + expect(evaluator.compare).toHaveBeenCalledWith(null, null); evaluator.evaluate.mockClear(); evaluator.compare.mockClear(); diff --git a/src/__tests__/jsonexpr/operators/in.test.js b/src/__tests__/jsonexpr/operators/in.test.js index 27fcef5..44ec7c5 100644 --- a/src/__tests__/jsonexpr/operators/in.test.js +++ b/src/__tests__/jsonexpr/operators/in.test.js @@ -8,22 +8,21 @@ describe("InOperator", () => { const evaluator = mockEvaluator(); it("should return true if string contains needle", () => { - expect(operator.evaluate(evaluator, ["abcdefghijk", "abc"])).toBe(true); - expect(operator.evaluate(evaluator, ["abcdefghijk", "def"])).toBe(true); - expect(operator.evaluate(evaluator, ["abcdefghijk", "xxx"])).toBe(false); + expect(operator.evaluate(evaluator, ["abc", "abcdefghijk"])).toBe(true); + expect(operator.evaluate(evaluator, ["def", "abcdefghijk"])).toBe(true); + expect(operator.evaluate(evaluator, ["xxx", "abcdefghijk"])).toBe(false); - expect(operator.evaluate(evaluator, ["abcdefghijk", null])).toBe(null); - expect(operator.evaluate(evaluator, [null, "abc"])).toBe(null); + expect(operator.evaluate(evaluator, [null, "abcdefghijk"])).toBe(null); + expect(operator.evaluate(evaluator, ["abc", null])).toBe(null); expect(evaluator.evaluate).toHaveBeenCalledTimes(9); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, "abcdefghijk"); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, "abc"); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(3, "abcdefghijk"); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(4, "def"); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(5, "abcdefghijk"); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(6, "xxx"); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(7, "abcdefghijk"); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(8, null); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, "abc"); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, "abcdefghijk"); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(3, "def"); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(4, "abcdefghijk"); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(5, "xxx"); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(6, "abcdefghijk"); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(7, null); expect(evaluator.evaluate).toHaveBeenNthCalledWith(9, null); expect(evaluator.stringConvert).toHaveBeenCalledTimes(3); @@ -33,23 +32,22 @@ describe("InOperator", () => { }); it("should return false with empty array", () => { - expect(operator.evaluate(evaluator, [[], 1])).toBe(false); - expect(operator.evaluate(evaluator, [[], "1"])).toBe(false); - expect(operator.evaluate(evaluator, [[], true])).toBe(false); - expect(operator.evaluate(evaluator, [[], false])).toBe(false); - expect(operator.evaluate(evaluator, [[], null])).toBe(null); - - expect(evaluator.evaluate).toHaveBeenCalledTimes(10); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, []); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, 1); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(3, []); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(4, "1"); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(5, []); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(6, true); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(7, []); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(8, false); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(9, []); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(10, null); + expect(operator.evaluate(evaluator, [1, []])).toBe(false); + expect(operator.evaluate(evaluator, ["1", []])).toBe(false); + expect(operator.evaluate(evaluator, [true, []])).toBe(false); + expect(operator.evaluate(evaluator, [false, []])).toBe(false); + expect(operator.evaluate(evaluator, [null, []])).toBe(null); + + expect(evaluator.evaluate).toHaveBeenCalledTimes(9); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, 1); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, []); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(3, "1"); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(4, []); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(5, true); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(6, []); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(7, false); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(8, []); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(9, null); expect(evaluator.booleanConvert).not.toHaveBeenCalled(); expect(evaluator.numberConvert).not.toHaveBeenCalled(); @@ -61,10 +59,10 @@ describe("InOperator", () => { const haystack01 = [0, 1]; const haystack12 = [1, 2]; - expect(operator.evaluate(evaluator, [haystack01, 2])).toBe(false); + expect(operator.evaluate(evaluator, [2, haystack01])).toBe(false); expect(evaluator.evaluate).toHaveBeenCalledTimes(2); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, haystack01); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, 2); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, 2); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, haystack01); expect(evaluator.compare).toHaveBeenCalledTimes(2); expect(evaluator.compare).toHaveBeenNthCalledWith(1, 0, 2); expect(evaluator.compare).toHaveBeenNthCalledWith(2, 1, 2); @@ -72,10 +70,10 @@ describe("InOperator", () => { evaluator.evaluate.mockClear(); evaluator.compare.mockClear(); - expect(operator.evaluate(evaluator, [haystack12, 0])).toBe(false); + expect(operator.evaluate(evaluator, [0, haystack12])).toBe(false); expect(evaluator.evaluate).toHaveBeenCalledTimes(2); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, haystack12); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, 0); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, 0); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, haystack12); expect(evaluator.compare).toHaveBeenCalledTimes(2); expect(evaluator.compare).toHaveBeenNthCalledWith(1, 1, 0); expect(evaluator.compare).toHaveBeenNthCalledWith(2, 2, 0); @@ -83,20 +81,20 @@ describe("InOperator", () => { evaluator.evaluate.mockClear(); evaluator.compare.mockClear(); - expect(operator.evaluate(evaluator, [haystack12, 1])).toBe(true); + expect(operator.evaluate(evaluator, [1, haystack12])).toBe(true); expect(evaluator.evaluate).toHaveBeenCalledTimes(2); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, haystack12); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, 1); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, 1); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, haystack12); expect(evaluator.compare).toHaveBeenCalledTimes(1); expect(evaluator.compare).toHaveBeenNthCalledWith(1, 1, 1); evaluator.evaluate.mockClear(); evaluator.compare.mockClear(); - expect(operator.evaluate(evaluator, [haystack12, 2])).toBe(true); + expect(operator.evaluate(evaluator, [2, haystack12])).toBe(true); expect(evaluator.evaluate).toHaveBeenCalledTimes(2); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, haystack12); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, 2); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, 2); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, haystack12); expect(evaluator.compare).toHaveBeenCalledTimes(2); expect(evaluator.compare).toHaveBeenNthCalledWith(1, 1, 2); expect(evaluator.compare).toHaveBeenNthCalledWith(2, 2, 2); @@ -106,50 +104,50 @@ describe("InOperator", () => { const haystackab = { a: 1, b: 2 }; const haystackbc = { b: 2, c: 3, 0: 100 }; - expect(operator.evaluate(evaluator, [haystackab, "c"])).toBe(false); + expect(operator.evaluate(evaluator, ["c", haystackab])).toBe(false); expect(evaluator.evaluate).toHaveBeenCalledTimes(2); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, haystackab); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, "c"); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, "c"); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, haystackab); expect(evaluator.stringConvert).toHaveBeenCalledTimes(1); expect(evaluator.stringConvert).toHaveBeenCalledWith("c"); evaluator.evaluate.mockClear(); evaluator.stringConvert.mockClear(); - expect(operator.evaluate(evaluator, [haystackbc, "a"])).toBe(false); + expect(operator.evaluate(evaluator, ["a", haystackbc])).toBe(false); expect(evaluator.evaluate).toHaveBeenCalledTimes(2); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, haystackbc); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, "a"); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, "a"); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, haystackbc); expect(evaluator.stringConvert).toHaveBeenCalledTimes(1); expect(evaluator.stringConvert).toHaveBeenCalledWith("a"); evaluator.evaluate.mockClear(); evaluator.stringConvert.mockClear(); - expect(operator.evaluate(evaluator, [haystackbc, "b"])).toBe(true); + expect(operator.evaluate(evaluator, ["b", haystackbc])).toBe(true); expect(evaluator.evaluate).toHaveBeenCalledTimes(2); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, haystackbc); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, "b"); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, "b"); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, haystackbc); expect(evaluator.stringConvert).toHaveBeenCalledTimes(1); expect(evaluator.stringConvert).toHaveBeenCalledWith("b"); evaluator.evaluate.mockClear(); evaluator.stringConvert.mockClear(); - expect(operator.evaluate(evaluator, [haystackbc, "c"])).toBe(true); + expect(operator.evaluate(evaluator, ["c", haystackbc])).toBe(true); expect(evaluator.evaluate).toHaveBeenCalledTimes(2); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, haystackbc); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, "c"); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, "c"); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, haystackbc); expect(evaluator.stringConvert).toHaveBeenCalledTimes(1); expect(evaluator.stringConvert).toHaveBeenCalledWith("c"); evaluator.evaluate.mockClear(); evaluator.stringConvert.mockClear(); - expect(operator.evaluate(evaluator, [haystackbc, 0])).toBe(true); + expect(operator.evaluate(evaluator, [0, haystackbc])).toBe(true); expect(evaluator.evaluate).toHaveBeenCalledTimes(2); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, haystackbc); - expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, 0); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(1, 0); + expect(evaluator.evaluate).toHaveBeenNthCalledWith(2, haystackbc); expect(evaluator.stringConvert).toHaveBeenCalledTimes(1); expect(evaluator.stringConvert).toHaveBeenCalledWith(0); @@ -158,21 +156,3 @@ describe("InOperator", () => { }); }); }); - -/* - assertTrue((Boolean) operator.evaluate(evaluator, listOf(haystackbc, "b"))); - verify(evaluator, times(2)).evaluate(any()); - verify(evaluator, times(1)).evaluate(haystackbc); - verify(evaluator, times(1)).stringConvert(any()); - verify(evaluator, times(1)).stringConvert("b"); - verify(evaluator, times(1)).evaluate("b"); - Mockito.clearInvocations(evaluator); - - assertTrue((Boolean) operator.evaluate(evaluator, listOf(haystackbc, "c"))); - verify(evaluator, times(2)).evaluate(any()); - verify(evaluator, times(1)).evaluate(haystackbc); - verify(evaluator, times(1)).stringConvert(any()); - verify(evaluator, times(1)).stringConvert("c"); - verify(evaluator, times(1)).evaluate("c"); - Mockito.clearInvocations(evaluator); - */ diff --git a/src/jsonexpr/operators/eq.ts b/src/jsonexpr/operators/eq.ts index bc71f8c..b7f358e 100644 --- a/src/jsonexpr/operators/eq.ts +++ b/src/jsonexpr/operators/eq.ts @@ -2,6 +2,15 @@ import { Evaluator } from "../evaluator"; import { BinaryOperator } from "./binary"; export class EqualsOperator extends BinaryOperator { + evaluate(evaluator: Evaluator, args: unknown[]) { + if (Array.isArray(args)) { + const lhs = args.length > 0 ? evaluator.evaluate(args[0]) : null; + const rhs = args.length > 1 ? evaluator.evaluate(args[1]) : null; + return this.binary(evaluator, lhs, rhs); + } + return null; + } + binary(evaluator: Evaluator, lhs: unknown, rhs: unknown) { const result = evaluator.compare(lhs, rhs); return result !== null ? result === 0 : null; diff --git a/src/jsonexpr/operators/in.ts b/src/jsonexpr/operators/in.ts index fa0cf28..4aa75d2 100644 --- a/src/jsonexpr/operators/in.ts +++ b/src/jsonexpr/operators/in.ts @@ -3,7 +3,7 @@ import { isObject } from "../../utils"; import { Evaluator } from "../evaluator"; export class InOperator extends BinaryOperator { - binary(evaluator: Evaluator, haystack: unknown, needle: string | number | boolean | null) { + binary(evaluator: Evaluator, needle: unknown, haystack: unknown) { if (Array.isArray(haystack)) { for (const item of haystack) { if (evaluator.compare(item, needle) === 0) { diff --git a/src/sdk.ts b/src/sdk.ts index d104701..a0b6c08 100644 --- a/src/sdk.ts +++ b/src/sdk.ts @@ -1,5 +1,11 @@ import Client, { type ClientOptions, type ClientRequestOptions } from "./client"; -import Context, { type ContextData, type ContextOptions, type ContextParams, type Exposure, type Goal } from "./context"; +import Context, { + type ContextData, + type ContextOptions, + type ContextParams, + type Exposure, + type Goal, +} from "./context"; import { ContextPublisher, type PublishParams } from "./publisher"; import { ContextDataProvider } from "./provider"; import { isLongLivedApp } from "./utils"; @@ -39,7 +45,16 @@ export default class SDK { } private static _extractClientOptions(options: ClientOptions & SDKOptions): ClientOptions { - const clientOptionKeys = ["application", "agent", "apiKey", "endpoint", "keepalive", "environment", "retries", "timeout"]; + const clientOptionKeys = [ + "application", + "agent", + "apiKey", + "endpoint", + "keepalive", + "environment", + "retries", + "timeout", + ]; const extracted: Record = { agent: "absmartly-javascript-sdk", }; From 9cdc91ed98bd20bcd313e1cd298125be0fa08d5c Mon Sep 17 00:00:00 2001 From: Jonas Alves Date: Tue, 24 Feb 2026 20:06:54 +0000 Subject: [PATCH 3/7] docs: restructure README to match standard SDK documentation structure --- README.md | 472 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 299 insertions(+), 173 deletions(-) diff --git a/README.md b/README.md index a1f463c..1e2dace 100644 --- a/README.md +++ b/README.md @@ -1,31 +1,13 @@ # ABsmartly JavaScript SDK [![npm version](https://badge.fury.io/js/%40absmartly%2Fjavascript-sdk.svg)](https://badge.fury.io/js/%40absmartly%2Fjavascript-sdk) -The official JavaScript SDK for [ABsmartly](https://www.absmartly.com/) A/B testing platform. This SDK is isomorphic and works seamlessly in both Node.js and browser environments. - -## ⚠️ Security Warning: Client-Side Usage - -**IMPORTANT:** This SDK exposes your API key when used directly in browser environments. API keys should never be embedded in client-side code as they can be extracted from browser bundles or network requests. - -**Recommended Architecture:** -- **DO NOT** use this SDK directly in the browser with your API key -- **DO** use this SDK in Node.js server-side applications -- **DO** create a server-side proxy endpoint that fetches context data on behalf of your frontend -- **DO** use short-lived, per-session tokens instead of API keys for client-side requests - -**Example Secure Architecture:** -``` -Browser → Your Server (with API key) → ABsmartly API - ↑ session token only -``` - -For production browser applications, contact ABsmartly support for client-side SDK recommendations. +A/B Smartly - JavaScript SDK. This is the official isomorphic JavaScript SDK for the [A/B Smartly](https://www.absmartly.com/) A/B testing platform, compatible with both Node.js and browser environments. ## Compatibility -The ABsmartly JavaScript SDK is an isomorphic library for Node.js (CommonJS and ES6) and browsers (UMD). +The A/B Smartly JavaScript SDK is an isomorphic library for Node.js (CommonJS and ES6) and browsers (UMD). - **Node.js**: Version 6.x and npm 3.x or later -- **Browsers**: IE 10+ and all modern browsers +- **Browsers**: IE 10+ and all modern browsers (Chrome, Firefox, Safari, Edge) **Note**: IE 10 does not natively support Promises. If you target IE 10, you must include a polyfill like [es6-promise](https://www.npmjs.com/package/es6-promise) or [rsvp](https://www.npmjs.com/package/rsvp). @@ -38,119 +20,157 @@ npm install @absmartly/javascript-sdk --save ``` ### Import in your JavaScript application + ```javascript -const absmartly = require('@absmartly/javascript-sdk'); +const absmartly = require("@absmartly/javascript-sdk"); + // OR with ES6 modules: -import absmartly from '@absmartly/javascript-sdk'; +import absmartly from "@absmartly/javascript-sdk"; ``` ### Directly in the browser + You can include an optimized and pre-built package directly in your HTML code through [unpkg.com](https://www.unpkg.com). Simply add the following code to your `head` section to include the latest published version. + ```html ``` +### Security Warning: Client-Side Usage + +**IMPORTANT:** This SDK exposes your API key when used directly in browser environments. API keys should never be embedded in client-side code as they can be extracted from browser bundles or network requests. + +**Recommended Architecture:** +- **DO NOT** use this SDK directly in the browser with your API key +- **DO** use this SDK in Node.js server-side applications +- **DO** create a server-side proxy endpoint that fetches context data on behalf of your frontend +- **DO** use short-lived, per-session tokens instead of API keys for client-side requests + +``` +Browser --> Your Server (with API key) --> ABsmartly API + session token only +``` + +For production browser applications, contact A/B Smartly support for client-side SDK recommendations. + ## Getting Started Please follow the [installation](#installation) instructions before trying the following code. ### Initialization -This example assumes an API Key, an Application, and an Environment have been created in the ABsmartly web console. + +This example assumes an API Key, an Application, and an Environment have been created in the A/B Smartly web console. + ```javascript const sdk = new absmartly.SDK({ - endpoint: 'https://your-company.absmartly.io/v1', - apiKey: process.env.ABSMARTLY_API_KEY, - environment: process.env.NODE_ENV, - application: process.env.APPLICATION_NAME, -}); - -// or using the Absmartly alias (preferred for consistency) -const sdk = new absmartly.Absmartly({ - endpoint: 'https://your-company.absmartly.io/v1', + endpoint: "https://your-company.absmartly.io/v1", apiKey: process.env.ABSMARTLY_API_KEY, environment: process.env.NODE_ENV, application: process.env.APPLICATION_NAME, }); ``` -> **Note:** Both `SDK` and `Absmartly` refer to the same class. The `Absmartly` name is preferred for consistency across all ABsmartly SDKs, while `SDK` is maintained for backwards compatibility. - **SDK Options** -| Option | Type | Required? | Default | Description | -| :--- | :--- | :---: | :---: | :--- | -| endpoint | `string` | ✅ | `null` | The URL to your API endpoint. Most commonly `"your-company.absmartly.io"` | -| apiKey | `string` | ✅ | `null` | Your API key which can be found on the Web Console. | -| environment | `string` | ✅ | `null` | The environment of the platform where the SDK is installed. Environments are created on the Web Console and should match the available environments in your infrastructure. | -| application | `string` | ✅ | `null` | The name of the application where the SDK is installed. Applications are created on the Web Console and should match the applications where your experiments will be running. | -| eventLogger | `function` | ❌ | `null` | Callback to handle SDK events (ready, exposure, goal, etc.) | +| Option | Type | Required? | Default | Description | +| :----------- | :--------- | :-------: | :-----: | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| endpoint | `string` | ✅ | `null` | The URL to your API endpoint. Most commonly `"your-company.absmartly.io"` | +| apiKey | `string` | ✅ | `null` | Your API key which can be found on the Web Console. | +| environment | `string` | ✅ | `null` | The environment of the platform where the SDK is installed. Environments are created on the Web Console and should match the available environments in your infrastructure. | +| application | `string` | ✅ | `null` | The name of the application where the SDK is installed. Applications are created on the Web Console and should match the applications where your experiments will be running.| +| retries | `number` | ❌ | `5` | Number of retry attempts for failed HTTP requests. | +| timeout | `number` | ❌ | `3000` | HTTP request timeout in milliseconds. | +| eventLogger | `function` | ❌ | `null` | Callback to handle SDK events (ready, exposure, goal, etc.) | -## Basic Usage +## Creating a New Context -### Creating a New Context Request +### With Promises -#### With raw promises ```javascript -// define a new context request const request = { units: { - session_id: '5ebf06d8cb5d8137290c4abb64155584fbdb64d8', + session_id: "5ebf06d8cb5d8137290c4abb64155584fbdb64d8", }, }; -// create context with raw promises const context = sdk.createContext(request); context.ready().then((response) => { - console.log("ABSmartly Context ready!") + console.log("ABSmartly Context ready!"); }).catch((error) => { console.log(error); }); ``` -#### With async/await +### With async/await + ```javascript -// define a new context request const request = { units: { - session_id: '5ebf06d8cb5d8137290c4abb64155584fbdb64d8', + session_id: "5ebf06d8cb5d8137290c4abb64155584fbdb64d8", }, }; -// create context with raw promises const context = sdk.createContext(request); try { await context.ready(); - console.log("ABSmartly Context ready!") + console.log("ABSmartly Context ready!"); } catch (error) { console.log(error); } ``` -#### With pre-fetched data -When doing full-stack experimentation with A/B Smartly, we recommend creating a context only once on the server-side. -Creating a context involves a round-trip to the A/B Smartly event collector. -We can avoid repeating the round-trip on the client-side by sending the server-side data embedded in the first document, for example, by rendering it on the template. -Then we can initialize the A/B Smartly context on the client-side directly with it. +### With Pre-fetched Data + +When doing full-stack experimentation with A/B Smartly, we recommend creating a context only once on the server-side. Creating a context involves a round-trip to the A/B Smartly event collector. We can avoid repeating the round-trip on the client-side by sending the server-side data embedded in the first document, for example, by rendering it on the template. Then we can initialize the A/B Smartly context on the client-side directly with it. ```html - - - + + + +``` + +### Refreshing the Context with Fresh Experiment Data + +For long-running single-page-applications (SPA), the context is usually created once when the application is first reached. However, any experiments being tracked in your production code, but started after the context was created, will not be triggered. To mitigate this, we can use the `refreshInterval` option when creating the context. + +```javascript +const request = { + units: { + session_id: "5ebf06d8cb5d8137290c4abb64155584fbdb64d8", + }, +}; + +const context = sdk.createContext(request, { + refreshInterval: 5 * 60 * 1000, // 5 minutes +}); +``` + +Alternatively, the `refresh()` method can be called manually. The `refresh()` method pulls updated experiment data from the A/B Smartly collector and will trigger recently started experiments when `treatment()` is called again. + +```javascript +setTimeout(async () => { + try { + await context.refresh(); + } catch (error) { + console.error(error); + } +}, 5 * 60 * 1000); ``` ### Setting Extra Units + You can add additional units to a context by calling the `unit()` or the `units()` method. This is useful when a user logs in to your application and you want to add the new unit type to the context. > **Note:** You cannot override an already set unit type as that would be a change of identity. In this case, you must create a new context instead. @@ -158,27 +178,19 @@ You can add additional units to a context by calling the `unit()` or the `units( The `unit()` and `units()` methods can be called before the context is ready. ```javascript -context.unit('db_user_id', 1000013); +context.unit("db_user_id", 1000013); -// or context.units({ db_user_id: 1000013, }); ``` -### Context Attributes -The `attribute()` and `attributes()` methods can be called before the context is ready. -```javascript -context.attribute('user_agent', navigator.userAgent); - -context.attributes({ - customer_age: 'new_customer', -}); -``` +## Basic Usage ### Selecting a Treatment + ```javascript -if (context.treatment("exp_test_experiment") == 0) { +if (context.treatment("exp_test_experiment") === 0) { // user is in control group (variant 0) } else { // user is in treatment group @@ -194,73 +206,101 @@ const defaultButtonColor = "red"; const buttonColor = context.variableValue("button.color", defaultButtonColor); ``` -### Tracking Goals -Goals are created in the A/B Smartly web console. +### Peek at Treatment Variants + +Although generally not recommended, it is sometimes necessary to peek at a treatment without triggering an exposure. The A/B Smartly SDK provides a `peek()` method for that. + ```javascript -context.track("payment", { item_count: 1, total_amount: 1999.99 }); +if (context.peek("exp_test_experiment") === 0) { + // user is in control group (variant 0) +} else { + // user is in treatment group +} ``` -## Advanced +#### Peeking at Variable Values -### Publishing Pending Data -Sometimes it is necessary to ensure all events have been published to the A/B Smartly collector, before proceeding. -One such case is when the user is about to navigate away right before being exposed to a treatment. -You can explicitly call the `publish()` method, which returns a promise, before navigating away. ```javascript -await context.publish().then(() => { - window.location = "https://www.absmartly.com" -}) +const buttonColor = context.peekVariableValue("button.color", "red"); ``` -### Finalizing -The `finalize()` method will ensure all events have been published to the A/B Smartly collector, like `publish()`, and will also "seal" the context, throwing an error if any method that could generate an event is called. +### Overriding Treatment Variants + +During development, it is useful to force a treatment for an experiment. This can be achieved with the `override()` and/or `overrides()` methods. The `override()` and `overrides()` methods can be called before the context is ready. + ```javascript -await context.finalize().then(() => { - window.location = "https://www.absmartly.com" -}) +context.override("exp_test_experiment", 1); // force variant 1 + +context.overrides({ + exp_test_experiment: 1, + exp_another_experiment: 0, +}); ``` -### Refreshing the Context with Fresh Experiment Data -For long-running single-page-applications (SPA), the context is usually created once when the application is first reached. -However, any experiments being tracked in your production code, but started after the context was created, will not be triggered. -To mitigate this, we can use the `refreshInterval` option when creating the context. +## Advanced + +### Context Attributes + +Attributes are used to pass meta-data about the user and/or the request. They can be used later in the Web Console to create segments or audiences. They can be set using the `attribute()` or `attributes()` methods, before or after the context is ready. ```javascript -const request = { - units: { - session_id: '5ebf06d8cb5d8137290c4abb64155584fbdb64d8', - }, -}; +context.attribute("user_agent", navigator.userAgent); -const context = sdk.createContext(request, { - refreshInterval: 5 * 60 * 1000 +context.attributes({ + customer_age: "new_customer", }); ``` -Alternatively, the `refresh()` method can be called manually. -The `refresh()` method pulls updated experiment data from the A/B Smartly collector and will trigger recently started experiments when `treatment()` is called again. +### Custom Assignments + +Sometimes it may be necessary to override the automatic selection of a variant. For example, if you wish to have your variant chosen based on data from an API call. This can be accomplished using the `customAssignment()` method. + ```javascript -setTimeout(async () => { - try { - context.refresh(); - } catch(error) { - console.error(error); - } -}, 5 * 60 * 1000); +context.customAssignment("exp_test_experiment", 1); + +context.customAssignments({ + exp_test_experiment: 1, +}); +``` + +### Tracking Goals + +Goals are created in the A/B Smartly web console. + +```javascript +context.track("payment", { item_count: 1, total_amount: 1999.99 }); +``` + +### Publishing Pending Data + +Sometimes it is necessary to ensure all events have been published to the A/B Smartly collector, before proceeding. One such case is when the user is about to navigate away right before being exposed to a treatment. You can explicitly call the `publish()` method, which returns a promise, before navigating away. + +```javascript +await context.publish(); +window.location = "https://www.absmartly.com"; +``` + +### Finalizing + +The `finalize()` method will ensure all events have been published to the A/B Smartly collector, like `publish()`, and will also "seal" the context, throwing an error if any method that could generate an event is called. + +```javascript +await context.finalize(); +window.location = "https://www.absmartly.com"; ``` ### Using a Custom Event Logger -The A/B Smartly SDK can be instantiated with an event logger used for all contexts. -In addition, an event logger can be specified when creating a particular context, in the `createContext` call options. -The example below illustrates this with the implementation of the default event logger, used if none is specified. + +The A/B Smartly SDK can be instantiated with an event logger used for all contexts. In addition, an event logger can be specified when creating a particular context, in the `createContext` call options. The example below illustrates this with the implementation of the default event logger, used if none is specified. + ```javascript const sdk = new absmartly.SDK({ - endpoint: 'https://sandbox-api.absmartly.com/v1', + endpoint: "https://your-company.absmartly.io/v1", apiKey: process.env.ABSMARTLY_API_KEY, environment: process.env.NODE_ENV, application: process.env.APPLICATION_NAME, eventLogger: (context, eventName, data) => { - if (eventName == "error") { + if (eventName === "error") { console.error(data); } }, @@ -271,63 +311,42 @@ const sdk = new absmartly.SDK({ The data parameter depends on the type of event. Currently, the SDK logs the following events: -| Event | When | Data | -| :--- | :--- | :--- | -| `"error"` | Context receives an error | Error object thrown | -| `"ready"` | Context turns ready | Data used to initialize the context | -| `"refresh"` | `refresh()` method succeeds | Data used to refresh the context | -| `"publish"` | `publish()` method succeeds | Data sent to the ABsmartly event collector | -| `"exposure"` | `treatment()` method succeeds on first exposure | Exposure data enqueued for publishing | -| `"goal"` | `track()` method succeeds | Goal data enqueued for publishing | -| `"finalize"` | `finalize()` method succeeds the first time | undefined | - - -### Peek at Treatment Variants -Although generally not recommended, it is sometimes necessary to peek at a treatment without triggering an exposure. The ABsmartly SDK provides a `peek()` method for that. - -```javascript -if (context.peek("exp_test_experiment") == 0) { - // user is in control group (variant 0) -} else { - // user is in treatment group -} -``` - -### Overriding Treatment Variants -During development, it is useful to force a treatment for an experiment. This can be achieved with the `override()` and/or `overrides()` methods. The `override()` and `overrides()` methods can be called before the context is ready. - -```javascript -context.override("exp_test_experiment", 1); // force variant 1 of treatment -context.overrides({ - exp_test_experiment: 1, - exp_another_experiment: 0, -}); -``` +| Event | When | Data | +| :----------- | :------------------------------------------------ | :-------------------------------------------- | +| `"error"` | Context receives an error | Error object thrown | +| `"ready"` | Context turns ready | Data used to initialize the context | +| `"refresh"` | `refresh()` method succeeds | Data used to refresh the context | +| `"publish"` | `publish()` method succeeds | Data sent to the A/B Smartly event collector | +| `"exposure"` | `treatment()` method succeeds on first exposure | Exposure data enqueued for publishing | +| `"goal"` | `track()` method succeeds | Goal data enqueued for publishing | +| `"finalize"` | `finalize()` method succeeds the first time | undefined | ### HTTP Request Timeout -It is possible to set a timeout per individual HTTP request, overriding the global timeout set for all request when instantiating the SDK object. -Here is an example of setting a timeout only for the createContext request. +It is possible to set a timeout per individual HTTP request, overriding the global timeout set for all requests when instantiating the SDK object. + +Here is an example of setting a timeout only for the `createContext` request. ```javascript const context = sdk.createContext(request, { - refreshInterval: 5 * 60 * 1000 + refreshInterval: 5 * 60 * 1000, }, { - timeout: 1500 + timeout: 1500, }); ``` ### HTTP Request Cancellation -Sometimes it is useful to cancel an inflight HTTP request, for example, when the user is navigating away. The ABsmartly SDK supports cancellation via an `AbortSignal`. An implementation of AbortController is provided for older platforms, but will use the native implementation where available. + +Sometimes it is useful to cancel an inflight HTTP request, for example, when the user is navigating away. The A/B Smartly SDK supports cancellation via an `AbortSignal`. An implementation of AbortController is provided for older platforms, but will use the native implementation where available. Here is an example of a cancellation scenario. ```javascript const controller = new absmartly.AbortController(); const context = sdk.createContext(request, { - refreshInterval: 5 * 60 * 1000 + refreshInterval: 5 * 60 * 1000, }, { - signal: controller.signal + signal: controller.signal, }); // abort request if not ready after 1500ms @@ -338,11 +357,127 @@ await context.ready(); clearTimeout(timeoutId); ``` +## Node.js Usage + +### Express.js Middleware Example + +```javascript +const absmartly = require("@absmartly/javascript-sdk"); + +const sdk = new absmartly.SDK({ + endpoint: "https://your-company.absmartly.io/v1", + apiKey: process.env.ABSMARTLY_API_KEY, + environment: "production", + application: "website", +}); + +app.use(async (req, res, next) => { + const context = sdk.createContext({ + units: { + session_id: req.cookies.session_id, + }, + }); + + try { + await context.ready(); + req.absmartly = context; + next(); + } catch (error) { + console.error("ABSmartly context failed:", error); + next(); + } +}); + +app.get("/landing", (req, res) => { + const context = req.absmartly; + const treatment = context.treatment("exp_landing_page"); + + if (treatment === 0) { + res.render("landing-control"); + } else { + res.render("landing-treatment"); + } +}); +``` + +### Server-Side Rendering (SSR) with Data Forwarding + +Create the context on the server and pass the data to the client to avoid a second round-trip. + +```javascript +app.get("/", async (req, res) => { + const context = sdk.createContext({ + units: { session_id: req.cookies.session_id }, + }); + + await context.ready(); + + const contextData = context.data(); + const treatment = context.treatment("exp_homepage"); + + res.render("index", { + treatment, + absmartlyData: JSON.stringify(contextData), + }); +}); +``` + +On the client side, initialize the context with the pre-fetched data: + +```javascript +const context = sdk.createContextWith( + { units: { session_id: sessionId } }, + JSON.parse(window.__ABSMARTLY_DATA__) +); +// context is immediately ready, no round-trip needed +``` + +## Browser Usage + +### Single-Page Application (SPA) Example + +```javascript +import absmartly from "@absmartly/javascript-sdk"; + +const sdk = new absmartly.SDK({ + endpoint: "https://your-company.absmartly.io/v1", + apiKey: "YOUR_API_KEY", + environment: "production", + application: "website", + eventLogger: (context, eventName, data) => { + if (eventName === "exposure") { + analytics.track("Experiment Viewed", { + experiment: data.name, + variant: data.variant, + }); + } + }, +}); + +const context = sdk.createContext({ + units: { + session_id: getUserSessionId(), + }, +}); + +await context.ready(); + +const showNewFeature = context.treatment("exp_new_feature") !== 0; + +if (showNewFeature) { + renderNewFeature(); +} else { + renderOldFeature(); +} + +document.getElementById("checkout-btn").addEventListener("click", () => { + context.track("checkout", { total: getCartTotal() }); +}); +``` ## About A/B Smartly -**A/B Smartly** is the leading provider of state-of-the-art, on-premises, full-stack experimentation platforms for engineering and product teams that want to confidently deploy features as fast as they can develop them. -A/B Smartly's real-time analytics helps engineering and product teams ensure that new features will improve the customer experience without breaking or degrading performance and/or business metrics. +**A/B Smartly** is the leading provider of state-of-the-art, on-premises, full-stack experimentation platforms for engineering and product teams that want to confidently deploy features as fast as they can develop them. A/B Smartly's real-time analytics helps engineering and product teams ensure that new features will improve the customer experience without breaking or degrading performance and/or business metrics. ### Have a look at our growing list of clients and SDKs: - [JavaScript SDK](https://www.github.com/absmartly/javascript-sdk) (this package) @@ -360,12 +495,3 @@ A/B Smartly's real-time analytics helps engineering and product teams ensure tha - [Ruby SDK](https://www.github.com/absmartly/ruby-sdk) - [.NET SDK](https://www.github.com/absmartly/dotnet-sdk) - [Rust SDK](https://www.github.com/absmartly/rust-sdk) - -## Documentation - -- [Full Documentation](https://docs.absmartly.com/) -- [JavaScript SDK Documentation](https://docs.absmartly.com/docs/SDK-Documentation/getting-started) - -## License - -MIT License From 589f8c9ec1f8079e4b1825907145529b2257cb59 Mon Sep 17 00:00:00 2001 From: Jonas Alves Date: Fri, 27 Feb 2026 08:05:15 +0000 Subject: [PATCH 4/7] feat: add public getSDK() and getOptions() methods to Context Exposes the SDK instance and context options through public API so React and Angular SDKs no longer need to access private _sdk/_opts fields. --- src/context.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/context.ts b/src/context.ts index 37a205e..66e00d6 100644 --- a/src/context.ts +++ b/src/context.ts @@ -475,6 +475,14 @@ export default class Context { } } + getSDK(): SDK { + return this._sdk; + } + + getOptions(): ContextOptions { + return this._opts; + } + private _checkNotFinalized() { if (this.isFinalized()) { throw new Error("ABSmartly Context is finalized."); From 3d35b122cccdc69fe844b6da25ae4c19e8e6f980 Mon Sep 17 00:00:00 2001 From: Jonas Alves Date: Sun, 15 Mar 2026 20:46:26 +0000 Subject: [PATCH 5/7] fix(js): return safe defaults from read methods when context not ready or finalized treatment/peek return 0, variableValue/peekVariableValue return defaultValue, variableKeys returns {}, experiments returns [], custom field methods return null. Write methods (track, unit, attribute) keep throwing. --- src/__tests__/context.test.js | 84 +++++++++++++++++++--------- src/context.ts | 101 ++++++++++++++++++---------------- 2 files changed, 113 insertions(+), 72 deletions(-) diff --git a/src/__tests__/context.test.js b/src/__tests__/context.test.js index 94a3912..fe92a41 100644 --- a/src/__tests__/context.test.js +++ b/src/__tests__/context.test.js @@ -607,12 +607,12 @@ describe("Context", () => { expect(context.isFinalized()).toEqual(false); expect(() => context.data()).toThrow(); - expect(() => context.treatment("test")).toThrow(); - expect(() => context.peek("test")).toThrow(); - expect(() => context.experiments()).toThrow(); - expect(() => context.variableKeys()).toThrow(); - expect(() => context.variableValue("a", 17)).toThrow(); - expect(() => context.peekVariableValue("a", 17)).toThrow(); + expect(context.treatment("test")).toEqual(0); + expect(context.peek("test")).toEqual(0); + expect(context.experiments()).toEqual([]); + expect(context.variableKeys()).toEqual({}); + expect(context.variableValue("a", 17)).toEqual(17); + expect(context.peekVariableValue("a", 17)).toEqual(17); done(); }); @@ -1511,6 +1511,30 @@ describe("Context", () => { done(); }); + + it("should return 0 when not ready", (done) => { + const context = new Context(sdk, contextOptions, contextParams, Promise.resolve(getContextResponse)); + expect(context.isReady()).toEqual(false); + + expect(context.peek("exp_test_ab")).toEqual(0); + + done(); + }); + + it("should return 0 after finalize", (done) => { + const context = new Context(sdk, contextOptions, contextParams, getContextResponse); + publisher.publish.mockReturnValue(Promise.resolve()); + + context.treatment("exp_test_ab"); + + context.finalize().then(() => { + expect(context.peek("exp_test_ab")).toEqual(0); + done(); + }); + + expect(context.isFinalizing()).toEqual(true); + expect(context.peek("exp_test_ab")).toEqual(0); + }); }); describe("treatment()", () => { @@ -1927,7 +1951,7 @@ describe("Context", () => { }); }); - it("should throw after finalized() call", (done) => { + it("should return 0 after finalized() call", (done) => { const context = new Context(sdk, contextOptions, contextParams, getContextResponse); publisher.publish.mockReturnValue(Promise.resolve()); @@ -1936,13 +1960,13 @@ describe("Context", () => { expect(context.pending()).toEqual(1); context.finalize().then(() => { - expect(() => context.treatment("exp_test_ab")).toThrow(); + expect(context.treatment("exp_test_ab")).toEqual(0); done(); }); expect(context.isFinalizing()).toEqual(true); - expect(() => context.treatment("exp_test_ab")).toThrow(); + expect(context.treatment("exp_test_ab")).toEqual(0); }); it("should re-evaluate audience expression when attributes change in strict mode", (done) => { @@ -2140,6 +2164,15 @@ describe("Context", () => { done(); }); + it("should return 0 when not ready", (done) => { + const context = new Context(sdk, contextOptions, contextParams, Promise.resolve(getContextResponse)); + expect(context.isReady()).toEqual(false); + + expect(context.treatment("exp_test_ab")).toEqual(0); + + done(); + }); + it("should update attrsSeq after checking unchanged audience to avoid repeated evaluation", (done) => { const context = new Context(sdk, contextOptions, contextParams, audienceStrictContextResponse); @@ -2575,7 +2608,7 @@ describe("Context", () => { }); }); - it("should throw after finalized() call", (done) => { + it("should return defaultValue after finalized() call", (done) => { const context = new Context(sdk, contextOptions, contextParams, getContextResponse); publisher.publish.mockReturnValue(Promise.resolve()); @@ -2584,13 +2617,13 @@ describe("Context", () => { expect(context.pending()).toEqual(1); context.finalize().then(() => { - expect(() => context.variableValue("button.color", 17)).toThrow(); + expect(context.variableValue("button.color", 17)).toEqual(17); done(); }); expect(context.isFinalizing()).toEqual(true); - expect(() => context.variableValue("button.color", 17)).toThrow(); + expect(context.variableValue("button.color", 17)).toEqual(17); }); }); @@ -4113,27 +4146,28 @@ describe("Context", () => { expect(context.customFieldValue("exp_test_custom_fields", "false_boolean_field")).toEqual(false); }); - it("should console an error when JSON cannot be parsed", () => { - const errorSpy = jest.spyOn(console, "error"); - const context = new Context(sdk, contextOptions, contextParams, getContextResponse); + it("should log an error through eventLogger when JSON cannot be parsed", () => { + const eventLogger = jest.fn(); + const context = new Context(sdk, { ...contextOptions, eventLogger }, contextParams, getContextResponse); expect(context.pending()).toEqual(0); expect(context.customFieldValue("exp_test_abc", "json_invalid")).toEqual(null); - expect(errorSpy).toHaveBeenCalledTimes(1); - expect(errorSpy).toHaveBeenCalledWith( - "Failed to parse JSON custom field value 'json_invalid' for experiment 'exp_test_abc'" - ); + expect(eventLogger).toHaveBeenCalledWith(context, "error", expect.any(Error)); }); - it("should console an error when a field type is invalid", () => { - const errorSpy = jest.spyOn(console, "error"); - const context = new Context(sdk, contextOptions, contextParams, getContextResponse); + it("should log an error through eventLogger when a field type is invalid", () => { + const eventLogger = jest.fn(); + const context = new Context(sdk, { ...contextOptions, eventLogger }, contextParams, getContextResponse); expect(context.pending()).toEqual(0); expect(context.customFieldValue("exp_test_custom_fields", "invalid_type_field")).toEqual(null); - expect(errorSpy).toHaveBeenCalledTimes(1); - expect(errorSpy).toHaveBeenCalledWith( - "Unknown custom field type 'invalid' for experiment 'exp_test_custom_fields' and key 'invalid_type_field' - you may need to upgrade to the latest SDK version" + expect(eventLogger).toHaveBeenCalledWith( + context, + "error", + expect.objectContaining({ + message: + "Unknown custom field type 'invalid' for experiment 'exp_test_custom_fields' and key 'invalid_type_field' - you may need to upgrade to the latest SDK version", + }) ); }); }); diff --git a/src/context.ts b/src/context.ts index 66e00d6..be5cdb4 100644 --- a/src/context.ts +++ b/src/context.ts @@ -136,14 +136,17 @@ export default class Context { private _data: ContextData; private _exposures: Exposure[]; private _failed: boolean; + private _failedError: Error | null; private _finalized: boolean; - private _finalizing: boolean | Promise | null; + private _finalizing: Promise | null; private _goals: Goal[]; private _index: Record; private _indexVariables: Record; private _overrides: Record; private _pending: number; private _attrsSeq: number; + private _attrsMapCache: Record | null; + private _attrsMapCacheSeq: number; private _hashes?: Record; private _promise?: Promise; private _publishTimeout?: ReturnType; @@ -157,6 +160,7 @@ export default class Context { this._opts = options; this._pending = 0; this._failed = false; + this._failedError = null; this._finalized = false; this._attrs = []; this._goals = []; @@ -167,6 +171,8 @@ export default class Context { this._assigners = {}; this._audienceMatcher = new AudienceMatcher(); this._attrsSeq = 0; + this._attrsMapCache = null; + this._attrsMapCacheSeq = -1; if (params.units) { this.units(params.units); @@ -188,6 +194,7 @@ export default class Context { this._init({}); this._failed = true; + this._failedError = error; delete this._promise; this._logError(error); @@ -216,6 +223,10 @@ export default class Context { return this._failed; } + readyError(): Error | null { + return this._failedError; + } + ready() { if (this.isReady()) { return Promise.resolve(true); @@ -346,32 +357,24 @@ export default class Context { if (!experimentName || typeof experimentName !== "string") { throw new Error("Experiment name must be a non-empty string"); } - this._checkReady(true); + if (!this.isReady() || this.isFinalized() || this.isFinalizing()) { + return 0; + } return this._peek(experimentName).variant; } - /** - * Gets the treatment variant for an experiment and queues an exposure event. - * @param experimentName - Name of the experiment - * @returns The variant number (0 for control, 1+ for treatments) - * @throws Error if context is not ready or experiment name is invalid - */ treatment(experimentName: string) { if (!experimentName || typeof experimentName !== "string") { throw new Error("Experiment name must be a non-empty string"); } - this._checkReady(true); + if (!this.isReady() || this.isFinalized() || this.isFinalizing()) { + return 0; + } return this._treatment(experimentName).variant; } - /** - * Tracks a goal achievement for conversion tracking. - * @param goalName - Name of the goal - * @param properties - Optional additional properties to track with the goal - * @throws Error if context is finalized or goal name is invalid - */ track(goalName: string, properties?: Record | null) { if (!goalName || typeof goalName !== "string") { throw new Error("Goal name must be a non-empty string"); @@ -386,48 +389,39 @@ export default class Context { } experiments() { - this._checkReady(); + if (!this.isReady()) { + return []; + } - return this._data.experiments?.map((x) => x.name); + return this._data.experiments?.map((x) => x.name) ?? []; } - /** - * Gets the value of a variable and queues an exposure event. - * This causes the user to be exposed to the experiment variant containing this variable. - * @param key - Variable name - * @param defaultValue - Default value if variable is not defined - * @returns The variable value from the assigned variant, or defaultValue - * @throws Error if context is not ready or key is invalid - */ variableValue(key: string, defaultValue: string): string { if (!key || typeof key !== "string") { throw new Error("Variable key must be a non-empty string"); } - this._checkReady(true); + if (!this.isReady() || this.isFinalized() || this.isFinalizing()) { + return defaultValue; + } return this._variableValue(key, defaultValue); } - /** - * Gets the value of a variable without queuing an exposure event. - * Use this for internal logging or non-user-facing decisions. - * Unlike variableValue(), this does NOT expose the user to the experiment. - * @param key - Variable name - * @param defaultValue - Default value if variable is not defined - * @returns The variable value from the assigned variant, or defaultValue - * @throws Error if context is not ready or key is invalid - */ peekVariableValue(key: string, defaultValue: string): string { if (!key || typeof key !== "string") { throw new Error("Variable key must be a non-empty string"); } - this._checkReady(true); + if (!this.isReady() || this.isFinalized() || this.isFinalizing()) { + return defaultValue; + } return this._peekVariable(key, defaultValue); } variableKeys() { - this._checkReady(true); + if (!this.isReady() || this.isFinalized() || this.isFinalizing()) { + return {}; + } const variableExperiments: Record = {}; @@ -480,7 +474,7 @@ export default class Context { } getOptions(): ContextOptions { - return this._opts; + return { ...this._opts }; } private _checkNotFinalized() { @@ -502,10 +496,15 @@ export default class Context { } private _getAttributesMap(): Record { + if (this._attrsMapCache !== null && this._attrsMapCacheSeq === this._attrsSeq) { + return this._attrsMapCache; + } const attrs: Record = {}; for (const attr of this._attrs) { attrs[attr.name] = attr.value; } + this._attrsMapCache = attrs; + this._attrsMapCacheSeq = this._attrsSeq; return attrs; } @@ -714,7 +713,9 @@ export default class Context { } customFieldKeys() { - this._checkReady(true); + if (!this.isReady() || this.isFinalized() || this.isFinalizing()) { + return []; + } return this._customFieldKeys(); } @@ -737,14 +738,16 @@ export default class Context { if (field.value === "") return ""; return JSON.parse(field.value); } catch (e) { - console.error(`Failed to parse JSON custom field value '${key}' for experiment '${experimentName}'`); + this._logError(e as Error); return null; } case "boolean": return field.value === "true"; default: - console.error( - `Unknown custom field type '${field.type}' for experiment '${experimentName}' and key '${key}' - you may need to upgrade to the latest SDK version` + this._logError( + new Error( + `Unknown custom field type '${field.type}' for experiment '${experimentName}' and key '${key}' - you may need to upgrade to the latest SDK version` + ) ); return null; } @@ -755,7 +758,9 @@ export default class Context { } customFieldValue(experimentName: string, key: string) { - this._checkReady(true); + if (!this.isReady() || this.isFinalized() || this.isFinalizing()) { + return null; + } return this._customFieldValue(experimentName, key); } @@ -774,14 +779,16 @@ export default class Context { } customFieldValueType(experimentName: string, key: string) { - this._checkReady(true); + if (!this.isReady() || this.isFinalized() || this.isFinalizing()) { + return null; + } return this._customFieldValueType(experimentName, key); } private _resolveVariableValue(key: string, defaultValue: string, shouldQueueExposure: boolean): string { - for (const i in this._indexVariables[key]) { - const experimentName = this._indexVariables[key][i].data.name; + for (const experiment of this._indexVariables[key] ?? []) { + const experimentName = experiment.data.name; const assignment = this._assign(experimentName); if (assignment.variables !== undefined) { if (shouldQueueExposure && !assignment.exposed) { @@ -1010,7 +1017,7 @@ export default class Context { try { parsed = JSON.parse(config); } catch (error) { - console.error(`Failed to parse variant config for experiment "${experiment.name}":`, error); + this._logError(error as Error); parsed = {}; } } From bcaf531c9ca5147bdf52b97e09d8cb7c6ba7f3ab Mon Sep 17 00:00:00 2001 From: Jonas Alves Date: Tue, 17 Mar 2026 13:45:15 +0000 Subject: [PATCH 6/7] =?UTF-8?q?feat:=20cross-SDK=20consistency=20fixes=20?= =?UTF-8?q?=E2=80=94=20all=20201=20scenarios=20passing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 - src/__tests__/fixes.test.js | 455 ++++++++++++++++++++++++++++++++ src/abort-controller-shim.ts | 11 +- src/client.ts | 24 +- src/fetch.ts | 14 +- src/jsonexpr/operators/eq.ts | 9 +- src/jsonexpr/operators/match.ts | 33 ++- src/matcher.ts | 4 +- src/sdk.ts | 39 +-- 9 files changed, 522 insertions(+), 70 deletions(-) create mode 100644 src/__tests__/fixes.test.js diff --git a/.gitignore b/.gitignore index 29ed217..066c4b5 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,3 @@ js .claude/ .DS_Store -AUDIT_REPORT.md -FIXES_SUMMARY.md -FIXES_SUMMARY_COMPLETE.md diff --git a/src/__tests__/fixes.test.js b/src/__tests__/fixes.test.js new file mode 100644 index 0000000..c7deaef --- /dev/null +++ b/src/__tests__/fixes.test.js @@ -0,0 +1,455 @@ +import { MatchOperator } from "../jsonexpr/operators/match"; +import { EqualsOperator } from "../jsonexpr/operators/eq"; +import { mockEvaluator } from "./jsonexpr/operators/evaluator"; +import { AbortController as ShimAbortController, AbortSignal as ShimAbortSignal } from "../abort-controller-shim"; +import { AudienceMatcher } from "../matcher"; +import SDK from "../sdk"; +import Context from "../context"; +import Client from "../client"; +import { ContextPublisher } from "../publisher"; +import { ContextDataProvider } from "../provider"; + +jest.mock("../client"); +jest.mock("../sdk"); +jest.mock("../provider"); +jest.mock("../publisher"); + +describe("Fix #1: ReDoS protection in MatchOperator", () => { + const operator = new MatchOperator(); + const evaluator = mockEvaluator(); + + it("should reject patterns with nested quantifiers", () => { + expect(operator.evaluate(evaluator, ["aaaaaaaaaa", "(a+)+$"])).toBe(null); + expect(operator.evaluate(evaluator, ["test", "(a*)*b"])).toBe(null); + expect(operator.evaluate(evaluator, ["test", "(x+){2}"])).toBe(null); + }); + + it("should still allow safe patterns", () => { + expect(operator.evaluate(evaluator, ["abc", "a+"])).toBe(true); + expect(operator.evaluate(evaluator, ["abc", "^abc$"])).toBe(true); + expect(operator.evaluate(evaluator, ["abc", "[a-z]+"])).toBe(true); + }); + + it("should reject patterns exceeding max length", () => { + const longPattern = "a".repeat(1001); + expect(operator.evaluate(evaluator, ["test", longPattern])).toBe(null); + }); + + it("should reject text exceeding max length", () => { + const longText = "a".repeat(10001); + expect(operator.evaluate(evaluator, [longText, "a"])).toBe(null); + }); + + it("should cache compiled regexes", () => { + expect(operator.evaluate(evaluator, ["abc", "abc"])).toBe(true); + expect(operator.evaluate(evaluator, ["abc", "abc"])).toBe(true); + }); + + it("should return null for invalid regex", () => { + expect(operator.evaluate(evaluator, ["test", "[invalid"])).toBe(null); + }); + + it("should not use console.error", () => { + const errorSpy = jest.spyOn(console, "error").mockImplementation(() => {}); + operator.evaluate(evaluator, ["test", "[invalid"]); + operator.evaluate(evaluator, ["test", "a".repeat(1001)]); + operator.evaluate(evaluator, ["a".repeat(10001), "a"]); + expect(errorSpy).not.toHaveBeenCalled(); + errorSpy.mockRestore(); + }); +}); + +describe("Fix #2: ready() error handling and readyError()", () => { + const sdk = new SDK(); + const publisher = new ContextPublisher(); + const provider = new ContextDataProvider(); + + sdk.getContextDataProvider.mockReturnValue(provider); + sdk.getContextPublisher.mockReturnValue(publisher); + sdk.getClient.mockReturnValue(new Client()); + sdk.getEventLogger.mockReturnValue(SDK.defaultEventLogger); + + const contextOptions = { + publishDelay: -1, + refreshPeriod: 0, + }; + + const contextParams = { + units: { + session_id: "test-session", + }, + }; + + it("should store error via readyError() when context fetch fails", async () => { + const error = new Error("fetch failed"); + const context = new Context(sdk, contextOptions, contextParams, Promise.reject(error)); + await context.ready(); + + expect(context.isFailed()).toBe(true); + expect(context.readyError()).toBe(error); + }); + + it("should return null for readyError() when no failure", () => { + const context = new Context(sdk, contextOptions, contextParams, { experiments: [] }); + expect(context.readyError()).toBe(null); + }); +}); + +describe("Fix #3: for...of on array in _resolveVariableValue", () => { + const sdk = new SDK(); + const publisher = new ContextPublisher(); + const provider = new ContextDataProvider(); + + sdk.getContextDataProvider.mockReturnValue(provider); + sdk.getContextPublisher.mockReturnValue(publisher); + sdk.getClient.mockReturnValue(new Client()); + sdk.getEventLogger.mockReturnValue(SDK.defaultEventLogger); + + const contextOptions = { + publishDelay: -1, + refreshPeriod: 0, + }; + + const contextParams = { + units: { + session_id: "e791e240fcd3df7d238cfc285f475e8152fcc0ec", + }, + }; + + it("should handle unknown variable keys without error", () => { + const context = new Context(sdk, contextOptions, contextParams, { + experiments: [ + { + id: 1, + name: "exp_test", + iteration: 1, + unitType: "session_id", + seedHi: 3603515, + seedLo: 233373850, + split: [0.5, 0.5], + trafficSeedHi: 449867249, + trafficSeedLo: 455443629, + trafficSplit: [0.0, 1.0], + fullOnVariant: 0, + audience: null, + audienceStrict: false, + variants: [ + { name: "A", config: null }, + { name: "B", config: '{"color":"red"}' }, + ], + customFieldValues: null, + }, + ], + }); + + expect(context.variableValue("nonexistent_key", "default")).toBe("default"); + }); +}); + +describe("Fix #4: console.error routed through eventLogger", () => { + const sdk = new SDK(); + const publisher = new ContextPublisher(); + const provider = new ContextDataProvider(); + + sdk.getContextDataProvider.mockReturnValue(provider); + sdk.getContextPublisher.mockReturnValue(publisher); + sdk.getClient.mockReturnValue(new Client()); + + const contextParams = { + units: { + session_id: "test", + }, + }; + + it("should not call console.error directly for custom field parse errors", () => { + const eventLogger = jest.fn(); + const errorSpy = jest.spyOn(console, "error").mockImplementation(() => {}); + + sdk.getEventLogger.mockReturnValue(eventLogger); + const context = new Context(sdk, { publishDelay: -1, refreshPeriod: 0, eventLogger }, contextParams, { + experiments: [ + { + id: 1, + name: "exp", + iteration: 1, + unitType: "session_id", + seedHi: 1, + seedLo: 1, + split: [1], + trafficSeedHi: 1, + trafficSeedLo: 1, + trafficSplit: [0, 1], + fullOnVariant: 0, + audience: null, + audienceStrict: false, + variants: [{ name: "A", config: null }], + customFieldValues: [{ name: "bad_json", value: "{invalid", type: "json" }], + }, + ], + }); + + context.customFieldValue("exp", "bad_json"); + expect(errorSpy).not.toHaveBeenCalled(); + expect(eventLogger).toHaveBeenCalledWith(context, "error", expect.any(Error)); + errorSpy.mockRestore(); + }); + + it("should not call console.error in AudienceMatcher on parse failure", () => { + const errorSpy = jest.spyOn(console, "error").mockImplementation(() => {}); + const matcher = new AudienceMatcher(); + matcher.evaluate("{invalid json", {}); + expect(errorSpy).not.toHaveBeenCalled(); + errorSpy.mockRestore(); + }); + + it("should route variant config parse errors through eventLogger", () => { + const eventLogger = jest.fn(); + sdk.getEventLogger.mockReturnValue(eventLogger); + + const errorSpy = jest.spyOn(console, "error").mockImplementation(() => {}); + + const context = new Context(sdk, { publishDelay: -1, refreshPeriod: 0, eventLogger }, contextParams, { + experiments: [ + { + id: 1, + name: "exp_bad_config", + iteration: 1, + unitType: "session_id", + seedHi: 1, + seedLo: 1, + split: [1], + trafficSeedHi: 1, + trafficSeedLo: 1, + trafficSplit: [0, 1], + fullOnVariant: 0, + audience: null, + audienceStrict: false, + variants: [{ name: "A", config: "{invalid json}" }], + customFieldValues: null, + }, + ], + }); + + expect(errorSpy).not.toHaveBeenCalled(); + expect(eventLogger).toHaveBeenCalledWith(context, "error", expect.any(Error)); + errorSpy.mockRestore(); + }); +}); + +describe("Fix #5: _finalizing type cleanup", () => { + it("should not use boolean for _finalizing", () => { + const sdk = new SDK(); + const publisher = new ContextPublisher(); + const provider = new ContextDataProvider(); + + sdk.getContextDataProvider.mockReturnValue(provider); + sdk.getContextPublisher.mockReturnValue(publisher); + sdk.getClient.mockReturnValue(new Client()); + sdk.getEventLogger.mockReturnValue(jest.fn()); + + const context = new Context( + sdk, + { publishDelay: -1, refreshPeriod: 0 }, + { units: { session_id: "test" } }, + { experiments: [] } + ); + + expect(context.isFinalizing()).toBe(false); + expect(context.isFinalized()).toBe(false); + }); +}); + +describe("Fix #11: SDK._extractClientOptions uses includes", () => { + it("should extract client options correctly", () => { + const sdk = new SDK({ + agent: "test-agent", + apiKey: "key", + application: "app", + endpoint: "http://localhost", + environment: "test", + timeout: 5000, + }); + expect(sdk).toBeInstanceOf(SDK); + }); +}); + +describe("Fix #12: SDK.defaultEventLogger logs error message", () => { + let ActualSDK; + beforeAll(() => { + ActualSDK = jest.requireActual("../sdk").default; + }); + + it("should log error.message for Error instances", () => { + const errorSpy = jest.spyOn(console, "error").mockImplementation(() => {}); + const error = new Error("something failed"); + ActualSDK.defaultEventLogger(null, "error", error); + expect(errorSpy).toHaveBeenCalledWith("something failed"); + errorSpy.mockRestore(); + }); + + it("should log raw data for non-Error values", () => { + const errorSpy = jest.spyOn(console, "error").mockImplementation(() => {}); + ActualSDK.defaultEventLogger(null, "error", "plain text error"); + expect(errorSpy).toHaveBeenCalledWith("plain text error"); + errorSpy.mockRestore(); + }); +}); + +describe("Fix #13: _getAttributesMap caching", () => { + const sdk = new SDK(); + const publisher = new ContextPublisher(); + const provider = new ContextDataProvider(); + + sdk.getContextDataProvider.mockReturnValue(provider); + sdk.getContextPublisher.mockReturnValue(publisher); + sdk.getClient.mockReturnValue(new Client()); + sdk.getEventLogger.mockReturnValue(jest.fn()); + + it("should return correct attributes after multiple attribute() calls", () => { + const context = new Context( + sdk, + { publishDelay: -1, refreshPeriod: 0 }, + { units: { session_id: "test" } }, + { experiments: [] } + ); + + context.attribute("age", 25); + context.attribute("country", "US"); + + const attrs = context.getAttributes(); + expect(attrs).toEqual({ age: 25, country: "US" }); + }); +}); + +describe("Fix #15: fetch.ts throws on missing implementation", () => { + it("should not export undefined", async () => { + const fetchModule = await import("../fetch"); + const fetchImpl = fetchModule.default; + expect(fetchImpl).not.toBeUndefined(); + expect(typeof fetchImpl).toBe("function"); + }); +}); + +describe("Fix #16: Client.request timeout uses nullish coalescing", () => { + it("should accept timeout of 0 in options", () => { + const client = new Client({ + endpoint: "http://test", + agent: "test", + environment: "test", + apiKey: "key", + application: "app", + timeout: 5000, + }); + + expect(client).toBeInstanceOf(Client); + }); +}); + +describe("Fix #21: AbortController shim sets signal.reason", () => { + it("should set default reason on abort()", () => { + const controller = new ShimAbortController(); + controller.abort(); + expect(controller.signal.aborted).toBe(true); + expect(controller.signal.reason).toBeInstanceOf(Error); + expect(controller.signal.reason.message).toBe("The operation was aborted."); + }); + + it("should set custom reason on abort(reason)", () => { + const controller = new ShimAbortController(); + const customReason = new Error("custom abort"); + controller.abort(customReason); + expect(controller.signal.reason).toBe(customReason); + }); + + it("should have undefined reason before abort", () => { + const controller = new ShimAbortController(); + expect(controller.signal.reason).toBeUndefined(); + }); +}); + +describe("Fix #25: Context.getOptions() returns shallow copy", () => { + it("should not allow mutation of internal options", () => { + const sdk = new SDK(); + const publisher = new ContextPublisher(); + const provider = new ContextDataProvider(); + + sdk.getContextDataProvider.mockReturnValue(provider); + sdk.getContextPublisher.mockReturnValue(publisher); + sdk.getClient.mockReturnValue(new Client()); + sdk.getEventLogger.mockReturnValue(jest.fn()); + + const originalOptions = { publishDelay: 100, refreshPeriod: 0 }; + const context = new Context(sdk, originalOptions, { units: { session_id: "test" } }, { experiments: [] }); + + const opts = context.getOptions(); + opts.publishDelay = 9999; + + expect(context.getOptions().publishDelay).toBe(100); + }); +}); + +describe("Fix #32: AbortSignal dispatchEvent uses explicit onabort", () => { + it("should call onabort handler on dispatch", () => { + const signal = new ShimAbortSignal(); + const handler = jest.fn(); + signal.onabort = handler; + signal.dispatchEvent({ type: "abort" }); + expect(handler).toHaveBeenCalledTimes(1); + expect(handler).toHaveBeenCalledWith({ type: "abort" }); + }); + + it("should not call onabort for non-abort events", () => { + const signal = new ShimAbortSignal(); + const handler = jest.fn(); + signal.onabort = handler; + signal.dispatchEvent({ type: "other" }); + expect(handler).not.toHaveBeenCalled(); + }); +}); + +describe("Fix #33: Client constructor uses spread", () => { + it("should merge defaults with provided options", () => { + const client = new Client({ + endpoint: "http://test", + agent: "custom-agent", + environment: "prod", + apiKey: "key123", + application: "myapp", + timeout: 10000, + }); + + expect(client).toBeInstanceOf(Client); + }); +}); + +describe("Fix #34: SDK._contextOptions uses spread", () => { + it("should merge custom options with defaults", () => { + const sdk = new SDK({ + agent: "test", + apiKey: "key", + application: "app", + endpoint: "http://localhost", + environment: "test", + }); + + expect(sdk).toBeInstanceOf(SDK); + }); +}); + +describe("Fix #20: EqualsOperator without redundant Array.isArray", () => { + const operator = new EqualsOperator(); + const evaluator = mockEvaluator(); + + it("should evaluate equality correctly", () => { + expect(operator.evaluate(evaluator, [1, 1])).toBe(true); + expect(operator.evaluate(evaluator, [1, 2])).toBe(false); + }); + + it("should handle null comparison", () => { + expect(operator.evaluate(evaluator, [null, null])).toBe(null); + }); + + it("should handle empty args", () => { + expect(operator.evaluate(evaluator, [])).toBe(null); + }); +}); diff --git a/src/abort-controller-shim.ts b/src/abort-controller-shim.ts index 4a3c1f1..af18593 100644 --- a/src/abort-controller-shim.ts +++ b/src/abort-controller-shim.ts @@ -5,6 +5,8 @@ export type AbortControllerEvents = { // eslint-disable-next-line no-shadow export class AbortSignal { aborted = false; + reason: unknown = undefined; + onabort?: ((evt: { type: string }) => void) | null; private readonly _events: AbortControllerEvents; constructor() { @@ -34,9 +36,9 @@ export class AbortSignal { } dispatchEvent(evt: { type: string }) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - this[`on${evt.type}`] && this[`on${evt.type}`](evt); + if (evt.type === "abort" && this.onabort) { + this.onabort(evt); + } const listeners = this._events[evt.type]; if (listeners) { for (const listener of listeners) { @@ -54,7 +56,7 @@ export class AbortSignal { export class AbortController { signal = new AbortSignal(); - abort() { + abort(reason?: unknown) { let evt: Event | { type: string; bubbles: boolean; cancelable: boolean }; try { evt = new Event("abort"); @@ -67,6 +69,7 @@ export class AbortController { } this.signal.aborted = true; + this.signal.reason = reason ?? new Error("The operation was aborted."); this.signal.dispatchEvent(evt); } diff --git a/src/client.ts b/src/client.ts index 7f158e1..27a26e4 100644 --- a/src/client.ts +++ b/src/client.ts @@ -47,19 +47,13 @@ export default class Client { private readonly _delay: number; constructor(opts: ClientOptions) { - this._opts = Object.assign( - { - agent: "javascript-client", - apiKey: undefined, - application: undefined, - endpoint: undefined, - environment: undefined, - retries: DEFAULT_RETRIES, - timeout: DEFAULT_TIMEOUT_MS, - keepalive: true, - }, - opts - ); + this._opts = { + agent: "javascript-client", + retries: DEFAULT_RETRIES, + timeout: DEFAULT_TIMEOUT_MS, + keepalive: true, + ...opts, + }; for (const key of ["agent", "application", "apiKey", "endpoint", "environment"] as const) { if (key in this._opts && this._opts[key] !== undefined) { @@ -249,7 +243,7 @@ export default class Client { options.signal.addEventListener("abort", abort); } - const timeout = options.timeout || this._opts.timeout || 0; + const timeout = options.timeout ?? this._opts.timeout ?? 0; const timeoutId = timeout > 0 ? setTimeout(() => { @@ -265,7 +259,7 @@ export default class Client { } }; - return tryWith(this._opts.retries ?? DEFAULT_RETRIES, this._opts.timeout ?? DEFAULT_TIMEOUT_MS) + return tryWith(this._opts.retries ?? DEFAULT_RETRIES, timeout || DEFAULT_TIMEOUT_MS) .then((value: string) => { finalCleanUp(); return value; diff --git a/src/fetch.ts b/src/fetch.ts index 4b18dcf..339a221 100644 --- a/src/fetch.ts +++ b/src/fetch.ts @@ -16,9 +16,9 @@ function getFetchImplementation() { return fetchShim; } - if (global) { - if (global.fetch) { - return global.fetch.bind(global); + if (typeof globalThis !== "undefined") { + if (globalThis.fetch) { + return globalThis.fetch.bind(globalThis); } return function (url: string, opts: Record) { return new Promise((resolve, reject) => { @@ -34,6 +34,12 @@ function getFetchImplementation() { return undefined; } -const exported = getFetchImplementation(); +const impl = getFetchImplementation(); + +const exported = + impl ?? + function () { + throw new Error("No fetch implementation found. Please provide a fetch polyfill for your environment."); + }; export default exported; diff --git a/src/jsonexpr/operators/eq.ts b/src/jsonexpr/operators/eq.ts index b7f358e..225b7ee 100644 --- a/src/jsonexpr/operators/eq.ts +++ b/src/jsonexpr/operators/eq.ts @@ -3,12 +3,9 @@ import { BinaryOperator } from "./binary"; export class EqualsOperator extends BinaryOperator { evaluate(evaluator: Evaluator, args: unknown[]) { - if (Array.isArray(args)) { - const lhs = args.length > 0 ? evaluator.evaluate(args[0]) : null; - const rhs = args.length > 1 ? evaluator.evaluate(args[1]) : null; - return this.binary(evaluator, lhs, rhs); - } - return null; + const lhs = args.length > 0 ? evaluator.evaluate(args[0]) : null; + const rhs = args.length > 1 ? evaluator.evaluate(args[1]) : null; + return this.binary(evaluator, lhs, rhs); } binary(evaluator: Evaluator, lhs: unknown, rhs: unknown) { diff --git a/src/jsonexpr/operators/match.ts b/src/jsonexpr/operators/match.ts index 6c38c1f..bc4db32 100644 --- a/src/jsonexpr/operators/match.ts +++ b/src/jsonexpr/operators/match.ts @@ -3,6 +3,31 @@ import { BinaryOperator } from "./binary"; const MAX_PATTERN_LENGTH = 1000; const MAX_TEXT_LENGTH = 10000; +const MAX_REGEX_CACHE_SIZE = 100; + +const REDOS_PATTERN = /(\+|\*|\{)\)(\+|\*|\{)/; + +function hasNestedQuantifiers(pattern: string): boolean { + return REDOS_PATTERN.test(pattern); +} + +const regexCache = new Map(); + +function getOrCompileRegex(pattern: string): RegExp { + let compiled = regexCache.get(pattern); + if (compiled !== undefined) { + return compiled; + } + compiled = new RegExp(pattern); + if (regexCache.size >= MAX_REGEX_CACHE_SIZE) { + const firstKey = regexCache.keys().next().value; + if (firstKey !== undefined) { + regexCache.delete(firstKey); + } + } + regexCache.set(pattern, compiled); + return compiled; +} export class MatchOperator extends BinaryOperator { binary(evaluator: Evaluator, text: string | null, pattern: string | null) { @@ -11,18 +36,18 @@ export class MatchOperator extends BinaryOperator { pattern = evaluator.stringConvert(pattern); if (pattern !== null) { if (pattern.length > MAX_PATTERN_LENGTH) { - console.error(`Regex pattern too long: ${pattern.length} > ${MAX_PATTERN_LENGTH}`); return null; } if (text.length > MAX_TEXT_LENGTH) { - console.error(`Text too long for regex matching: ${text.length} > ${MAX_TEXT_LENGTH}`); + return null; + } + if (hasNestedQuantifiers(pattern)) { return null; } try { - const compiled = new RegExp(pattern); + const compiled = getOrCompileRegex(pattern); return compiled.test(text); } catch (error) { - console.error(`Invalid regex pattern: ${pattern}`, error); return null; } } diff --git a/src/matcher.ts b/src/matcher.ts index d3fe4bb..a1d6970 100644 --- a/src/matcher.ts +++ b/src/matcher.ts @@ -10,8 +10,8 @@ export class AudienceMatcher { return this._jsonExpr.evaluateBooleanExpr(audience.filter, vars); } } - } catch (error) { - console.error("Failed to parse audience string:", error); + } catch (_error) { + // parse failure — caller handles null return } return null; diff --git a/src/sdk.ts b/src/sdk.ts index a0b6c08..a93ab29 100644 --- a/src/sdk.ts +++ b/src/sdk.ts @@ -26,7 +26,7 @@ export type SDKOptions = { export default class SDK { static defaultEventLogger: EventLogger = (_, eventName, data) => { if (eventName === "error") { - console.error(data); + console.error(data instanceof Error ? data.message : data); } }; private _eventLogger: EventLogger; @@ -60,7 +60,7 @@ export default class SDK { }; for (const [key, value] of Object.entries(options || {})) { - if (clientOptionKeys.indexOf(key) !== -1) { + if (clientOptionKeys.includes(key)) { extracted[key] = value; } } @@ -68,24 +68,10 @@ export default class SDK { return extracted as ClientOptions; } - /** - * Retrieves context data from the ABSmartly platform. - * @param requestOptions - Optional request configuration - * @returns Promise resolving to context data - */ getContextData(requestOptions: ClientRequestOptions) { return this._provider.getContextData(this, requestOptions); } - /** - * Creates a new experiment context with the specified units. - * The context will asynchronously fetch experiment data from the ABSmartly platform. - * @param params - Context parameters including unit identifiers (e.g., { units: { user_id: "123" } }) - * @param options - Optional context configuration (publishDelay, refreshPeriod, etc.) - * @param requestOptions - Optional request configuration - * @returns A new Context instance - * @throws Error if unit parameters are invalid - */ createContext( params: ContextParams, options?: Partial, @@ -126,15 +112,6 @@ export default class SDK { return this._client; } - /** - * Creates a new experiment context with pre-fetched context data. - * Use this method when you want to manage context data fetching yourself. - * @param params - Context parameters including unit identifiers - * @param data - Pre-fetched context data or a promise resolving to context data - * @param options - Optional context configuration - * @returns A new Context instance - * @throws Error if unit parameters are invalid - */ createContextWith( params: ContextParams, data: ContextData | Promise, @@ -152,13 +129,11 @@ export default class SDK { const NO_PUBLISH_DELAY = -1; const NO_REFRESH = 0; - return Object.assign( - { - publishDelay: isLongLivedApp() ? DEFAULT_PUBLISH_DELAY_MS : NO_PUBLISH_DELAY, - refreshPeriod: NO_REFRESH, - }, - options || {} - ); + return { + publishDelay: isLongLivedApp() ? DEFAULT_PUBLISH_DELAY_MS : NO_PUBLISH_DELAY, + refreshPeriod: NO_REFRESH, + ...options, + }; } private static _validateParams(params: ContextParams) { From d455fe6c71b5c558fea15a7f251f61fe7eafe738 Mon Sep 17 00:00:00 2001 From: Jonas Alves Date: Wed, 18 Mar 2026 13:26:54 +0000 Subject: [PATCH 7/7] fix: address coderabbit review issues - Fix ready() fast path to return !this._failed after context failure - Remove duplicate _logError calls in setTimeout/_setRefreshTimer catch blocks - Widen ClientOptions.agent type to include 'absmartly-javascript-sdk' - Use Partial in _extractClientOptions for proper typing --- src/client.ts | 2 +- src/context.ts | 12 ++++++------ src/sdk.ts | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/client.ts b/src/client.ts index 27a26e4..16e448a 100644 --- a/src/client.ts +++ b/src/client.ts @@ -32,7 +32,7 @@ const DEFAULT_TIMEOUT_MS = 3000; const RETRY_DELAY_MS = 50; export type ClientOptions = { - agent?: "javascript-client"; + agent?: "javascript-client" | "absmartly-javascript-sdk"; apiKey: string; application: string | { name: string; version: number }; endpoint: string; diff --git a/src/context.ts b/src/context.ts index be5cdb4..8377af8 100644 --- a/src/context.ts +++ b/src/context.ts @@ -229,10 +229,10 @@ export default class Context { ready() { if (this.isReady()) { - return Promise.resolve(true); + return Promise.resolve(!this._failed); } - return this._promise?.then(() => true).catch(() => false) ?? Promise.resolve(true); + return this._promise?.then(() => true).catch(() => false) ?? Promise.resolve(!this._failed); } pending() { @@ -847,8 +847,8 @@ export default class Context { else resolve(); }); }); - } catch (error) { - this._logError(error as Error); + } catch { + // _flush already logs publish errors. } }, this._opts.publishDelay); } @@ -1050,8 +1050,8 @@ export default class Context { else resolve(); }); }); - } catch (error) { - this._logError(error as Error); + } catch { + // _refresh already logs refresh errors. } }, this._opts.refreshPeriod); } diff --git a/src/sdk.ts b/src/sdk.ts index a93ab29..8ed8040 100644 --- a/src/sdk.ts +++ b/src/sdk.ts @@ -55,13 +55,13 @@ export default class SDK { "retries", "timeout", ]; - const extracted: Record = { + const extracted: Partial = { agent: "absmartly-javascript-sdk", }; for (const [key, value] of Object.entries(options || {})) { if (clientOptionKeys.includes(key)) { - extracted[key] = value; + (extracted as Record)[key] = value; } }