From fd2e3595bce556836515209b825a5f3c3635bd43 Mon Sep 17 00:00:00 2001 From: Jonathan Puglla Date: Mon, 26 Oct 2026 11:31:15 -0500 Subject: [PATCH 1/8] chore: update dev dependencies for improved compatibility and features. --- package-lock.json | 2796 +++++++++++++++++++++------------------------ package.json | 5 +- 2 files changed, 1285 insertions(+), 1516 deletions(-) diff --git a/package-lock.json b/package-lock.json index d6b1cea4..09010a21 100644 --- a/package-lock.json +++ b/package-lock.json @@ -51,13 +51,14 @@ "@types/node-forge": "^1.3.11", "@types/react": "^18", "@types/react-dom": "^18", - "@vitest/coverage-istanbul": "^1.4.0", + "@vitest/coverage-istanbul": "5.0.5", "eslint": "^9", "eslint-config-next": "16.3.3", "prettier": "^3.2.5", "typescript": "^5.4.5", + "vite": "^8.3.5", "vite-tsconfig-paths": "^4.3.2", - "vitest": "^1.6.1" + "vitest": "5.0.5" }, "engines": { "node": "24.x" @@ -305,6 +306,16 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, + "node_modules/@babel/helper-globals": { + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-8.0.6.tgz", + "integrity": "sha512-OdnA/N52LhzFpeUuVz5YGuSWjc7TPHiFmVYZbPlSgExocO2GwbxVpSni9MVs5wwW+wZ4kPuwBEBN9pIPDYz1mA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, "node_modules/@babel/helper-member-expression-to-functions": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", @@ -430,18 +441,18 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.11.tgz", + "integrity": "sha512-snZbO7MEDOof+hvNa97EjEY7qD7h2kwvQr5AJC3ZhQudKW5dnej6k3kP9NQV88hJwaS8vDqRLFo9rivLEhle2g==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -484,12 +495,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", - "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.11.tgz", + "integrity": "sha512-OZ6+oaK7GMyg3IfCpj8JOmn8n+w2rJbDT0LLfVDTLb1ZFeKDh3AiNELgCMUw175IJHSkLl74hG0M0VLsvx+gag==", "license": "MIT", "dependencies": { - "@babel/types": "^7.27.3" + "@babel/types": "^7.29.11" }, "bin": { "parser": "bin/babel-parser.js" @@ -498,6 +509,19 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/parser/node_modules/@babel/types": { + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.11.tgz", + "integrity": "sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.11", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", @@ -1931,577 +1955,186 @@ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", "license": "MIT" }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", + "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", "cpu": [ - "arm" + "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ - "android" + "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", + "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ - "android" + "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", - "cpu": [ - "x64" - ], + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "android" - ], + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], "engines": { - "node": ">=12" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", - "cpu": [ - "x64" - ], + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", - "cpu": [ - "arm" - ], + "node_modules/@eslint/eslintrc": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", + "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.3.0", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", - "cpu": [ - "arm64" - ], + "node_modules/@eslint/js": { + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", "dev": true, "license": "MIT", - "optional": true, - "os": [ - "linux" - ], "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", - "cpu": [ - "ia32" - ], + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "Apache-2.0", "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", - "cpu": [ - "loong64" - ], + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, "engines": { - "node": ">=12" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", - "cpu": [ - "mips64el" - ], - "dev": true, + "node_modules/@floating-ui/core": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", + "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "dependencies": { + "@floating-ui/utils": "^0.2.8" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", - "cpu": [ - "ppc64" - ], - "dev": true, + "node_modules/@floating-ui/dom": { + "version": "1.6.12", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.12.tgz", + "integrity": "sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==", "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", - "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", - "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", - "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", - "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", - "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.7", - "debug": "^4.3.1", - "minimatch": "^3.1.5" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", - "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.17.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", - "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", - "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.14.0", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.3.0", - "minimatch": "^3.1.5", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "9.39.5", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", - "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", - "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", - "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.17.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@floating-ui/core": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", - "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==", - "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.8" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.6.12", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.12.tgz", - "integrity": "sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==", - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.8" + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.8" } }, "node_modules/@floating-ui/utils": { @@ -3165,38 +2798,23 @@ } }, "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", "dev": true, "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">=8" } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "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==", + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { @@ -3207,24 +2825,17 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -3600,6 +3211,16 @@ "node": ">=12.4.0" } }, + "node_modules/@oxc-project/types": { + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.155.0.tgz", + "integrity": "sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, "node_modules/@playwright/test": { "version": "1.61.1", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.1.tgz", @@ -5359,10 +4980,10 @@ "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1" } }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.4.tgz", - "integrity": "sha512-jfUJrFct/hTA0XDM5p/htWKoNNTbDLY0KRwEt6pyOA6k2fmk0WVwl65PdUdJZgzGEHWx+49LilkcSaumQRyNQw==", + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.15.tgz", + "integrity": "sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw==", "cpu": [ "arm" ], @@ -5371,12 +4992,15 @@ "optional": true, "os": [ "android" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.24.4.tgz", - "integrity": "sha512-j4nrEO6nHU1nZUuCfRKoCcvh7PIywQPUCBa2UsootTHvTHIoIu2BzueInGJhhvQO/2FTRdNYpf63xsgEqH9IhA==", + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.15.tgz", + "integrity": "sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg==", "cpu": [ "arm64" ], @@ -5385,12 +5009,15 @@ "optional": true, "os": [ "android" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.4.tgz", - "integrity": "sha512-GmU/QgGtBTeraKyldC7cDVVvAJEOr3dFLKneez/n7BvX57UdhOqDsVwzU7UOnYA7AAOt+Xb26lk79PldDHgMIQ==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.15.tgz", + "integrity": "sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA==", "cpu": [ "arm64" ], @@ -5399,12 +5026,15 @@ "optional": true, "os": [ "darwin" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.24.4.tgz", - "integrity": "sha512-N6oDBiZCBKlwYcsEPXGDE4g9RoxZLK6vT98M8111cW7VsVJFpNEqvJeIPfsCzbf0XEakPslh72X0gnlMi4Ddgg==", + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.15.tgz", + "integrity": "sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A==", "cpu": [ "x64" ], @@ -5413,26 +5043,15 @@ "optional": true, "os": [ "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.24.4.tgz", - "integrity": "sha512-py5oNShCCjCyjWXCZNrRGRpjWsF0ic8f4ieBNra5buQz0O/U6mMXCpC1LvrHuhJsNPgRt36tSYMidGzZiJF6mw==", - "cpu": [ - "arm64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.24.4.tgz", - "integrity": "sha512-L7VVVW9FCnTTp4i7KrmHeDsDvjB4++KOBENYtNYAiYl96jeBThFfhP6HVxL74v4SiZEVDH/1ILscR5U9S4ms4g==", + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.15.tgz", + "integrity": "sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ==", "cpu": [ "x64" ], @@ -5441,26 +5060,15 @@ "optional": true, "os": [ "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.24.4.tgz", - "integrity": "sha512-10ICosOwYChROdQoQo589N5idQIisxjaFE/PAnX2i0Zr84mY0k9zul1ArH0rnJ/fpgiqfu13TFZR5A5YJLOYZA==", - "cpu": [ - "arm" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.24.4.tgz", - "integrity": "sha512-ySAfWs69LYC7QhRDZNKqNhz2UKN8LDfbKSMAEtoEI0jitwfAG2iZwVqGACJT+kfYvvz3/JgsLlcBP+WWoKCLcw==", + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.15.tgz", + "integrity": "sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ==", "cpu": [ "arm" ], @@ -5469,12 +5077,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.24.4.tgz", - "integrity": "sha512-uHYJ0HNOI6pGEeZ/5mgm5arNVTI0nLlmrbdph+pGXpC9tFHFDQmDMOEqkmUObRfosJqpU8RliYoGz06qSdtcjg==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.15.tgz", + "integrity": "sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw==", "cpu": [ "arm64" ], @@ -5483,12 +5094,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.24.4.tgz", - "integrity": "sha512-38yiWLemQf7aLHDgTg85fh3hW9stJ0Muk7+s6tIkSUOMmi4Xbv5pH/5Bofnsb6spIwD5FJiR+jg71f0CH5OzoA==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.15.tgz", + "integrity": "sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ==", "cpu": [ "arm64" ], @@ -5497,12 +5111,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.24.4.tgz", - "integrity": "sha512-q73XUPnkwt9ZNF2xRS4fvneSuaHw2BXuV5rI4cw0fWYVIWIBeDZX7c7FWhFQPNTnE24172K30I+dViWRVD9TwA==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.15.tgz", + "integrity": "sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw==", "cpu": [ "ppc64" ], @@ -5511,67 +5128,83 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.24.4.tgz", - "integrity": "sha512-Aie/TbmQi6UXokJqDZdmTJuZBCU3QBDA8oTKRGtd4ABi/nHgXICulfg1KI6n9/koDsiDbvHAiQO3YAUNa/7BCw==", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.15.tgz", + "integrity": "sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA==", "cpu": [ - "riscv64" + "s390x" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.24.4.tgz", - "integrity": "sha512-P8MPErVO/y8ohWSP9JY7lLQ8+YMHfTI4bAdtCi3pC2hTeqFJco2jYspzOzTUB8hwUWIIu1xwOrJE11nP+0JFAQ==", + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.15.tgz", + "integrity": "sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw==", "cpu": [ - "s390x" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.24.4.tgz", - "integrity": "sha512-K03TljaaoPK5FOyNMZAAEmhlyO49LaE4qCsr0lYHUKyb6QacTNF9pnfPpXnFlFD3TXuFbFbz7tJ51FujUXkXYA==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.15.tgz", + "integrity": "sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ==", "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.24.4.tgz", - "integrity": "sha512-VJYl4xSl/wqG2D5xTYncVWW+26ICV4wubwN9Gs5NrqhJtayikwCXzPL8GDsLnaLU3WwhQ8W02IinYSFJfyo34Q==", + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.15.tgz", + "integrity": "sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w==", "cpu": [ - "x64" + "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ - "linux" - ] + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.24.4.tgz", - "integrity": "sha512-ku2GvtPwQfCqoPFIJCqZ8o7bJcj+Y54cZSr43hHca6jLwAiCbZdBUOrqE6y29QFajNAzzpIOwsckaTFmN6/8TA==", + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.15.tgz", + "integrity": "sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg==", "cpu": [ "arm64" ], @@ -5580,34 +5213,46 @@ "optional": true, "os": [ "win32" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.24.4.tgz", - "integrity": "sha512-V3nCe+eTt/W6UYNr/wGvO1fLpHUrnlirlypZfKCT1fG6hWfqhPgQV/K/mRBXBpxc0eKLIF18pIOFVPh0mqHjlg==", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.15.tgz", + "integrity": "sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ==", "cpu": [ - "ia32" + "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "win32" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.24.4.tgz", - "integrity": "sha512-LTw1Dfd0mBIEqUVCxbvTE/LLo+9ZxVC9k99v1v4ahg9Aak6FpqOfNu5kRkeTAn0wphoC4JU7No1/rL+bBCEwhg==", + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.65.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.65.1.tgz", + "integrity": "sha512-44IKzBPqXXVm0kt1O+F5eW4juR5FyIxGNlFkpP8qwIDzucBsjM1HCbdYUQrQw8rU/g6HRl/tfBm0+Qq4H4Ro3w==", "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ - "win32" + "linux" ] }, "node_modules/@rtsao/scc": { @@ -5617,13 +5262,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, - "license": "MIT" - }, "node_modules/@svgr/babel-plugin-add-jsx-attribute": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-8.0.0.tgz", @@ -5910,6 +5548,17 @@ "@types/estree": "*" } }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -5918,10 +5567,17 @@ "@types/ms": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "license": "MIT" }, "node_modules/@types/estree-jsx": { @@ -5932,6 +5588,13 @@ "@types/estree": "*" } }, + "node_modules/@types/gensync": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/gensync/-/gensync-1.0.5.tgz", + "integrity": "sha512-MbsRCT7mTikHwKZ0X+LVUTLRrZZRLipTuXEO9qOYO+zmjMVk81axyClMROf6uoPD9MRVu46bx8zoR0Ad9q3NAg==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/hast": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", @@ -5946,6 +5609,13 @@ "integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ==", "license": "MIT" }, + "node_modules/@types/jsesc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@types/jsesc/-/jsesc-2.5.1.tgz", + "integrity": "sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -6702,128 +6372,334 @@ } }, "node_modules/@vitest/coverage-istanbul": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/coverage-istanbul/-/coverage-istanbul-1.6.1.tgz", - "integrity": "sha512-0NWKNPrbMo1s6emwnn+UpGPxrSEd9R6VpQ3wzYz0y43esZjjDkGLb6Qkvfu6LNyQO4TAGyepaZ11imUmmIFLaw==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/coverage-istanbul/-/coverage-istanbul-5.0.5.tgz", + "integrity": "sha512-nmbE0+e1JSH9kYEoHGp/iKCQq6+TqwvQ4EcH2zKPULOIrY7rPHxDHsipYGGr+0xMDdzCy46F8zj3edz8dsqvdg==", "dev": true, "license": "MIT", "dependencies": { - "debug": "^4.3.4", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-instrument": "^6.0.1", - "istanbul-lib-report": "^3.0.1", - "istanbul-lib-source-maps": "^5.0.4", - "istanbul-reports": "^3.1.6", - "magicast": "^0.3.3", - "picocolors": "^1.0.0", - "test-exclude": "^6.0.0" + "@jridgewell/gen-mapping": "^0.3.13", + "@jridgewell/trace-mapping": "0.3.31", + "@vitest/istanbul-lib-coverage": "^1.0.2", + "@vitest/istanbul-lib-instrument": "^1.0.2", + "@vitest/istanbul-lib-report": "^1.0.2", + "@vitest/istanbul-lib-source-maps": "^1.0.2", + "magicast": "^0.5.5", + "obug": "^3.0.0", + "tinyrainbow": "^3.2.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "1.6.1" + "vitest": "5.0.5" } }, - "node_modules/@vitest/expect": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.6.1.tgz", - "integrity": "sha512-jXL+9+ZNIJKruofqXuuTClf44eSpcHlgj3CiuNihUF3Ioujtmc0zIa3UJOW5RjDK1YLBJZnWBlPuqhYycLioog==", + "node_modules/@vitest/istanbul-lib-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@vitest/istanbul-lib-coverage/-/istanbul-lib-coverage-1.0.2.tgz", + "integrity": "sha512-9J/JMwOf9AoJhAywhrn7ScKTL38hsWQP/qPG60OtaAFcQ5OXPwKsxZFlbnuCKmZ61m8/lGgHYnFpdyQZUvG/iA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + } + }, + "node_modules/@vitest/istanbul-lib-instrument": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@vitest/istanbul-lib-instrument/-/istanbul-lib-instrument-1.0.2.tgz", + "integrity": "sha512-KFlvK0uYxYjdNoQJR+6F1B45u0ZOmYgOZndqfwLiGBjYHACc8q1Wyuw/ZQeGP3UKc7XsSZ51NKQDKt0IIbGMQg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "1.6.1", - "@vitest/utils": "1.6.1", - "chai": "^4.3.10" + "@babel/core": "^8.0.1", + "@babel/parser": "^8.0.4", + "@istanbuljs/schema": "^0.1.3", + "@jridgewell/trace-mapping": "^0.3.23", + "@vitest/istanbul-lib-coverage": "1.0.2" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">=22" } }, - "node_modules/@vitest/runner": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.6.1.tgz", - "integrity": "sha512-3nSnYXkVkf3mXFfE7vVyPmi3Sazhb/2cfZGGs0JRzFsPFvAMBEcrweV1V1GsrstdXeKCTXlJbvnQwGWgEIHmOA==", + "node_modules/@vitest/istanbul-lib-instrument/node_modules/@babel/code-frame": { + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-8.0.6.tgz", + "integrity": "sha512-uMHhvmDmwFiLuiozaoRYTZHqMFuu3fem/X9JY78lohAuLoh8LSGGC8JqUzo2cVuBsBxkjMZNk56CE7ock8Xxkw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "1.6.1", - "p-limit": "^5.0.0", - "pathe": "^1.1.1" + "@babel/helper-validator-identifier": "^8.0.6", + "js-tokens": "^10.0.0" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": "^22.18.0 || >=24.11.0" } }, - "node_modules/@vitest/runner/node_modules/p-limit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", - "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "node_modules/@vitest/istanbul-lib-instrument/node_modules/@babel/compat-data": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-8.0.5.tgz", + "integrity": "sha512-YLsYoQMvL8l8WrGpN3Zj7O1wK5LEBN+cQtux7BcuHyxIXve724XG+zuJ1n3U1cUweRtTzQOA4IHbuQw3N34SZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@vitest/istanbul-lib-instrument/node_modules/@babel/core": { + "version": "8.0.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-8.0.9.tgz", + "integrity": "sha512-wXtyKlpR/pXf/u0vmib+OBakQaIzLglLcRoKpWxCHW/whqq7YfQYPD0IAAl9H/evi40MyLIjYYdvb6N+9DI2yA==", "dev": true, "license": "MIT", "dependencies": { - "yocto-queue": "^1.0.0" + "@babel/code-frame": "^8.0.6", + "@babel/generator": "^8.0.6", + "@babel/helper-compilation-targets": "^8.0.7", + "@babel/helpers": "^8.0.8", + "@babel/parser": "^8.0.8", + "@babel/template": "^8.0.0", + "@babel/traverse": "^8.0.7", + "@babel/types": "^8.0.8", + "@types/gensync": "^1.0.5", + "convert-source-map": "^2.0.0", + "empathic": "^2.0.1", + "gensync": "^1.0.0-beta.2", + "import-meta-resolve": "^4.2.0", + "json5": "^2.2.3", + "obug": "^3.0.0", + "verkit": "^0.5.0" }, "engines": { - "node": ">=18" + "node": "^22.18.0 || >=24.11.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@vitest/runner/node_modules/yocto-queue": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", - "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", + "node_modules/@vitest/istanbul-lib-instrument/node_modules/@babel/generator": { + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-8.0.6.tgz", + "integrity": "sha512-/pcanjdRsonCexCuxgGD8fKnBLLzk2i9uM4kmcmGyyev2R5YDs+rafMYZ89RvaGY1mW8T7C5au0TyYaEeHBGng==", "dev": true, "license": "MIT", + "dependencies": { + "@babel/parser": "^8.0.6", + "@babel/types": "^8.0.6", + "@jridgewell/gen-mapping": "0.4.0-beta.0", + "@jridgewell/trace-mapping": "^0.3.31", + "@types/jsesc": "^2.5.0", + "jsesc": "^3.0.2" + }, "engines": { - "node": ">=12.20" + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@vitest/istanbul-lib-instrument/node_modules/@babel/helper-compilation-targets": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-8.0.7.tgz", + "integrity": "sha512-ilAZCdOOlOVA7dQbaFNw2f/59p6GW33NxTopc3DvEE3efp3ZXTrGX/yZScaKw8/vd9bq31Ag0EcXiLw32bAgJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^8.0.5", + "@babel/helper-validator-option": "^8.0.0", + "browserslist": "^4.24.0", + "flru": "1.0.2", + "verkit": "^0.5.0" }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "engines": { + "node": "^22.18.0 || >=24.11.0" } }, - "node_modules/@vitest/snapshot": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.6.1.tgz", - "integrity": "sha512-WvidQuWAzU2p95u8GAKlRMqMyN1yOJkGHnx3M1PL9Raf7AQ1kwLKg04ADlCa3+OXUZE7BceOhVZiuWAbzCKcUQ==", + "node_modules/@vitest/istanbul-lib-instrument/node_modules/@babel/helper-string-parser": { + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-8.0.8.tgz", + "integrity": "sha512-fVM+gSuKbdv9cpmyRNh5EvsDHUZCdxQH3FdnFKSkNYHpbsn95bbrxm+ES3MVeQjcqwi2O5ur/DjLfmTQaO60ng==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@vitest/istanbul-lib-instrument/node_modules/@babel/helper-validator-identifier": { + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-8.0.6.tgz", + "integrity": "sha512-HYx0YmuchRxRSpnjvcNffnPS8DCauKNmnumIuHG3jnQAVewRA7kfJGwF13ayEpSZs7s4R/oBH4eppuXcAQd5JQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@vitest/istanbul-lib-instrument/node_modules/@babel/helper-validator-option": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-8.0.0.tgz", + "integrity": "sha512-U4Dybxh4WESWHt5XhBeExi4DrY0/DNK1aHpQbsrQXCUbFHuMweT0TpLEWKvaraV2Y6fS+ZXunsZ8zIuZIgvF2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@vitest/istanbul-lib-instrument/node_modules/@babel/helpers": { + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-8.0.8.tgz", + "integrity": "sha512-FBmhiDWs625wHg03OKiLQDeT6LOc2lWBbW97Yqs2iI1tZcucjpmYnO29NHsQ83XxKvredcvUymW/AAn2UtnakA==", "dev": true, "license": "MIT", "dependencies": { - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "pretty-format": "^29.7.0" + "@babel/template": "^8.0.0", + "@babel/types": "^8.0.8" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": "^22.18.0 || >=24.11.0" } }, - "node_modules/@vitest/spy": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.6.1.tgz", - "integrity": "sha512-MGcMmpGkZebsMZhbQKkAf9CX5zGvjkBTqf8Zx3ApYWXr3wG+QvEu2eXWfnIIWYSJExIp4V9FCKDEeygzkYrXMw==", + "node_modules/@vitest/istanbul-lib-instrument/node_modules/@babel/parser": { + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-8.0.8.tgz", + "integrity": "sha512-Gq65JogAr0S2p0BzvfSJboyuiRnxDQApyOqHkKmkrvm+VlLK4gC3pKsiOKEdnhFBemrFbSnOu90mZZWDpiJnmQ==", "dev": true, "license": "MIT", "dependencies": { - "tinyspy": "^2.2.0" + "@babel/types": "^8.0.8" }, - "funding": { - "url": "https://opencollective.com/vitest" + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" } }, - "node_modules/@vitest/utils": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.6.1.tgz", - "integrity": "sha512-jOrrUvXM4Av9ZWiG1EajNto0u96kWAhJ1LmPmJhXXQx/32MecEKd10pOLYgS2BQx1TgkGhloPU1ArDW2vvaY6g==", + "node_modules/@vitest/istanbul-lib-instrument/node_modules/@babel/template": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-8.0.0.tgz", + "integrity": "sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==", "dev": true, "license": "MIT", "dependencies": { - "diff-sequences": "^29.6.3", - "estree-walker": "^3.0.3", - "loupe": "^2.3.7", - "pretty-format": "^29.7.0" + "@babel/code-frame": "^8.0.0", + "@babel/parser": "^8.0.0", + "@babel/types": "^8.0.0" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@vitest/istanbul-lib-instrument/node_modules/@babel/traverse": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-8.0.7.tgz", + "integrity": "sha512-bVV0brinsTWgc6ptSIoQo3GbITtl0VZAqEUJbHHcyfDPEu+q/fEvlYEa+6H85HoK8IdksKBFtKJbJFZ6Kff+zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^8.0.6", + "@babel/generator": "^8.0.6", + "@babel/helper-globals": "^8.0.6", + "@babel/parser": "^8.0.7", + "@babel/template": "^8.0.0", + "@babel/types": "^8.0.6", + "obug": "^3.0.0" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@vitest/istanbul-lib-instrument/node_modules/@babel/types": { + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-8.0.8.tgz", + "integrity": "sha512-iXSSZAKQWmaeAwGtFw10v9fg5hUiUs0L+w+Z2K11kym3pDNK+SF0VcAQPsEFvWcTSZ7LMgClMXDBvTbH0IlICg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^8.0.8", + "@babel/helper-validator-identifier": "^8.0.6" + }, + "engines": { + "node": "^22.18.0 || >=24.11.0" + } + }, + "node_modules/@vitest/istanbul-lib-instrument/node_modules/@jridgewell/gen-mapping": { + "version": "0.4.0-beta.0", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.4.0-beta.0.tgz", + "integrity": "sha512-JdGNkbE4GlNPYQhM0L95fBQr7ctLZJ276QXQLTad4t1oSdnnCI3fDq9DW3BqYAWv8Wc3+HS+4Gsii1oPMCfz1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.6.0-beta.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@vitest/istanbul-lib-instrument/node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/istanbul-lib-instrument/node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@vitest/istanbul-lib-report": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@vitest/istanbul-lib-report/-/istanbul-lib-report-1.0.2.tgz", + "integrity": "sha512-gUsfXZJbzPamoIY5TvHFiMMoXESBrUMo+xqaj+rYrWI69+EnvRlYBlP96ZnHPY4vX8kyUpgAnFUCg5wZG/HkDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/istanbul-lib-coverage": "1.0.2" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@vitest/istanbul-lib-source-maps": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@vitest/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.0.2.tgz", + "integrity": "sha512-Ia3+9WDB5UKN940ztJ5LyhqDaS/It05Uh2VQ70KIPDWlOed2ZsJTJTyMb7x6OIW4eAwWSBFknpqJafJetTTXLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "@vitest/istanbul-lib-coverage": "1.0.2", + "obug": "^2.1.4" }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@vitest/istanbul-lib-source-maps/node_modules/obug": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.2.1.tgz", + "integrity": "sha512-XrsrhT5sybtKI6wakr2SPOlGZWWYbUXZ7a0jT8/QOeAPau+1X/bSegNe5YR75oJmEZQbKningirmGOEJCIk61Q==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/@vitest/spy": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.5.tgz", + "integrity": "sha512-lwJRExuH417257Q5wv9QYugN9IMJkoxcrKrYxGPQkBzl7wlNpTUxD/5yNX4ESKVUIShKPgkGchYPFMWOOk0wzw==", + "dev": true, + "license": "MIT", "funding": { "url": "https://opencollective.com/vitest" } @@ -6848,15 +6724,6 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/acorn-walk": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", - "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/ajv": { "version": "6.15.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", @@ -7078,13 +6945,13 @@ } }, "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", "engines": { - "node": "*" + "node": ">=12" } }, "node_modules/ast-types-flow": { @@ -7180,6 +7047,15 @@ "node": ">=10" } }, + "node_modules/babel-plugin-macros/node_modules/yaml": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/babel-plugin-polyfill-corejs2": { "version": "0.4.11", "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.11.tgz", @@ -7339,16 +7215,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/cac": { - "version": "6.7.14", - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/call-bind": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", @@ -7449,22 +7315,13 @@ } }, "node_modules/chai": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", - "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.3.0.tgz", + "integrity": "sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==", "dev": true, "license": "MIT", - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.1.0" - }, "engines": { - "node": ">=4" + "node": ">=18" } }, "node_modules/chalk": { @@ -7520,19 +7377,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.2" - }, - "engines": { - "node": "*" - } - }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -7859,26 +7703,13 @@ "node_modules/decode-named-character-reference": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz", - "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/deep-eql": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", - "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", - "dev": true, - "license": "MIT", + "integrity": "sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==", "dependencies": { - "type-detect": "^4.0.0" + "character-entities": "^2.0.0" }, - "engines": { - "node": ">=6" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" } }, "node_modules/deep-is": { @@ -7942,8 +7773,8 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "devOptional": true, "license": "Apache-2.0", - "optional": true, "engines": { "node": ">=8" } @@ -7960,16 +7791,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -8097,6 +7918,16 @@ "dev": true, "license": "MIT" }, + "node_modules/empathic": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.1.0.tgz", + "integrity": "sha512-AnfC1ATldl49/cvZdLPDjBfrRNwbDO05aibiOtzQu3qtlbJtomNLhF30HEtn/7iBz50dlMECqATo3fG0LrdEgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -8252,6 +8083,13 @@ "node": ">= 0.4" } }, + "node_modules/es-module-lexer": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", + "dev": true, + "license": "MIT" + }, "node_modules/es-object-atoms": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", @@ -8347,45 +8185,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" - } - }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -8978,27 +8777,14 @@ "node": ">=0.10.0" } }, - "node_modules/execa": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", - "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^8.0.1", - "human-signals": "^5.0.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^4.1.0", - "strip-final-newline": "^3.0.0" - }, + "license": "Apache-2.0", "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" + "node": ">=12.0.0" } }, "node_modules/extend": { @@ -9103,6 +8889,16 @@ "dev": true, "license": "ISC" }, + "node_modules/flru": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/flru/-/flru-1.0.2.tgz", + "integrity": "sha512-kWyh8ADvHBFz6ua5xYOPnUroZTT/bwWfrCeL0Wj1dzG4/YOmOcfJ99W8dOVyyynJN35rZ9aCOtHChqQovV7yog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -9119,12 +8915,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -9198,16 +8988,6 @@ "node": ">=6.9.0" } }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -9247,18 +9027,6 @@ "node": ">= 0.4" } }, - "node_modules/get-stream": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", - "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", - "dev": true, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/get-symbol-description": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", @@ -9636,12 +9404,6 @@ "react-is": "^16.7.0" } }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, "node_modules/html-url-attributes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.0.tgz", @@ -9660,15 +9422,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/human-signals": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", - "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", - "dev": true, - "engines": { - "node": ">=16.17.0" - } - }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -9699,6 +9452,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -9708,22 +9472,6 @@ "node": ">=0.8.19" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, "node_modules/inline-style-parser": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.3.tgz", @@ -10138,18 +9886,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/is-string": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", @@ -10260,72 +9996,6 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz", - "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.4.tgz", - "integrity": "sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/iterator.prototype": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", @@ -10432,77 +10102,332 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" }, - "bin": { - "json5": "lib/cli.js" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/jsonc-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", - "dev": true - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=4.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/language-subtag-registry": { - "version": "0.3.23", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", - "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "CC0-1.0" + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } }, - "node_modules/language-tags": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", - "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "language-subtag-registry": "^0.3.20" + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.10" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 0.8.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/lines-and-columns": { @@ -10511,22 +10436,6 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "license": "MIT" }, - "node_modules/local-pkg": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", - "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", - "dev": true, - "dependencies": { - "mlly": "^1.4.2", - "pkg-types": "^1.0.3" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -10587,16 +10496,6 @@ "loose-envify": "cli.js" } }, - "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.1" - } - }, "node_modules/lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -10607,39 +10506,39 @@ } }, "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.4.tgz", + "integrity": "sha512-jQz9sVhrZuphH4NrAIHSgkDJCluQO7Ioi4KGRLa7aTUjAODQ13hENX0YIytqYE/rIkO485gQG2LeBxJaW53kkQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" + "@jridgewell/sourcemap-codec": "^1.6.0" } }, "node_modules/magicast": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.3.tgz", - "integrity": "sha512-ZbrP1Qxnpoes8sz47AM0z08U+jW6TyRgZzcWy3Ma3vDhJttwMwAFDMMQFobwdBxByBD46JYmxRzeF7w2+wJEuw==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.5.tgz", + "integrity": "sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/parser": "^7.23.6", - "@babel/types": "^7.23.6", - "source-map-js": "^1.0.2" + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "source-map-js": "^1.2.1" } }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "node_modules/magicast/node_modules/@babel/types": { + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.11.tgz", + "integrity": "sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==", "dev": true, + "license": "MIT", "dependencies": { - "semver": "^7.5.3" + "@babel/helper-string-parser": "^7.29.11", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6.9.0" } }, "node_modules/markdown-extensions": { @@ -10838,12 +10737,6 @@ "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", "license": "MIT" }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -11444,18 +11337,6 @@ "node": ">=8.6" } }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -11479,18 +11360,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/mlly": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.6.1.tgz", - "integrity": "sha512-vLgaHvaeunuOXHSmEbZ9izxPx3USsk8KCQ8iC+aTlp5sKRSoZvwhHh5L9VbKSaVC6sJDqbyohIS76E2VmHIPAA==", - "dev": true, - "dependencies": { - "acorn": "^8.11.3", - "pathe": "^1.1.2", - "pkg-types": "^1.0.3", - "ufo": "^1.3.2" - } - }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -11672,33 +11541,6 @@ "node": ">=0.10.0" } }, - "node_modules/npm-run-path": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", - "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", - "dev": true, - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -11830,28 +11672,18 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "node_modules/obug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/obug/-/obug-3.0.0.tgz", + "integrity": "sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==", "dev": true, - "dependencies": { - "mimic-fn": "^4.0.0" - }, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=12.20.0" } }, "node_modules/optionator": { @@ -11993,15 +11825,6 @@ "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -12024,22 +11847,6 @@ "node": ">=8" } }, - "node_modules/pathe": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", - "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", - "dev": true - }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -12057,17 +11864,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pkg-types": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", - "integrity": "sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==", - "dev": true, - "dependencies": { - "jsonc-parser": "^3.2.0", - "mlly": "^1.2.0", - "pathe": "^1.1.0" - } - }, "node_modules/playwright": { "version": "1.61.1", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", @@ -12194,41 +11990,6 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, "node_modules/prismjs": { "version": "1.30.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", @@ -12816,42 +12577,38 @@ "node": ">=0.10.0" } }, - "node_modules/rollup": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.24.4.tgz", - "integrity": "sha512-vGorVWIsWfX3xbcyAS+I047kFKapHYivmkaT63Smj77XwvLSJos6M1xGqZnBPFQFBRZDOcG1QnYEIxAvTr/HjA==", + "node_modules/rolldown": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.15.tgz", + "integrity": "sha512-6odND+fel5zVw3I4Yc2e69gvguDnX0CAAGiNDZSq4ik5WUcaLrkD2GLuog0XcODbor31hZ5ayEMgVDno+KPV5g==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.6" + "@oxc-project/types": "=0.155.0", + "@rolldown/pluginutils": "^1.0.0" }, "bin": { - "rollup": "dist/bin/rollup" + "rolldown": "bin/cli.mjs" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.24.4", - "@rollup/rollup-android-arm64": "4.24.4", - "@rollup/rollup-darwin-arm64": "4.24.4", - "@rollup/rollup-darwin-x64": "4.24.4", - "@rollup/rollup-freebsd-arm64": "4.24.4", - "@rollup/rollup-freebsd-x64": "4.24.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.24.4", - "@rollup/rollup-linux-arm-musleabihf": "4.24.4", - "@rollup/rollup-linux-arm64-gnu": "4.24.4", - "@rollup/rollup-linux-arm64-musl": "4.24.4", - "@rollup/rollup-linux-powerpc64le-gnu": "4.24.4", - "@rollup/rollup-linux-riscv64-gnu": "4.24.4", - "@rollup/rollup-linux-s390x-gnu": "4.24.4", - "@rollup/rollup-linux-x64-gnu": "4.24.4", - "@rollup/rollup-linux-x64-musl": "4.24.4", - "@rollup/rollup-win32-arm64-msvc": "4.24.4", - "@rollup/rollup-win32-ia32-msvc": "4.24.4", - "@rollup/rollup-win32-x64-msvc": "4.24.4", - "fsevents": "~2.3.2" + "@rolldown/binding-android-arm-eabi": "1.2.15", + "@rolldown/binding-android-arm64": "1.2.15", + "@rolldown/binding-darwin-arm64": "1.2.15", + "@rolldown/binding-darwin-x64": "1.2.15", + "@rolldown/binding-freebsd-x64": "1.2.15", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.15", + "@rolldown/binding-linux-arm64-gnu": "1.2.15", + "@rolldown/binding-linux-arm64-musl": "1.2.15", + "@rolldown/binding-linux-ppc64-gnu": "1.2.15", + "@rolldown/binding-linux-s390x-gnu": "1.2.15", + "@rolldown/binding-linux-x64-gnu": "1.2.15", + "@rolldown/binding-linux-x64-musl": "1.2.15", + "@rolldown/binding-openharmony-arm64": "1.2.15", + "@rolldown/binding-win32-arm64-msvc": "1.2.15", + "@rolldown/binding-win32-x64-msvc": "1.2.15" } }, "node_modules/run-parallel": { @@ -13182,24 +12939,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/snake-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", @@ -13219,9 +12958,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -13243,17 +12982,12 @@ "dev": true, "license": "MIT" }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true - }, "node_modules/std-env": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", - "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", - "dev": true + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.3.0.tgz", + "integrity": "sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==", + "dev": true, + "license": "MIT" }, "node_modules/stop-iteration-iterator": { "version": "1.1.0", @@ -13405,18 +13139,6 @@ "node": ">=4" } }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -13430,24 +13152,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strip-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.1.0.tgz", - "integrity": "sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==", - "dev": true, - "dependencies": { - "js-tokens": "^9.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/strip-literal/node_modules/js-tokens": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.0.tgz", - "integrity": "sha512-WriZw1luRMlmV3LGJaR6QOJjWwgLUTf89OwT2lUOyjX2dJGBwgmIkbcz+7WFZjrZM635JOIR517++e/67CP9dQ==", - "dev": true - }, "node_modules/style-to-object": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.6.tgz", @@ -13539,51 +13243,31 @@ "url": "https://opencollective.com/svgo" } }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "node_modules/third-party-capital": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/third-party-capital/-/third-party-capital-1.0.20.tgz", + "integrity": "sha512-oB7yIimd8SuGptespDAZnNkzIz+NWaJCu2RMsbs4Wmp9zSDUM8Nhi3s2OOcqYuv3mN4hitXc8DVx+LyUmbUDiA==" + }, + "node_modules/tinybench": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.2.tgz", + "integrity": "sha512-nIZ2N2dFpjBsg2hgitKu7mA+FvsIJuGJOwysRngmVRRmWgR/UyPjwJj8c/fT/dhm8EkHnF5EOogRf+0Z2qrimA==", "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=20.0.0" } }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "node_modules/tinyexec": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, + "license": "MIT", "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=18" } }, - "node_modules/third-party-capital": { - "version": "1.0.20", - "resolved": "https://registry.npmjs.org/third-party-capital/-/third-party-capital-1.0.20.tgz", - "integrity": "sha512-oB7yIimd8SuGptespDAZnNkzIz+NWaJCu2RMsbs4Wmp9zSDUM8Nhi3s2OOcqYuv3mN4hitXc8DVx+LyUmbUDiA==" - }, - "node_modules/tinybench": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.6.0.tgz", - "integrity": "sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==", - "dev": true - }, "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", @@ -13632,19 +13316,10 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/tinypool": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.3.tgz", - "integrity": "sha512-Ud7uepAklqRH1bvwy22ynrliC7Dljz7Tm8M/0RBUW+YRa4YHhZ6e4PpgE+fu1zr/WqB1kbeuVrdfeuyIBpy4tw==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-2.2.1.tgz", - "integrity": "sha512-KYad6Vy5VDWV4GH3fjpseMQ/XU2BhIYP7Vzd0LG44qRWm/Yt2WCOTicFdvmgo6gWaqooMQCawTtILVQJupKu7A==", + "node_modules/tinyrainbow": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.2.0.tgz", + "integrity": "sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==", "dev": true, "license": "MIT", "engines": { @@ -14172,16 +13847,6 @@ "node": ">= 0.8.0" } }, - "node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/typed-array-buffer": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", @@ -14296,12 +13961,6 @@ "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/ufo": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.5.3.tgz", - "integrity": "sha512-Y7HYmWaFwPUmkoQCUIAYpKqkOf+SbVj/2fJJZ4RJMCfZp0rTGwRbzQD+HghfnhKOjL9E01okqz+ncJskGYfBNw==", - "dev": true - }, "node_modules/unbox-primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", @@ -14575,6 +14234,19 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/verkit": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/verkit/-/verkit-0.5.0.tgz", + "integrity": "sha512-u4J1hr6Y+C/hit34GwL3eIcUf3wngT8TlYUjK7tk0jxydKLAq3sYvifjdTDA6U3KCKRcQCQDGf/srjxfZF3l7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, "node_modules/vfile": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.1.tgz", @@ -14616,21 +14288,23 @@ } }, "node_modules/vite": { - "version": "5.4.19", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.19.tgz", - "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.5.tgz", + "integrity": "sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.21.3", - "postcss": "^8.4.43", - "rollup": "^4.20.0" + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.30", + "rolldown": "~1.2.14", + "tinyglobby": "^0.2.17" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -14639,23 +14313,33 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || >=20.0.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.7.1 || ^0.8.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, - "less": { + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { "optional": true }, - "lightningcss": { + "less": { "optional": true }, "sass": { @@ -14672,32 +14356,15 @@ }, "terser": { "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } } }, - "node_modules/vite-node": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.6.1.tgz", - "integrity": "sha512-YAXkfvGtuTzwWbDSACdJSg4A4DZiAqckWe90Zapc/sEX3XvHcw1NdurM/6od8J207tSDqNbSsgdCacBgvJKFuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.3.4", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "vite": "^5.0.0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || >=20.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, "node_modules/vite-tsconfig-paths": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-4.3.2.tgz", @@ -14717,59 +14384,134 @@ } } }, + "node_modules/vite/node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vite/node_modules/postcss": { + "version": "8.5.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz", + "integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.20", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/vitest": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.6.1.tgz", - "integrity": "sha512-Ljb1cnSJSivGN0LqXd/zmDbWEM0RNNg2t1QW/XUhYl/qPqyu7CsqeWtqQXHVaJsecLPuDoak2oJcZN2QoRIOag==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/expect": "1.6.1", - "@vitest/runner": "1.6.1", - "@vitest/snapshot": "1.6.1", - "@vitest/spy": "1.6.1", - "@vitest/utils": "1.6.1", - "acorn-walk": "^8.3.2", - "chai": "^4.3.10", - "debug": "^4.3.4", - "execa": "^8.0.1", - "local-pkg": "^0.5.0", - "magic-string": "^0.30.5", - "pathe": "^1.1.1", - "picocolors": "^1.0.0", - "std-env": "^3.5.0", - "strip-literal": "^2.0.0", - "tinybench": "^2.5.1", - "tinypool": "^0.8.3", - "vite": "^5.0.0", - "vite-node": "1.6.1", - "why-is-node-running": "^2.2.2" + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.5.tgz", + "integrity": "sha512-cPODOMF8YnkiiYtDdHo6n4wWmBrOBBbOIYH2d33M4l3CdNn72h+qMmO6uXffUN6A5AZFXNND/d+krS9mgkXRfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/mocker": "5.0.5", + "chai": "^6.3.0", + "es-module-lexer": "^2.3.2", + "expect-type": "^1.4.0", + "magic-string": "^1.4.3", + "obug": "^3.0.0", + "picomatch": "^4.0.7", + "std-env": "^4.3.0", + "tinybench": "^6.2.2", + "tinyexec": "^1.3.1", + "tinyglobby": "^0.2.17", + "why-is-node-running": "3.2.1" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": "^18.0.0 || >=20.0.0" + "node": "^22.12.0 || ^24.0.0 || >=26.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "@edge-runtime/vm": "*", - "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.6.1", - "@vitest/ui": "1.6.1", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "5.0.5", + "@vitest/browser-preview": "5.0.5", + "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", + "@vitest/coverage-istanbul": "5.0.5", + "@vitest/coverage-v8": "5.0.5", + "@vitest/ui": "5.0.5", "happy-dom": "*", - "jsdom": "*" + "jsdom": "*", + "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { "optional": true }, + "@opentelemetry/api": { + "optional": true + }, "@types/node": { "optional": true }, - "@vitest/browser": { + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { "optional": true }, "@vitest/ui": { @@ -14780,9 +14522,53 @@ }, "jsdom": { "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/vitest/node_modules/@vitest/mocker": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.5.tgz", + "integrity": "sha512-xLL3lkDegJZw+MGoGaCCsFayDZthGgpAtr3Vziyfc4p7xH89K7KRPcRBBh/kEYm0Aqkt5DtTmkYpBtRV/FrHZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.31", + "@vitest/spy": "5.0.5", + "estree-walker": "^3.0.3", + "magic-string": "^1.4.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9 || ^3.0.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true } } }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/web-namespaces": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", @@ -14897,34 +14683,16 @@ } }, "node_modules/why-is-node-running": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.2.2.tgz", - "integrity": "sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-3.2.1.tgz", + "integrity": "sha512-Tb2FUhB4vUsGQlfSquQLYkApkuPAFQXGFzxWKHHumVz2dK+X1RUm/HnID4+TfIGYJ1kTcwOaCk/buYCEJr6YjQ==", "dev": true, - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, + "license": "MIT", "bin": { "why-is-node-running": "cli.js" }, "engines": { - "node": ">=8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, - "node_modules/yaml": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", - "license": "ISC", - "engines": { - "node": ">= 6" + "node": ">=20.11" } }, "node_modules/yocto-queue": { diff --git a/package.json b/package.json index 12a265b5..cb447d98 100644 --- a/package.json +++ b/package.json @@ -61,12 +61,13 @@ "@types/node-forge": "^1.3.11", "@types/react": "^18", "@types/react-dom": "^18", - "@vitest/coverage-istanbul": "^1.4.0", + "@vitest/coverage-istanbul": "5.0.5", "eslint": "^9", "eslint-config-next": "16.3.3", "prettier": "^3.2.5", "typescript": "^5.4.5", + "vite": "^8.3.5", "vite-tsconfig-paths": "^4.3.2", - "vitest": "^1.6.1" + "vitest": "5.0.5" } } From 8688151d6c17725eea193aaef506a21cfd62db42 Mon Sep 17 00:00:00 2001 From: Jonathan Puglla Date: Mon, 26 Oct 2026 11:42:07 -0500 Subject: [PATCH 2/8] fix: enable ECDSA and EdDSA support in library definitions and tests. --- src/data/libraries-next.json | 8 ++++---- tests/library-support.values.test.ts | 13 +++++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/data/libraries-next.json b/src/data/libraries-next.json index cdbb4cb6..561efdc7 100644 --- a/src/data/libraries-next.json +++ b/src/data/libraries-next.json @@ -2525,13 +2525,13 @@ "rs256": true, "rs384": true, "rs512": true, - "es256": false, - "es384": false, - "es512": false, + "es256": true, + "es384": true, + "es512": true, "ps256": false, "ps384": false, "ps512": false, - "eddsa": false, + "eddsa": true, "es256k": false }, "authorUrl": "https://github.com/orgs/awslabs", diff --git a/tests/library-support.values.test.ts b/tests/library-support.values.test.ts index 97e17c85..f95bb181 100644 --- a/tests/library-support.values.test.ts +++ b/tests/library-support.values.test.ts @@ -23,4 +23,17 @@ describe("library support definitions", () => { expect(new Set(definedValues).size).toBe(definedValues.length); expect(Array.from(dataValues).sort()).toEqual([...definedValues].sort()); }); + + test("lists the ECDSA and EdDSA support added in aws-jwt-verify v5", () => { + const awsJwtVerify = dictionary["node-js"]?.libs.find( + ({ gitHubRepoPath }) => gitHubRepoPath === "awslabs/aws-jwt-verify", + ); + + expect(awsJwtVerify?.support).toMatchObject({ + es256: true, + es384: true, + es512: true, + eddsa: true, + }); + }); }); From 8c044a89ba26ed9d7c8d0705d8b915c92cd02783 Mon Sep 17 00:00:00 2001 From: Jonathan Puglla Date: Mon, 26 Oct 2026 12:19:01 -0500 Subject: [PATCH 3/8] feat: add support for converting SEC1 EC private keys to PKCS#8 format and update related tests. --- src/features/common/services/jwt.service.ts | 14 ++ .../services/sec1-ec-private-key.service.ts | 134 ++++++++++++++++++ .../encoder/services/token-encoder.service.ts | 6 +- tests/sec1-ec-private-key.test.ts | 118 +++++++++++++++ tests/token-encoder.service.test.ts | 22 +++ 5 files changed, 289 insertions(+), 5 deletions(-) create mode 100644 src/features/common/services/sec1-ec-private-key.service.ts create mode 100644 tests/sec1-ec-private-key.test.ts diff --git a/src/features/common/services/jwt.service.ts b/src/features/common/services/jwt.service.ts index 39c6410a..a6d97c79 100644 --- a/src/features/common/services/jwt.service.ts +++ b/src/features/common/services/jwt.service.ts @@ -23,6 +23,10 @@ import { type JWK, } from "jose"; import nodeForge from "node-forge"; +import { + convertSec1EcPrivateKeyToPkcs8, + isSec1EcPrivateKey, +} from "@/features/common/services/sec1-ec-private-key.service"; import { EncodingValues } from "@/features/common/values/encoding.values"; import { getOperationException, @@ -994,6 +998,16 @@ export const getAsymmetricKeyCryptoKey = async ( key = safePrivateKeyInfoToPemResult.value; } + if (isSec1EcPrivateKey(key)) { + const convertSec1EcPrivateKeyResult = convertSec1EcPrivateKeyToPkcs8(key); + + if (convertSec1EcPrivateKeyResult.isErr()) { + return err(convertSec1EcPrivateKeyResult.error); + } + + key = convertSec1EcPrivateKeyResult.value; + } + if (key.startsWith("-----BEGIN")) { const safeImportPKCS8Result = await safeImportPKCS8( key, diff --git a/src/features/common/services/sec1-ec-private-key.service.ts b/src/features/common/services/sec1-ec-private-key.service.ts new file mode 100644 index 00000000..20abe71b --- /dev/null +++ b/src/features/common/services/sec1-ec-private-key.service.ts @@ -0,0 +1,134 @@ +import nodeForge from "node-forge"; +import { err, fromThrowable, ok, Result } from "neverthrow"; + +const { asn1, pem } = nodeForge; + +type Asn1Node = nodeForge.asn1.Asn1; + +const SEC1_PEM_TYPE = "EC PRIVATE KEY"; +const PKCS8_PEM_TYPE = "PRIVATE KEY"; +const SEC1_PEM_HEADER = `-----BEGIN ${SEC1_PEM_TYPE}-----`; +const EC_PUBLIC_KEY_OID = "1.2.840.10045.2.1"; +const PARAMETERS_TAG = 0; +const PUBLIC_KEY_TAG = 1; +const PKCS8_VERSION = 0; + +const CONVERSION_ERROR = "Cannot convert the SEC1 EC private key to PKCS#8."; + +const safeDecodePem = fromThrowable(pem.decode, () => CONVERSION_ERROR); +const safeFromDer = fromThrowable( + (bytes: string) => asn1.fromDer(bytes), + () => CONVERSION_ERROR, +); + +const isContextTag = + (tag: number) => + (node: Asn1Node): boolean => + node.tagClass === asn1.Class.CONTEXT_SPECIFIC && node.type === tag; + +const createNode = ( + type: number, + constructed: boolean, + value: Asn1Node[] | string, +): Asn1Node => asn1.create(asn1.Class.UNIVERSAL, type, constructed, value); + +const createSequence = (children: Asn1Node[]): Asn1Node => + createNode(asn1.Type.SEQUENCE, true, children); + +const toDerBytes = (node: Asn1Node): string => asn1.toDer(node).getBytes(); + +type EcPrivateKeyParts = { + version: Asn1Node; + privateKey: Asn1Node; + curveParameters: Asn1Node; + publicKey: Asn1Node | undefined; +}; + +export const isSec1EcPrivateKey = (key: string): boolean => + key.includes(SEC1_PEM_HEADER); + +const findSec1Block = (key: string): Result => + safeDecodePem(key).andThen((blocks) => { + const block = blocks.find(({ type }) => type === SEC1_PEM_TYPE); + + return block ? ok(block) : err(CONVERSION_ERROR); + }); + +const rejectEncrypted = ( + block: nodeForge.pem.ObjectPEM, +): Result => + block.procType?.type === "ENCRYPTED" + ? err( + "Encrypted EC private keys are not supported. Decrypt the key first, for example with `openssl ec -in key.pem -out decrypted.pem`.", + ) + : ok(block); + +const extractParts = (root: Asn1Node): Result => { + const children = Array.isArray(root.value) ? (root.value as Asn1Node[]) : []; + const [version, privateKey, ...optional] = children; + const parameters = optional.find(isContextTag(PARAMETERS_TAG)); + const curveParameters = Array.isArray(parameters?.value) + ? (parameters.value[0] as Asn1Node | undefined) + : undefined; + + if (!version || !privateKey || !curveParameters) { + return err( + "The SEC1 EC private key is malformed or does not specify its curve.", + ); + } + + return ok({ + version, + privateKey, + curveParameters, + publicKey: optional.find(isContextTag(PUBLIC_KEY_TAG)), + }); +}; + +const toPkcs8Der = ({ + version, + privateKey, + curveParameters, + publicKey, +}: EcPrivateKeyParts): string => { + const ecPrivateKey = createSequence( + publicKey ? [version, privateKey, publicKey] : [version, privateKey], + ); + + const algorithmIdentifier = createSequence([ + createNode( + asn1.Type.OID, + false, + asn1.oidToDer(EC_PUBLIC_KEY_OID).getBytes(), + ), + curveParameters, + ]); + + return toDerBytes( + createSequence([ + createNode( + asn1.Type.INTEGER, + false, + String.fromCharCode(PKCS8_VERSION), + ), + algorithmIdentifier, + createNode(asn1.Type.OCTETSTRING, false, toDerBytes(ecPrivateKey)), + ]), + ); +}; + +const toPem = (der: string): string => + pem + .encode({ type: PKCS8_PEM_TYPE, body: der }) + .replace(/\r\n/g, "\n") + .trim(); + +export const convertSec1EcPrivateKeyToPkcs8 = ( + key: string, +): Result => + findSec1Block(key) + .andThen(rejectEncrypted) + .andThen(({ body }) => safeFromDer(body)) + .andThen(extractParts) + .map(toPkcs8Der) + .map(toPem); diff --git a/src/features/encoder/services/token-encoder.service.ts b/src/features/encoder/services/token-encoder.service.ts index 08f35617..b87e258e 100644 --- a/src/features/encoder/services/token-encoder.service.ts +++ b/src/features/encoder/services/token-encoder.service.ts @@ -568,11 +568,7 @@ class _TokenEncoderService { ); if (jwt.isErr()) { - return err({ - task: DebuggerTaskValues.ENCODE, - input: DebuggerInputValues.KEY, - message: "Private key must not be empty.", - }) + return err(jwt.error); } return ok({ diff --git a/tests/sec1-ec-private-key.test.ts b/tests/sec1-ec-private-key.test.ts new file mode 100644 index 00000000..b366c854 --- /dev/null +++ b/tests/sec1-ec-private-key.test.ts @@ -0,0 +1,118 @@ +import { describe, expect, test } from "vitest"; +import { + getAsymmetricKeyCryptoKey, + signWithAsymmetricPrivateKey, + verifyDigitallySignedJwt, +} from "@/features/common/services/jwt.service"; +import { convertSec1EcPrivateKeyToPkcs8 } from "@/features/common/services/sec1-ec-private-key.service"; +import { AsymmetricKeyFormatValues } from "@/features/common/values/asymmetric-key-format.values"; + +const pem = (lines: string[]) => lines.join("\n"); + +// Generated with `openssl ecparam -genkey -noout`, `openssl pkcs8 -topk8 -nocrypt` +// and `openssl ec -pubout`. +const ecKeys = { + ES256: { + sec1: pem(["-----BEGIN EC PRIVATE KEY-----", "MHcCAQEEIGVO/eFcax0cgQ1UE5dMrdKicPsEJB+JEYgsVpzsvXXKoAoGCCqGSM49", "AwEHoUQDQgAE+wqjW5QaNLLkRTnFibBrLmPwmv6Lxl1E6H81YaHw/7qIvtbC71+U", "PRHgx2Lirf6fLp+8iHgyPEbzVCxoVDtdvg==", "-----END EC PRIVATE KEY-----"]), + pkcs8: pem(["-----BEGIN PRIVATE KEY-----", "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgZU794VxrHRyBDVQT", "l0yt0qJw+wQkH4kRiCxWnOy9dcqhRANCAAT7CqNblBo0suRFOcWJsGsuY/Ca/ovG", "XUTofzVhofD/uoi+1sLvX5Q9EeDHYuKt/p8un7yIeDI8RvNULGhUO12+", "-----END PRIVATE KEY-----"]), + pub: pem(["-----BEGIN PUBLIC KEY-----", "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+wqjW5QaNLLkRTnFibBrLmPwmv6L", "xl1E6H81YaHw/7qIvtbC71+UPRHgx2Lirf6fLp+8iHgyPEbzVCxoVDtdvg==", "-----END PUBLIC KEY-----"]), + }, + ES384: { + sec1: pem(["-----BEGIN EC PRIVATE KEY-----", "MIGkAgEBBDDOiWzUZhqT3lxKtxnZYyqozvUDfUn1ekYBZYleI7FaEQ4UCNrUVxoG", "wTyUmyIXtvigBwYFK4EEACKhZANiAAQDZ17H1w8LJlNlyIdtJ7dqbcY/z319fARy", "g/90cNqeT2XohpS6GyP8JaXbZlnKLHz88qDBx4CaA3bgrPSjwBAL+uF3CwKjhSs7", "JSb8AGNH2oyUNN80k1FT9T7TAPiI2Rg=", "-----END EC PRIVATE KEY-----"]), + pkcs8: pem(["-----BEGIN PRIVATE KEY-----", "MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDOiWzUZhqT3lxKtxnZ", "YyqozvUDfUn1ekYBZYleI7FaEQ4UCNrUVxoGwTyUmyIXtvihZANiAAQDZ17H1w8L", "JlNlyIdtJ7dqbcY/z319fARyg/90cNqeT2XohpS6GyP8JaXbZlnKLHz88qDBx4Ca", "A3bgrPSjwBAL+uF3CwKjhSs7JSb8AGNH2oyUNN80k1FT9T7TAPiI2Rg=", "-----END PRIVATE KEY-----"]), + pub: pem(["-----BEGIN PUBLIC KEY-----", "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEA2dex9cPCyZTZciHbSe3am3GP899fXwE", "coP/dHDank9l6IaUuhsj/CWl22ZZyix8/PKgwceAmgN24Kz0o8AQC/rhdwsCo4Ur", "OyUm/ABjR9qMlDTfNJNRU/U+0wD4iNkY", "-----END PUBLIC KEY-----"]), + }, + ES512: { + sec1: pem(["-----BEGIN EC PRIVATE KEY-----", "MIHcAgEBBEIBhkQwKFQvB3btYL/BW1Kz387SJXwLQSGvw3q6Afa2EZC0XT2l77Lx", "CCfRz7ilAQbxPc6MyVGKnKTTePCHh6kQv/6gBwYFK4EEACOhgYkDgYYABAFUt5Bx", "ehHtLp9oOXNw8Ds6WWssHSxfTxKiWtGtcpd5JJmkG+177y9JrygsMX6Ojm/ZfrDj", "qFkGONzQ3wRe9YjjZQHc+FN1DhscQ6h4UPBUNndJRKy/wTb/SP0jE8Mc6YWoh5vq", "Lmh6PdrzMGB0UT8p6MmEFQDYhWoIHp6iRlQ/9201fA==", "-----END EC PRIVATE KEY-----"]), + pkcs8: pem(["-----BEGIN PRIVATE KEY-----", "MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBhkQwKFQvB3btYL/B", "W1Kz387SJXwLQSGvw3q6Afa2EZC0XT2l77LxCCfRz7ilAQbxPc6MyVGKnKTTePCH", "h6kQv/6hgYkDgYYABAFUt5BxehHtLp9oOXNw8Ds6WWssHSxfTxKiWtGtcpd5JJmk", "G+177y9JrygsMX6Ojm/ZfrDjqFkGONzQ3wRe9YjjZQHc+FN1DhscQ6h4UPBUNndJ", "RKy/wTb/SP0jE8Mc6YWoh5vqLmh6PdrzMGB0UT8p6MmEFQDYhWoIHp6iRlQ/9201", "fA==", "-----END PRIVATE KEY-----"]), + pub: pem(["-----BEGIN PUBLIC KEY-----", "MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBVLeQcXoR7S6faDlzcPA7OllrLB0s", "X08SolrRrXKXeSSZpBvte+8vSa8oLDF+jo5v2X6w46hZBjjc0N8EXvWI42UB3PhT", "dQ4bHEOoeFDwVDZ3SUSsv8E2/0j9IxPDHOmFqIeb6i5oej3a8zBgdFE/KejJhBUA", "2IVqCB6eokZUP/dtNXw=", "-----END PUBLIC KEY-----"]), + }, +} as const; + +const sec1WithCurveParameters = pem(["-----BEGIN EC PARAMETERS-----", "BggqhkjOPQMBBw==", "-----END EC PARAMETERS-----", "-----BEGIN EC PRIVATE KEY-----", "MHcCAQEEIEFMkZys1GVqIfKKVh7xD6s7SJMSbVJrnHdYV2mC9w3doAoGCCqGSM49", "AwEHoUQDQgAERBWHf5bkbRrQ4iixJqQjIGzMCx77cGbrSjLPUTOB60qZBlOjk6Uk", "ajaFTz1cHW9qQn6tD2MDbuneX2uVM94O1A==", "-----END EC PRIVATE KEY-----"]); +const encryptedSec1 = pem(["-----BEGIN EC PRIVATE KEY-----", "Proc-Type: 4,ENCRYPTED", "DEK-Info: AES-256-CBC,43350ED0D6F2A3D8198CEA5EB7DEFE64", "", "NaXCpZowzEhUd3JZjW90P/+h/c5jX6667o36NJ+ShaPlGooNyrMco90mGnbM2aEE", "9fRCHAakHfVxQyvd9dgTyjJPqe7Rr2fiIISerTI2sgdYBgrO/w97r9ovcA+8gyTj", "8CrIp1KL3rk1rXJ8gL/9zzhERzndqRDu6iW4pWFhgP4=", "-----END EC PRIVATE KEY-----"]); + +type EcAlg = keyof typeof ecKeys; + +const ecAlgs = Object.keys(ecKeys) as EcAlg[]; + +describe("convertSec1EcPrivateKeyToPkcs8", () => { + test.each(ecAlgs)("matches the OpenSSL PKCS#8 output for %s", (alg) => { + const result = convertSec1EcPrivateKeyToPkcs8(ecKeys[alg].sec1); + + expect(result._unsafeUnwrap()).toBe(ecKeys[alg].pkcs8); + }); + + test("ignores the EC PARAMETERS block that `openssl ecparam -genkey` emits", () => { + const result = convertSec1EcPrivateKeyToPkcs8(sec1WithCurveParameters); + + expect(result.isOk()).toBe(true); + expect(result._unsafeUnwrap()).toMatch(/^-----BEGIN PRIVATE KEY-----/); + }); + + test("rejects encrypted keys with an actionable message", () => { + const result = convertSec1EcPrivateKeyToPkcs8(encryptedSec1); + + expect(result._unsafeUnwrapErr()).toContain("Encrypted EC private keys"); + }); + + test("rejects malformed keys", () => { + const malformed = [ + "-----BEGIN EC PRIVATE KEY-----", + "AAAA", + "-----END EC PRIVATE KEY-----", + ].join("\n"); + + expect(convertSec1EcPrivateKeyToPkcs8(malformed).isErr()).toBe(true); + }); +}); + +describe("SEC1 EC private keys", () => { + test.each(ecAlgs)("are imported for %s", async (alg) => { + const result = await getAsymmetricKeyCryptoKey( + ecKeys[alg].sec1, + alg, + AsymmetricKeyFormatValues.PEM, + ); + + expect(result.isOk()).toBe(true); + }); + + test.each(ecAlgs)("sign a JWT that %s public key verifies", async (alg) => { + const signed = await signWithAsymmetricPrivateKey( + { alg, typ: "JWT" }, + { sub: "1234567890" }, + ecKeys[alg].sec1, + AsymmetricKeyFormatValues.PEM, + ); + + const verified = await verifyDigitallySignedJwt({ + jwt: signed._unsafeUnwrap(), + alg, + asymmetricPublicKey: ecKeys[alg].pub, + asymmetricPublicKeyFormat: AsymmetricKeyFormatValues.PEM, + }); + + expect(verified.isOk()).toBe(true); + }); + + test("reject a key whose curve does not match the algorithm", async () => { + const result = await getAsymmetricKeyCryptoKey( + ecKeys.ES256.sec1, + "ES384", + AsymmetricKeyFormatValues.PEM, + ); + + expect(result.isErr()).toBe(true); + }); + + test("keep PKCS#8 keys working unchanged", async () => { + const result = await getAsymmetricKeyCryptoKey( + ecKeys.ES256.pkcs8, + "ES256", + AsymmetricKeyFormatValues.PEM, + ); + + expect(result.isOk()).toBe(true); + }); +}); diff --git a/tests/token-encoder.service.test.ts b/tests/token-encoder.service.test.ts index b76717c0..7a266eae 100644 --- a/tests/token-encoder.service.test.ts +++ b/tests/token-encoder.service.test.ts @@ -6,6 +6,7 @@ import { DefaultTokenWithSecretModel, } from "@/features/common/values/default-tokens.values"; import { EncoderResult } from "@/features/common/models/encoder-result.model"; +import { AsymmetricKeyFormatValues } from "@/features/common/values/asymmetric-key-format.values"; describe("processSymmetricSecretKey", () => { describe("should encode a JWT for SYMMETRIC type with HMAC algorithm", () => { @@ -57,3 +58,24 @@ describe("processSymmetricSecretKey", () => { }); }); }); + +describe("processAsymmetricPrivateKey", () => { + const baseParams = { + header: JSON.stringify({ alg: "ES256", typ: "JWT" }), + payload: JSON.stringify({ sub: "1234567890" }), + asymmetricPrivateKeyFormat: AsymmetricKeyFormatValues.PEM, + }; + + test("reports the real reason a private key cannot be used", async () => { + const result = await TokenEncoderService.processAsymmetricPrivateKey({ + ...baseParams, + asymmetricPrivateKey: + "-----BEGIN EC PRIVATE KEY-----\nAAAA\n-----END EC PRIVATE KEY-----", + }); + + expect(result.isErr()).toBe(true); + expect(JSON.stringify(result._unsafeUnwrapErr())).toContain( + "Cannot convert the SEC1 EC private key to PKCS#8.", + ); + }); +}); From bf622e734d9fe405bafdac1060d30bf7d563ffe8 Mon Sep 17 00:00:00 2001 From: Jonathan Puglla Date: Mon, 26 Oct 2026 12:37:46 -0500 Subject: [PATCH 4/8] feat: add archived status to libraries with corresponding UI and tests. --- src/data/libraries-next.json | 9 +++ .../library-card/library-card..module.scss | 11 +++ .../library-card/library-card.component.tsx | 6 ++ .../libraries/models/library.model.ts | 1 + .../localization/dictionaries/libraries/en.ts | 3 + .../localization/dictionaries/libraries/ja.ts | 4 + .../models/libraries-dictionary.model.ts | 3 + src/scripts/generate-library-data.ts | 78 +++++++++++++------ tests/library-support.values.test.ts | 26 +++++++ 9 files changed, 116 insertions(+), 25 deletions(-) diff --git a/src/data/libraries-next.json b/src/data/libraries-next.json index 561efdc7..09ebab61 100644 --- a/src/data/libraries-next.json +++ b/src/data/libraries-next.json @@ -512,6 +512,7 @@ "authorUrl": "https://babelouest.github.io/", "authorName": "babelouest", "gitHubRepoPath": "babelouest/rhonabwy", + "archived": true, "repoUrl": "https://github.com/babelouest/rhonabwy", "installCommandMarkdown": [ "[Download release package](https://github.com/babelouest/rhonabwy/releases/latest) or [install from source](https://github.com/babelouest/rhonabwy)" @@ -894,6 +895,7 @@ "authorUrl": "https://github.com/olehlong", "authorName": "olehlong", "gitHubRepoPath": "olehlong/jwtd", + "archived": true, "repoUrl": "https://github.com/olehlong/jwtd", "installCommandMarkdown": ["dub fetch jwtd"] }, @@ -925,6 +927,7 @@ "authorUrl": "https://github.com/zolamk", "authorName": "zolamk", "gitHubRepoPath": "zolamk/jwt", + "archived": true, "repoUrl": "https://github.com/zolamk/jwt", "installCommandMarkdown": ["dub fetch jwt"] } @@ -1380,6 +1383,7 @@ "authorUrl": "https://github.com/robbert229", "authorName": "robbert229", "gitHubRepoPath": "robbert229/jwt", + "archived": true, "repoUrl": "https://github.com/robbert229/jwt", "installCommandMarkdown": [ "go get [github.com/robbert229/jwt](https://godoc.org/github.com/robbert229/jwt)" @@ -1413,6 +1417,7 @@ "authorUrl": "https://github.com/square", "authorName": "DV", "gitHubRepoPath": "square/go-jose", + "archived": true, "repoUrl": "https://github.com/square/go-jose", "installCommandMarkdown": [ "go get [github.com/square/go-jose](https://godoc.org/github.com/square/go-jose)" @@ -1544,6 +1549,7 @@ "authorUrl": "https://github.com/gbrlsnchs", "authorName": "Gabriel Sanches", "gitHubRepoPath": "gbrlsnchs/jwt", + "archived": true, "repoUrl": "https://github.com/gbrlsnchs/jwt", "installCommandMarkdown": [ "GO111MODULE=on go get [github.com/gbrlsnchs/jwt/v3](https://godoc.org/github.com/gbrlsnchs/jwt)" @@ -2764,6 +2770,7 @@ "authorUrl": "https://github.com/emarref", "authorName": "Emarref", "gitHubRepoPath": "emarref/jwt", + "archived": true, "repoUrl": "https://github.com/emarref/jwt", "installCommandMarkdown": ["composer require emarref/jwt"] }, @@ -2795,6 +2802,7 @@ "authorUrl": "https://github.com/nov", "authorName": "Nov Matake", "gitHubRepoPath": "nov/jose-php", + "archived": true, "repoUrl": "https://github.com/nov/jose-php", "installCommandMarkdown": ["composer require gree/jose"] }, @@ -3643,6 +3651,7 @@ "authorUrl": "https://github.com/iain-logan", "authorName": "iain-logan", "gitHubRepoPath": "iain-logan/jwt", + "archived": true, "repoUrl": "https://github.com/iain-logan/jwt", "installCommandMarkdown": [ "libraryDependencies += \"io.igl\" %% \"jwt\" % \"1.2.2\"" diff --git a/src/features/libraries/components/library-card/library-card..module.scss b/src/features/libraries/components/library-card/library-card..module.scss index 5eb5ca76..2a300b5c 100644 --- a/src/features/libraries/components/library-card/library-card..module.scss +++ b/src/features/libraries/components/library-card/library-card..module.scss @@ -40,6 +40,17 @@ padding: 1.5rem 1.5rem 2.5rem; } +.archived { + margin: .5rem .5rem 0; + padding: .5rem; + border-radius: .5rem; + color: var(--color_fg_on_state_caution_subtle); + background: var(--color_bg_layer_alternate); + font-size: .875rem; + font-weight: 500; + line-height: 1.4; +} + .minVersion { grid-column: 1/-1; color: var(--color_fg_on_state_caution_subtle); diff --git a/src/features/libraries/components/library-card/library-card.component.tsx b/src/features/libraries/components/library-card/library-card.component.tsx index bdff1ad0..7969b55e 100644 --- a/src/features/libraries/components/library-card/library-card.component.tsx +++ b/src/features/libraries/components/library-card/library-card.component.tsx @@ -104,6 +104,7 @@ export const LibraryCardComponent: React.FC = ({ stars, repoUrl, minimumVersion, + archived, } = library; const command = installCommandMarkdown.join("
"); @@ -161,6 +162,11 @@ export const LibraryCardComponent: React.FC = ({ + {archived && ( +
+ {dictionary.archived.label} +
+ )}
{command}
diff --git a/src/features/libraries/models/library.model.ts b/src/features/libraries/models/library.model.ts index 49733069..da8a3e8d 100644 --- a/src/features/libraries/models/library.model.ts +++ b/src/features/libraries/models/library.model.ts @@ -10,4 +10,5 @@ export interface LibraryModel { repoUrl: string; installCommandMarkdown: string[]; stars?: number | null; + archived?: boolean; } diff --git a/src/features/localization/dictionaries/libraries/en.ts b/src/features/localization/dictionaries/libraries/en.ts index 3535bada..9f3426c3 100644 --- a/src/features/localization/dictionaries/libraries/en.ts +++ b/src/features/localization/dictionaries/libraries/en.ts @@ -57,6 +57,9 @@ export const enLibrariesDictionary: LibrariesDictionaryModel = { viewRepo: { label: "View repo", }, + archived: { + label: "Archived repository: read-only and no longer maintained.", + }, minimumVersion: { label: "Minimum version", resource: { diff --git a/src/features/localization/dictionaries/libraries/ja.ts b/src/features/localization/dictionaries/libraries/ja.ts index 62916fa4..4d17f00b 100644 --- a/src/features/localization/dictionaries/libraries/ja.ts +++ b/src/features/localization/dictionaries/libraries/ja.ts @@ -57,6 +57,10 @@ export const jaLibrariesDictionary: LibrariesDictionaryModel = { viewRepo: { label: "リポジトリを表示", }, + archived: { + label: + "アーカイブ済みのリポジトリです。読み取り専用で、メンテナンスされていません。", + }, minimumVersion: { label: "最小バージョン", resource: { diff --git a/src/features/localization/models/libraries-dictionary.model.ts b/src/features/localization/models/libraries-dictionary.model.ts index e4ce99cd..d262173c 100644 --- a/src/features/localization/models/libraries-dictionary.model.ts +++ b/src/features/localization/models/libraries-dictionary.model.ts @@ -14,6 +14,9 @@ export interface LibrariesDictionaryModel { viewRepo: { label: string; }; + archived: { + label: string; + }; minimumVersion: { label: string; resource: { diff --git a/src/scripts/generate-library-data.ts b/src/scripts/generate-library-data.ts index c3747b6a..426bba23 100644 --- a/src/scripts/generate-library-data.ts +++ b/src/scripts/generate-library-data.ts @@ -24,6 +24,11 @@ interface GitHubRepository { fullName: string; } +interface RepositoryMetadata { + stars: number; + archived: boolean; +} + interface RepositoryLibraries extends GitHubRepository { libraries: LibraryModel[]; } @@ -150,33 +155,37 @@ async function authenticateGitHubToken(token: string): Promise { } } -async function getStarCount( +async function getRepositoryMetadata( token: string, repository: GitHubRepository, -): Promise { +): Promise { try { const owner = encodeURIComponent(repository.owner); const repo = encodeURIComponent(repository.repo); const response = await requestGitHubApi(token, `/repos/${owner}/${repo}`); const data: unknown = await response.json(); - if (!isRecord(data) || typeof data.stargazers_count !== "number") { - throw new Error("GitHub API response has no star count"); + if ( + !isRecord(data) || + typeof data.stargazers_count !== "number" || + typeof data.archived !== "boolean" + ) { + throw new Error("GitHub API response has no star count or archived flag"); } - return data.stargazers_count; + return { stars: data.stargazers_count, archived: data.archived }; } catch (cause) { - throw new Error(`Unable to get the star count for ${repository.fullName}`, { + throw new Error(`Unable to get the metadata for ${repository.fullName}`, { cause, }); } } -async function getStarCounts( +async function getRepositoriesMetadata( token: string, repositories: RepositoryLibraries[], -): Promise> { - const starCounts = new Map(); +): Promise> { + const metadataByRepository = new Map(); for ( let index = 0; @@ -186,44 +195,57 @@ async function getStarCounts( const batch = repositories.slice(index, index + REQUEST_CONCURRENCY); const results = await Promise.allSettled( batch.map(async (repository) => { - const stars = await getStarCount(token, repository); + const metadata = await getRepositoryMetadata(token, repository); - return [repository.fullName, stars] as const; + return [repository.fullName, metadata] as const; }), ); for (const result of results) { if (result.status === "fulfilled") { - const [repository, stars] = result.value; - starCounts.set(repository, stars); + const [repository, metadata] = result.value; + metadataByRepository.set(repository, metadata); } else { console.warn( result.reason instanceof Error ? result.reason.message - : "Unable to get a repository star count", + : "Unable to get repository metadata", ); } } } - return starCounts; + return metadataByRepository; +} + +function applyRepositoryMetadata( + library: LibraryModel, + { stars, archived }: RepositoryMetadata, +): void { + library.stars = stars; + + if (archived) { + library.archived = true; + } else { + delete library.archived; + } } -function updateStarCounts( +function updateRepositoryMetadata( repositories: RepositoryLibraries[], - starCounts: Map, + metadataByRepository: Map, ): number { let updatedLibraries = 0; for (const repository of repositories) { - const stars = starCounts.get(repository.fullName); + const metadata = metadataByRepository.get(repository.fullName); - if (stars === undefined) { + if (metadata === undefined) { continue; } for (const library of repository.libraries) { - library.stars = stars; + applyRepositoryMetadata(library, metadata); updatedLibraries += 1; } } @@ -252,19 +274,25 @@ async function main(): Promise { await authenticateGitHubToken(token); - const starCounts = await getStarCounts(token, repositories); - const updatedLibraries = updateStarCounts(repositories, starCounts); - const failedRepositories = repositories.length - starCounts.size; + const metadataByRepository = await getRepositoriesMetadata( + token, + repositories, + ); + const updatedLibraries = updateRepositoryMetadata( + repositories, + metadataByRepository, + ); + const failedRepositories = repositories.length - metadataByRepository.size; await writeLibraryDictionary(dictionary); console.log( - `Updated ${updatedLibraries} library entries from ${starCounts.size} GitHub repositories.`, + `Updated ${updatedLibraries} library entries from ${metadataByRepository.size} GitHub repositories.`, ); if (failedRepositories > 0) { console.warn( - `Skipped ${failedRepositories} GitHub repositories whose star counts could not be fetched.`, + `Skipped ${failedRepositories} GitHub repositories whose metadata could not be fetched.`, ); } } diff --git a/tests/library-support.values.test.ts b/tests/library-support.values.test.ts index f95bb181..712e922c 100644 --- a/tests/library-support.values.test.ts +++ b/tests/library-support.values.test.ts @@ -37,3 +37,29 @@ describe("library support definitions", () => { }); }); }); + +describe("library repository status", () => { + const libs = Object.values(dictionary).flatMap(({ libs }) => libs); + + test("only marks a library as archived with an explicit true", () => { + const flags = libs + .map(({ archived }) => archived) + .filter((archived) => archived !== undefined); + + expect(flags.length).toBeGreaterThan(0); + expect(flags.every((archived) => archived === true)).toBe(true); + }); + + test.each([ + "zolamk/jwt", + "olehlong/jwtd", + "square/go-jose", + "nov/jose-php", + "robbert229/jwt", + "babelouest/rhonabwy", + ])("flags %s as archived", (gitHubRepoPath) => { + const library = libs.find((lib) => lib.gitHubRepoPath === gitHubRepoPath); + + expect(library?.archived).toBe(true); + }); +}); From efdc8ffd930e36f7b5c60d2d28866a4a4a723b1c Mon Sep 17 00:00:00 2001 From: Jonathan Puglla Date: Mon, 26 Oct 2026 14:28:54 -0500 Subject: [PATCH 5/8] feat: add JWKS support for asymmetric key handling. --- e2e/decoder-jwks.spec.ts | 178 ++++++++++ .../services/asymmetric-key-input.service.ts | 6 +- src/features/common/services/jwks.service.ts | 239 +++++++++++++ src/features/common/services/jwt.service.ts | 48 +++ .../values/asymmetric-key-format.values.ts | 1 + ...erification-public-key-input.component.tsx | 3 +- ...en-decoder-key-format-picker.component.tsx | 4 + .../services/decoder-fragment.service.ts | 4 +- .../decoder/services/decoder.store.ts | 18 +- .../services/public-key-input.service.ts | 11 + .../decoder/services/token-decoder.service.ts | 58 ++- .../localization/dictionaries/home/en.ts | 2 +- .../localization/dictionaries/home/ja.ts | 2 +- tests/decoder-fragment.service.test.ts | 16 + tests/decoder-jwks.store.test.ts | 137 ++++++++ tests/jwks.service.test.ts | 332 ++++++++++++++++++ 16 files changed, 1046 insertions(+), 13 deletions(-) create mode 100644 e2e/decoder-jwks.spec.ts create mode 100644 src/features/common/services/jwks.service.ts create mode 100644 tests/decoder-jwks.store.test.ts create mode 100644 tests/jwks.service.test.ts diff --git a/e2e/decoder-jwks.spec.ts b/e2e/decoder-jwks.spec.ts new file mode 100644 index 00000000..1f7f0046 --- /dev/null +++ b/e2e/decoder-jwks.spec.ts @@ -0,0 +1,178 @@ +import { expect, test, type Page } from "@playwright/test"; +import { + exportJWK, + generateKeyPair, + SignJWT, + type JWK, + type JSONWebKeySet, +} from "jose"; +import { dataTestidDictionary } from "@/libs/testing/data-testid.dictionary"; +import { E2E_BASE_URL } from "./e2e.utils"; + +type SigningKey = { + kid: string; + jwk: JWK; + sign: (kid?: string) => Promise; +}; + +const createSigningKey = async (kid: string): Promise => { + const { publicKey, privateKey } = await generateKeyPair("RS256", { + extractable: true, + }); + const jwk = { + ...(await exportJWK(publicKey)), + kid, + alg: "RS256", + use: "sig", + }; + + return { + kid, + jwk, + sign: (headerKid) => + new SignJWT({ sub: "jwks-e2e" }) + .setProtectedHeader({ alg: "RS256", typ: "JWT", kid: headerKid }) + .sign(privateKey), + }; +}; + +const toJwks = (...keys: SigningKey[]): JSONWebKeySet => ({ + keys: keys.map(({ jwk }) => jwk), +}); + +const getDecoder = (page: Page) => + page.getByTestId(dataTestidDictionary.decoder.id); + +const jwtInput = (page: Page) => + page + .getByTestId(dataTestidDictionary.decoder.jwtEditor.id) + .getByRole("textbox"); + +const keyEditor = (page: Page) => + page.getByTestId(dataTestidDictionary.decoder.secretKeyEditor.id); + +const selectPublicKeyFormat = async (page: Page, format: string) => { + await getDecoder(page) + .getByRole("combobox", { name: "Debugger picker" }) + .first() + .locator('xpath=ancestor::div[contains(@class, "control")]') + .dispatchEvent("mousedown", { button: 0 }); + await page + .getByRole("option", { name: format, exact: true }) + .dispatchEvent("click"); +}; + +test.describe("decoder JWKS public key input", () => { + let first: SigningKey; + let second: SigningKey; + + test.beforeAll(async () => { + [first, second] = await Promise.all([ + createSigningKey("e2e-key-1"), + createSigningKey("e2e-key-2"), + ]); + }); + + test.beforeEach(async ({ page }) => { + await page.goto(`${E2E_BASE_URL}/#token=${await second.sign(second.kid)}`); + await expect(getDecoder(page)).toContainText("Fix public key input errors"); + }); + + test("verifies a token against the matching key of a pasted JWKS", async ({ + page, + }) => { + await selectPublicKeyFormat(page, "JWKS"); + await keyEditor(page) + .getByRole("textbox") + .fill(JSON.stringify(toJwks(first, second))); + + await expect( + page.getByTestId( + dataTestidDictionary.decoder.jwtEditor.statusBar.success.id, + ), + ).toBeVisible(); + await expect(getDecoder(page)).toContainText("Signature Verified"); + }); + + test("detects a pasted JWKS and switches the format automatically", async ({ + page, + }) => { + await keyEditor(page) + .getByRole("textbox") + .fill(JSON.stringify(toJwks(second))); + + await expect( + page.locator(".react-select__single-value").filter({ hasText: "JWKS" }), + ).toBeVisible(); + await expect(getDecoder(page)).toContainText("Signature Verified"); + }); + + test("re-verifies when another token signed by the set is pasted", async ({ + page, + }) => { + await selectPublicKeyFormat(page, "JWKS"); + await keyEditor(page) + .getByRole("textbox") + .fill(JSON.stringify(toJwks(first, second))); + await expect(getDecoder(page)).toContainText("Signature Verified"); + + await jwtInput(page).fill(await first.sign(first.kid)); + + await expect(getDecoder(page)).toContainText("Signature Verified"); + }); + + test("rejects a token whose kid is not in the JWKS", async ({ page }) => { + await selectPublicKeyFormat(page, "JWKS"); + await keyEditor(page) + .getByRole("textbox") + .fill(JSON.stringify(toJwks(first))); + + await expect(getDecoder(page)).toContainText("Invalid Signature"); + await expect(keyEditor(page)).toContainText(`kid "${second.kid}"`); + }); + + test("explains malformed JWKS input", async ({ page }) => { + await selectPublicKeyFormat(page, "JWKS"); + await keyEditor(page).getByRole("textbox").fill('{"keys": "nope"}'); + + await expect(keyEditor(page)).toContainText("not a valid JWKS"); + }); + + test("refuses an insecure remote JWKS URI", async ({ page }) => { + await selectPublicKeyFormat(page, "JWKS"); + await keyEditor(page) + .getByRole("textbox") + .fill("http://example.com/jwks.json"); + + await expect(keyEditor(page)).toContainText("must use https"); + }); + + test("fetches a JWKS URI and verifies the token", async ({ page }) => { + const jwksUrl = "https://issuer.e2e.test/.well-known/jwks.json"; + + await page.route(jwksUrl, (route) => + route.fulfill({ + status: 200, + contentType: "application/json", + headers: { "access-control-allow-origin": "*" }, + body: JSON.stringify(toJwks(first, second)), + }), + ); + + await selectPublicKeyFormat(page, "JWKS"); + await keyEditor(page).getByRole("textbox").fill(jwksUrl); + + await expect(getDecoder(page)).toContainText("Signature Verified"); + }); + + test("loads a JWKS from a fragment link", async ({ page }) => { + const params = new URLSearchParams({ + token: await second.sign(second.kid), + publicKey: JSON.stringify(toJwks(first, second)), + }); + + await page.goto(`${E2E_BASE_URL}/#${params.toString()}`); + + await expect(getDecoder(page)).toContainText("Signature Verified"); + }); +}); diff --git a/src/features/common/services/asymmetric-key-input.service.ts b/src/features/common/services/asymmetric-key-input.service.ts index 9108059c..773ba379 100644 --- a/src/features/common/services/asymmetric-key-input.service.ts +++ b/src/features/common/services/asymmetric-key-input.service.ts @@ -4,6 +4,10 @@ import { } from "@/features/common/services/jwt.service"; import { AsymmetricKeyFormatValues } from "@/features/common/values/asymmetric-key-format.values"; +export const isJsonKeyFormat = (format: AsymmetricKeyFormatValues): boolean => + format === AsymmetricKeyFormatValues.JWK || + format === AsymmetricKeyFormatValues.JWKS; + export const detectAsymmetricKeyFormat = ( value: string, ): AsymmetricKeyFormatValues | null => { @@ -28,7 +32,7 @@ export const formatAsymmetricKeyInput = ( ): string => { const trimmedValue = value.trim(); - if (format !== AsymmetricKeyFormatValues.JWK) { + if (!isJsonKeyFormat(format)) { return trimmedValue; } diff --git a/src/features/common/services/jwks.service.ts b/src/features/common/services/jwks.service.ts new file mode 100644 index 00000000..bb6631c2 --- /dev/null +++ b/src/features/common/services/jwks.service.ts @@ -0,0 +1,239 @@ +import { + compactVerify, + createLocalJWKSet, + createRemoteJWKSet, + decodeProtectedHeader, + errors, + type CompactVerifyGetKey, + type JSONWebKeySet, +} from "jose"; +import { err, fromPromise, ok, Result } from "neverthrow"; +import { DebuggerErrorModel } from "@/features/common/models/debugger-error.model"; +import { DebuggerInputValues } from "@/features/common/values/debugger-input.values"; +import { DebuggerTaskValues } from "@/features/common/values/debugger-task.values"; +import { safeJsonParse } from "@/features/common/services/utils"; + +type JwksSource = + | { kind: "uri"; url: URL } + | { kind: "set"; jwks: JSONWebKeySet }; + +type RemoteKeySet = ReturnType; + +const URI_PATTERN = /^https?:\/\//i; +const LOOPBACK_HOSTNAMES = new Set(["localhost", "127.0.0.1", "[::1]"]); +const REMOTE_KEY_SET_OPTIONS = { cacheMaxAge: 30_000, cooldownDuration: 0 }; + +const MESSAGES = { + empty: "Enter a JWKS URI or JWKS JSON to verify the JWT signature.", + invalidJwks: + 'The provided value is not a valid JWKS URI or JWKS JSON. A JWKS is a JSON object with a "keys" array as defined on [RFC 7517](https://datatracker.ietf.org/doc/html/rfc7517#section-5).', + invalidUri: "The JWKS URI is not a valid URL.", + insecureUri: "The JWKS URI must use https.", + timeout: "Timed out while fetching the JWKS.", + signature: "Signature verification failed. Try again with another value.", + fallback: "Unable to verify the signature using the JWKS.", +} as const; + +let lastRemoteKeySet: { href: string; keySet: RemoteKeySet } | null = null; + +const toVerifyKeyError = (message: string): DebuggerErrorModel => ({ + task: DebuggerTaskValues.VERIFY, + input: DebuggerInputValues.KEY, + message, +}); + +const isRecord = (value: unknown): value is Record => + typeof value === "object" && value !== null && !Array.isArray(value); + +const isJwks = (value: unknown): value is JSONWebKeySet => + isRecord(value) && Array.isArray(value.keys) && value.keys.every(isRecord); + +const isSecureUrl = ({ protocol, hostname }: URL): boolean => + protocol === "https:" || + (protocol === "http:" && LOOPBACK_HOSTNAMES.has(hostname)); + +const parseUri = (value: string): Result => { + try { + const url = new URL(value); + + return isSecureUrl(url) ? ok(url) : err(MESSAGES.insecureUri); + } catch { + return err(MESSAGES.invalidUri); + } +}; + +const parseJwksJson = (value: string): Result => + safeJsonParse(value) + .mapErr(() => MESSAGES.invalidJwks) + .andThen((parsed) => + isJwks(parsed) ? ok(parsed) : err(MESSAGES.invalidJwks), + ); + +export const isJwksUri = (value: string): boolean => + URI_PATTERN.test(value.trim()); + +export const isJwksInput = (value: string): boolean => + isJwksUri(value) || parseJwksJson(value.trim()).isOk(); + +export const parseJwksInput = (value: string): Result => { + const trimmedValue = value.trim(); + + if (!trimmedValue) { + return err(MESSAGES.empty); + } + + if (isJwksUri(trimmedValue)) { + return parseUri(trimmedValue).map((url) => ({ kind: "uri", url })); + } + + return parseJwksJson(trimmedValue).map((jwks) => ({ kind: "set", jwks })); +}; + +export const validateJwksInput = ( + value: string, +): Result => + parseJwksInput(value) + .map(() => undefined) + .mapErr(toVerifyKeyError); + +const getRemoteKeySet = (url: URL): RemoteKeySet => { + if (lastRemoteKeySet?.href !== url.href) { + lastRemoteKeySet = { + href: url.href, + keySet: createRemoteJWKSet(url, REMOTE_KEY_SET_OPTIONS), + }; + } + + return lastRemoteKeySet.keySet; +}; + +const getKeyResolver = (source: JwksSource): CompactVerifyGetKey => + source.kind === "uri" + ? getRemoteKeySet(source.url) + : createLocalJWKSet(source.jwks); + +const verifyWithEachCandidate = async ( + jwt: string, + candidates: AsyncIterable, +): Promise => { + for await (const candidate of candidates) { + const outcome = await fromPromise( + compactVerify(jwt, candidate as Parameters[1]), + (e) => e, + ); + + if (outcome.isOk()) { + return; + } + + if (!(outcome.error instanceof errors.JWSSignatureVerificationFailed)) { + throw outcome.error; + } + } + + throw new errors.JWSSignatureVerificationFailed(); +}; + +const verifyWithKeyResolver = async ( + jwt: string, + getKey: CompactVerifyGetKey, +): Promise => { + try { + await compactVerify(jwt, getKey); + } catch (e) { + if (e instanceof errors.JWKSMultipleMatchingKeys) { + return verifyWithEachCandidate(jwt, e); + } + + throw e; + } +}; + +const toFetchFailureMessage = (source: JwksSource): string => + source.kind === "uri" + ? `Unable to fetch the JWKS from ${source.url.host}. The server may be unreachable or may not allow cross-origin requests (CORS). Paste the JWKS JSON instead.` + : MESSAGES.fallback; + +const describeHeaderMatchCriteria = (jwt: string): string => { + try { + const { kid, alg } = decodeProtectedHeader(jwt); + const criteria = [kid && `kid "${kid}"`, alg && `alg "${alg}"`].filter( + Boolean, + ); + + return criteria.length > 0 ? ` (${criteria.join(", ")})` : ""; + } catch { + return ""; + } +}; + +const toNoMatchingKeyMessage = (jwt: string): string => + `No key in the JWKS matches the "kid" and "alg" header values of the JWT${describeHeaderMatchCriteria(jwt)}.`; + +const toVerificationMessage = ( + e: unknown, + source: JwksSource, + jwt: string, +): string => { + if (e instanceof errors.JWKSNoMatchingKey) { + return toNoMatchingKeyMessage(jwt); + } + + if (e instanceof errors.JWKSTimeout) { + return MESSAGES.timeout; + } + + if (e instanceof errors.JWSSignatureVerificationFailed) { + return MESSAGES.signature; + } + + if (e instanceof errors.JOSEError) { + return e.message; + } + + return e instanceof TypeError + ? toFetchFailureMessage(source) + : MESSAGES.fallback; +}; + +export const verifyJwtWithJwks = async ({ + jwt, + jwksInput, +}: { + jwt: string; + jwksInput: string; +}): Promise> => { + const sourceResult = parseJwksInput(jwksInput); + + if (sourceResult.isErr()) { + return err(toVerifyKeyError(sourceResult.error)); + } + + const source = sourceResult.value; + + return fromPromise(verifyWithKeyResolver(jwt, getKeyResolver(source)), (e) => + toVerifyKeyError(toVerificationMessage(e, source, jwt)), + ).map(() => true); +}; + +export const wrapKeyInJwks = (jwk: string): Result => + safeJsonParse(jwk) + .map((parsed) => JSON.stringify({ keys: [parsed] }, null, 2)) + .mapErr(() => MESSAGES.invalidJwks); + +export const extractSingleJwk = (jwksInput: string): Result => + parseJwksInput(jwksInput).andThen((source) => { + if (source.kind === "uri") { + return err( + "A JWKS URI cannot be converted. Paste a PEM or JWK public key instead.", + ); + } + + const { keys } = source.jwks; + + return keys.length === 1 + ? ok(JSON.stringify(keys[0], null, 2)) + : err( + `The JWKS contains ${keys.length} keys. Keep a single key to convert it to another format.`, + ); + }); diff --git a/src/features/common/services/jwt.service.ts b/src/features/common/services/jwt.service.ts index a6d97c79..0875ec8c 100644 --- a/src/features/common/services/jwt.service.ts +++ b/src/features/common/services/jwt.service.ts @@ -23,6 +23,12 @@ import { type JWK, } from "jose"; import nodeForge from "node-forge"; +import { + extractSingleJwk, + validateJwksInput, + verifyJwtWithJwks, + wrapKeyInJwks, +} from "@/features/common/services/jwks.service"; import { convertSec1EcPrivateKeyToPkcs8, isSec1EcPrivateKey, @@ -806,6 +812,15 @@ export async function validateAsymmetricKey({ } } +export const validatePublicKeyInput = async (params: { + alg: string; + asymmetricPublicKey: string; + asymmetricPublicKeyFormat: AsymmetricKeyFormatValues; +}): Promise> => + params.asymmetricPublicKeyFormat === AsymmetricKeyFormatValues.JWKS + ? validateJwksInput(params.asymmetricPublicKey) + : validateAsymmetricKey(params); + type VerifyDigitallySignedJwtParams = { jwt: string; alg: string; @@ -821,6 +836,13 @@ export async function verifyDigitallySignedJwt({ }: VerifyDigitallySignedJwtParams): Promise< Result > { + if (asymmetricPublicKeyFormat === AsymmetricKeyFormatValues.JWKS) { + return verifyJwtWithJwks({ + jwt, + jwksInput: String(asymmetricPublicKey), + }); + } + const validateAsymmetricKeyResult = await validateAsymmetricKey({ alg, asymmetricPublicKey, @@ -1183,6 +1205,32 @@ export const convertAsymmetricPublicKeyFormat = async ({ return ok(key); } + if (sourceFormat === AsymmetricKeyFormatValues.JWKS) { + const singleJwkResult = extractSingleJwk(key); + + return singleJwkResult.isErr() + ? err(singleJwkResult.error) + : convertAsymmetricPublicKeyFormat({ + alg, + key: singleJwkResult.value, + sourceFormat: AsymmetricKeyFormatValues.JWK, + targetFormat, + }); + } + + if (targetFormat === AsymmetricKeyFormatValues.JWKS) { + const jwkResult = await convertAsymmetricPublicKeyFormat({ + alg, + key, + sourceFormat, + targetFormat: AsymmetricKeyFormatValues.JWK, + }); + + return jwkResult.isErr() + ? err(jwkResult.error) + : wrapKeyInJwks(jwkResult.value); + } + const importResult = await validateAsymmetricKey({ alg, asymmetricPublicKey: key, diff --git a/src/features/common/values/asymmetric-key-format.values.ts b/src/features/common/values/asymmetric-key-format.values.ts index 9533060a..c265c6be 100644 --- a/src/features/common/values/asymmetric-key-format.values.ts +++ b/src/features/common/values/asymmetric-key-format.values.ts @@ -1,4 +1,5 @@ export enum AsymmetricKeyFormatValues { PEM = "PEM", JWK = "JWK", + JWKS = "JWKS", } diff --git a/src/features/decoder/components/signature-verification-public-key-input.component.tsx b/src/features/decoder/components/signature-verification-public-key-input.component.tsx index b5a0e64d..ba210001 100644 --- a/src/features/decoder/components/signature-verification-public-key-input.component.tsx +++ b/src/features/decoder/components/signature-verification-public-key-input.component.tsx @@ -4,6 +4,7 @@ import { highlight, languages } from "prismjs"; import "prismjs/components/prism-json"; import { EditorComponent } from "@/features/common/components/code-editor/editor.component"; import { AsymmetricKeyFormatValues } from "@/features/common/values/asymmetric-key-format.values"; +import { isJsonKeyFormat } from "@/features/common/services/asymmetric-key-input.service"; interface SignatureVerificationPublicKeyInputComponentProps { publicKey: string; @@ -20,7 +21,7 @@ export const SignatureVerificationPublicKeyInputComponent: React.FC< handlePublicKeyChange, placeholder, }) => { - if (publicKeyFormat === AsymmetricKeyFormatValues.JWK) { + if (isJsonKeyFormat(publicKeyFormat)) { return ( { if (publicKey) { result.publicKey = publicKey; result.publicKeyFormat = - detectAsymmetricKeyFormat(publicKey) ?? AsymmetricKeyFormatValues.PEM; + detectPublicKeyFormat(publicKey) ?? AsymmetricKeyFormatValues.PEM; } if (usesLegacySyntax) { diff --git a/src/features/decoder/services/decoder.store.ts b/src/features/decoder/services/decoder.store.ts index 6a249d39..daafad3b 100644 --- a/src/features/decoder/services/decoder.store.ts +++ b/src/features/decoder/services/decoder.store.ts @@ -9,7 +9,8 @@ import { subscribeWithSelector } from "zustand/middleware"; import { AsymmetricKeyFormatValues } from "@/features/common/values/asymmetric-key-format.values"; import { JwtSignatureStatusValues } from "@/features/common/values/jwt-signature-status.values"; import { DecoderInputsModel } from "@/features/debugger/models/decoder-inputs.model"; -import { detectAsymmetricKeyFormat } from "@/features/common/services/asymmetric-key-input.service"; +import { isJwksUri } from "@/features/common/services/jwks.service"; +import { detectPublicKeyFormat } from "@/features/decoder/services/public-key-input.service"; export enum HashWarningVisibilityValues { VISIBLE = "VISIBLE", @@ -40,6 +41,11 @@ export const DEFAULT_PAYLOAD = { export const DEFAULT_DECODED_PAYLOAD = JSON.stringify(DEFAULT_PAYLOAD, null, 2); +const JWKS_URI_DEBOUNCE_MS = 400; + +const wait = (milliseconds: number) => + new Promise((resolve) => setTimeout(resolve, milliseconds)); + let latestJwtChangeRequestId = 0; let latestAsymmetricPublicKeyRequestId = 0; @@ -224,7 +230,7 @@ export const useDecoderStore = create()( handleAsymmetricPublicKeyChange: async (newAsymmetricPublicKey) => { const requestId = ++latestAsymmetricPublicKeyRequestId; const { jwt, alg, asymmetricPublicKeyFormat } = get(); - const detectedFormat = detectAsymmetricKeyFormat(newAsymmetricPublicKey); + const detectedFormat = detectPublicKeyFormat(newAsymmetricPublicKey); const nextFormat = detectedFormat ?? asymmetricPublicKeyFormat; set({ @@ -241,6 +247,14 @@ export const useDecoderStore = create()( : {}), }); + if (isJwksUri(newAsymmetricPublicKey)) { + await wait(JWKS_URI_DEBOUNCE_MS); + + if (requestId !== latestAsymmetricPublicKeyRequestId) { + return; + } + } + const update = await TokenDecoderService.handleAsymmetricPublicKeyChange({ jwt, alg, diff --git a/src/features/decoder/services/public-key-input.service.ts b/src/features/decoder/services/public-key-input.service.ts index c231ee59..75bfc0de 100644 --- a/src/features/decoder/services/public-key-input.service.ts +++ b/src/features/decoder/services/public-key-input.service.ts @@ -1 +1,12 @@ +import { detectAsymmetricKeyFormat } from "@/features/common/services/asymmetric-key-input.service"; +import { isJwksInput } from "@/features/common/services/jwks.service"; +import { AsymmetricKeyFormatValues } from "@/features/common/values/asymmetric-key-format.values"; + export { formatAsymmetricKeyInput as formatPublicKeyInput } from "@/features/common/services/asymmetric-key-input.service"; + +export const detectPublicKeyFormat = ( + value: string, +): AsymmetricKeyFormatValues | null => + isJwksInput(value) + ? AsymmetricKeyFormatValues.JWKS + : detectAsymmetricKeyFormat(value); diff --git a/src/features/decoder/services/token-decoder.service.ts b/src/features/decoder/services/token-decoder.service.ts index 3f1715d0..2a62adc7 100644 --- a/src/features/decoder/services/token-decoder.service.ts +++ b/src/features/decoder/services/token-decoder.service.ts @@ -16,7 +16,7 @@ import { isNoneAlg, isSupportedAlg, parseStringIntoValidJsonObject, - validateAsymmetricKey, + validatePublicKeyInput, validateJwtFormat, validateSymmetricSecret, verifyDigitallySignedJwt, @@ -65,6 +65,12 @@ type HandleAsymmetricPublicKeyHelperError = Pick< "signatureStatus" | "verificationInputErrors" | "signatureWarnings" >; +const hasProvidedJwks = ( + publicKey: string, + format: AsymmetricKeyFormatValues, +): boolean => + format === AsymmetricKeyFormatValues.JWKS && publicKey.trim() !== ""; + class _TokenDecoderService { async selectDecodingExample( algorithmPickerOptionValue: string, @@ -166,7 +172,7 @@ class _TokenDecoderService { format: params.asymmetricPublicKeyFormat, }; - const validateAsymmetricKeyResult = await validateAsymmetricKey({ + const validateAsymmetricKeyResult = await validatePublicKeyInput({ alg: params.alg, asymmetricPublicKey: params.asymmetricPublicKey, asymmetricPublicKeyFormat: params.asymmetricPublicKeyFormat, @@ -436,7 +442,7 @@ class _TokenDecoderService { } if (isDigitalSignatureAlg(alg)) { - const validateAsymmetricKeyResult = await validateAsymmetricKey({ + const validateAsymmetricKeyResult = await validatePublicKeyInput({ alg, asymmetricPublicKey: initialAsymmetricPublicKey, asymmetricPublicKeyFormat: initialAsymmetricPublicKeyFormat, @@ -575,6 +581,21 @@ class _TokenDecoderService { return stateUpdate; } + if ( + type === JwtTypeValues.DigitallySigned && + hasProvidedJwks( + initialAsymmetricPublicKey, + initialAsymmetricPublicKeyFormat, + ) + ) { + return this.verifyWithProvidedJwks({ + jwt: cleanJwtInput, + alg: decoded.header.alg, + jwks: initialAsymmetricPublicKey, + stateUpdate, + }); + } + if (type === JwtTypeValues.DigitallySigned) { const downloadPublicKeyResult = await downloadPublicKeyIfPossible({ header: decoded.header, @@ -658,6 +679,33 @@ class _TokenDecoderService { return stateUpdate; } + private async verifyWithProvidedJwks({ + jwt, + alg, + jwks, + stateUpdate, + }: { + jwt: string; + alg: string; + jwks: string; + stateUpdate: Partial; + }): Promise> { + const verificationResult = await verifyDigitallySignedJwt({ + jwt, + alg, + asymmetricPublicKey: jwks, + asymmetricPublicKeyFormat: AsymmetricKeyFormatValues.JWKS, + }); + + return verificationResult.isErr() + ? { + ...stateUpdate, + signatureStatus: JwtSignatureStatusValues.INVALID, + verificationInputErrors: [verificationResult.error.message], + } + : { ...stateUpdate, signatureStatus: JwtSignatureStatusValues.VALID }; + } + async handleSymmetricSecretKeyHelper(params: { jwt: string; symmetricSecretKeyEncoding: EncodingValues; @@ -818,7 +866,7 @@ class _TokenDecoderService { > > { if (!jwt) { - const validateAsymmetricKeyResult = await validateAsymmetricKey({ + const validateAsymmetricKeyResult = await validatePublicKeyInput({ alg, asymmetricPublicKey, asymmetricPublicKeyFormat, @@ -950,7 +998,7 @@ class _TokenDecoderService { let convertedPublicKey: string; if (conversionResult.isErr()) { - const targetFormatValidationResult = await validateAsymmetricKey({ + const targetFormatValidationResult = await validatePublicKeyInput({ alg, asymmetricPublicKey, asymmetricPublicKeyFormat: targetFormat, diff --git a/src/features/localization/dictionaries/home/en.ts b/src/features/localization/dictionaries/home/en.ts index 236f3cbc..7dc11e96 100644 --- a/src/features/localization/dictionaries/home/en.ts +++ b/src/features/localization/dictionaries/home/en.ts @@ -121,7 +121,7 @@ export const enHomeDictionary: HomeDictionaryModel = { placeholder: { secret: "Valid secret", publicKey: - "Public Key in SPKI, PKCS #1, X.509 Certificate, or JWK string format.", + "Public Key in SPKI, PKCS #1, X.509 Certificate, or JWK string format. Choose JWKS to enter a JWKS URI or JWKS JSON.", }, }, }, diff --git a/src/features/localization/dictionaries/home/ja.ts b/src/features/localization/dictionaries/home/ja.ts index c93d254c..0d1b2e83 100644 --- a/src/features/localization/dictionaries/home/ja.ts +++ b/src/features/localization/dictionaries/home/ja.ts @@ -120,7 +120,7 @@ export const jaHomeDictionary: HomeDictionaryModel = { placeholder: { secret: "有効なシークレット", publicKey: - "SPKI、PKCS #1、X.509証明書、またはJWK文字列形式の公開鍵。", + "SPKI、PKCS #1、X.509証明書、またはJWK文字列形式の公開鍵。JWKS URIまたはJWKS JSONを入力するには、JWKSを選択してください。", }, }, }, diff --git a/tests/decoder-fragment.service.test.ts b/tests/decoder-fragment.service.test.ts index 5a644a55..1fbcc253 100644 --- a/tests/decoder-fragment.service.test.ts +++ b/tests/decoder-fragment.service.test.ts @@ -82,6 +82,22 @@ describe("parseDecoderFragment", () => { }); }); + test.each([ + ["a JWKS", JSON.stringify({ keys: [{ kty: "RSA", e: "AQAB", n: "x" }] })], + ["a JWKS URI", "https://issuer.example/.well-known/jwks.json"], + ])("infers JWKS for %s", (_description, publicKey) => { + const params = new URLSearchParams({ + token: "header.payload.signature", + publicKey, + }); + + expect(parseDecoderFragment(`#${params.toString()}`)).toEqual({ + jwt: "header.payload.signature", + publicKey, + publicKeyFormat: AsymmetricKeyFormatValues.JWKS, + }); + }); + test.each([ ["an invalid JSON value", "{invalid"], ["a JSON array", "[]"], diff --git a/tests/decoder-jwks.store.test.ts b/tests/decoder-jwks.store.test.ts new file mode 100644 index 00000000..972eb66a --- /dev/null +++ b/tests/decoder-jwks.store.test.ts @@ -0,0 +1,137 @@ +import { beforeAll, beforeEach, describe, expect, test } from "vitest"; +import { exportJWK, generateKeyPair } from "jose"; +import { + DefaultTokensValues, + DefaultTokenWithKeysModel, +} from "@/features/common/values/default-tokens.values"; +import { AsymmetricKeyFormatValues } from "@/features/common/values/asymmetric-key-format.values"; +import { JwtSignatureStatusValues } from "@/features/common/values/jwt-signature-status.values"; +import { + initialState, + useDecoderStore, +} from "@/features/decoder/services/decoder.store"; +import { detectPublicKeyFormat } from "@/features/decoder/services/public-key-input.service"; + +const rs256 = DefaultTokensValues.RS256 as DefaultTokenWithKeysModel; +const rs384 = DefaultTokensValues.RS384 as DefaultTokenWithKeysModel; + +const toPublicJwk = ({ + d, + p, + q, + dp, + dq, + qi, + ...publicJwk +}: Record) => publicJwk; + +const rs256Jwk = toPublicJwk(rs256.jwk as Record); +const rs384Jwk = toPublicJwk(rs384.jwk as Record); + +let unrelatedJwk: Record; + +beforeAll(async () => { + const { publicKey } = await generateKeyPair("RS256", { extractable: true }); + unrelatedJwk = { ...(await exportJWK(publicKey)), kid: "unrelated" }; +}); + +const jwksOf = (...jwks: object[]) => JSON.stringify({ keys: jwks }); + +describe("detectPublicKeyFormat", () => { + test.each([ + [jwksOf(rs256Jwk), AsymmetricKeyFormatValues.JWKS], + ["https://issuer.example/jwks.json", AsymmetricKeyFormatValues.JWKS], + [JSON.stringify(rs256Jwk), AsymmetricKeyFormatValues.JWK], + [rs256.publicKey, AsymmetricKeyFormatValues.PEM], + ])("detects %#", (value, expected) => { + expect(detectPublicKeyFormat(value)).toBe(expected); + }); + + test("does not guess unknown input", () => { + expect(detectPublicKeyFormat("not a key")).toBeNull(); + }); +}); + +describe("decoder store with a JWKS", () => { + beforeEach(() => { + useDecoderStore.setState(initialState); + }); + + test("verifies the current token against a pasted JWKS", async () => { + useDecoderStore.setState({ jwt: rs256.token, alg: "RS256" }); + + await useDecoderStore + .getState() + .handleAsymmetricPublicKeyChange(jwksOf(rs384Jwk, rs256Jwk)); + + const state = useDecoderStore.getState(); + + expect(state.asymmetricPublicKeyFormat).toBe( + AsymmetricKeyFormatValues.JWKS, + ); + expect(state.signatureStatus).toBe(JwtSignatureStatusValues.VALID); + }); + + test("marks the signature invalid when the JWKS lacks the signing key", async () => { + useDecoderStore.setState({ jwt: rs256.token, alg: "RS256" }); + + await useDecoderStore + .getState() + .handleAsymmetricPublicKeyChange(jwksOf(unrelatedJwk)); + + const state = useDecoderStore.getState(); + + expect(state.signatureStatus).toBe(JwtSignatureStatusValues.INVALID); + expect(state.verificationInputErrors).not.toBeNull(); + }); + + test("keeps the JWKS and re-verifies when a new token is pasted", async () => { + useDecoderStore.setState({ + jwt: rs384.token, + alg: "RS384", + asymmetricPublicKey: jwksOf(rs256Jwk, rs384Jwk), + asymmetricPublicKeyFormat: AsymmetricKeyFormatValues.JWKS, + }); + + await useDecoderStore.getState().handleJwtChange(rs256.token); + + const state = useDecoderStore.getState(); + + expect(state.jwt).toBe(rs256.token); + expect(state.asymmetricPublicKey).toBe(jwksOf(rs256Jwk, rs384Jwk)); + expect(state.asymmetricPublicKeyFormat).toBe( + AsymmetricKeyFormatValues.JWKS, + ); + expect(state.signatureStatus).toBe(JwtSignatureStatusValues.VALID); + }); + + test("converts a single PEM key to a JWKS and back", async () => { + useDecoderStore.setState({ + jwt: rs256.token, + alg: "RS256", + asymmetricPublicKey: rs256.publicKey, + asymmetricPublicKeyFormat: AsymmetricKeyFormatValues.PEM, + }); + + await useDecoderStore + .getState() + .handleAsymmetricPublicKeyFormatChange(AsymmetricKeyFormatValues.JWKS); + + const toJwks = useDecoderStore.getState(); + + expect(toJwks.asymmetricPublicKeyFormat).toBe( + AsymmetricKeyFormatValues.JWKS, + ); + expect(JSON.parse(toJwks.asymmetricPublicKey).keys).toHaveLength(1); + expect(toJwks.signatureStatus).toBe(JwtSignatureStatusValues.VALID); + + await useDecoderStore + .getState() + .handleAsymmetricPublicKeyFormatChange(AsymmetricKeyFormatValues.PEM); + + const backToPem = useDecoderStore.getState(); + + expect(backToPem.asymmetricPublicKey).toContain("BEGIN PUBLIC KEY"); + expect(backToPem.signatureStatus).toBe(JwtSignatureStatusValues.VALID); + }); +}); diff --git a/tests/jwks.service.test.ts b/tests/jwks.service.test.ts new file mode 100644 index 00000000..ab05be28 --- /dev/null +++ b/tests/jwks.service.test.ts @@ -0,0 +1,332 @@ +import { afterEach, beforeAll, describe, expect, test, vi } from "vitest"; +import { + exportJWK, + generateKeyPair, + SignJWT, + type JSONWebKeySet, + type JWK, +} from "jose"; +import { + extractSingleJwk, + isJwksInput, + isJwksUri, + parseJwksInput, + validateJwksInput, + verifyJwtWithJwks, + wrapKeyInJwks, +} from "@/features/common/services/jwks.service"; +import { + convertAsymmetricPublicKeyFormat, + verifyDigitallySignedJwt, +} from "@/features/common/services/jwt.service"; +import { AsymmetricKeyFormatValues } from "@/features/common/values/asymmetric-key-format.values"; + +type TestKey = { + alg: string; + kid: string; + jwk: JWK; + sign: (header?: Record) => Promise; +}; + +const createTestKey = async (alg: string, kid: string): Promise => { + const { publicKey, privateKey } = await generateKeyPair(alg, { + extractable: true, + }); + const jwk = { ...(await exportJWK(publicKey)), kid, alg, use: "sig" }; + + return { + alg, + kid, + jwk, + sign: (header = {}) => + new SignJWT({ sub: "jwks-test" }) + .setProtectedHeader({ alg, typ: "JWT", ...header }) + .sign(privateKey), + }; +}; + +const jwksOf = (...keys: TestKey[]): JSONWebKeySet => ({ + keys: keys.map(({ jwk }) => jwk), +}); + +const jsonResponse = (body: unknown, status = 200) => + new Response(JSON.stringify(body), { + status, + headers: { "content-type": "application/json" }, + }); + +let rsa1: TestKey; +let rsa2: TestKey; +let ec1: TestKey; + +beforeAll(async () => { + [rsa1, rsa2, ec1] = await Promise.all([ + createTestKey("RS256", "rsa-1"), + createTestKey("RS256", "rsa-2"), + createTestKey("ES256", "ec-1"), + ]); +}); + +afterEach(() => { + vi.unstubAllGlobals(); +}); + +describe("JWKS input detection", () => { + test.each(["https://example.com/jwks.json", " http://localhost:3000/k "])( + "treats %j as a URI", + (value) => { + expect(isJwksUri(value)).toBe(true); + expect(isJwksInput(value)).toBe(true); + }, + ); + + test("treats a JSON object with a keys array as a JWKS", () => { + expect(isJwksInput(JSON.stringify(jwksOf(rsa1)))).toBe(true); + expect(isJwksInput('{"keys":[]}')).toBe(true); + }); + + test.each([ + "", + "not a key", + "-----BEGIN PUBLIC KEY-----\nabc\n-----END PUBLIC KEY-----", + '{"kty":"RSA","n":"x","e":"AQAB"}', + '{"keys":"nope"}', + '{"keys":[1]}', + "[]", + ])("does not treat %j as a JWKS", (value) => { + expect(isJwksInput(value)).toBe(false); + }); +}); + +describe("parseJwksInput", () => { + test("rejects empty input", () => { + expect(parseJwksInput(" ")._unsafeUnwrapErr()).toContain("Enter a JWKS"); + }); + + test("rejects insecure remote URIs but allows loopback http", () => { + expect(parseJwksInput("http://example.com/jwks")._unsafeUnwrapErr()).toBe( + "The JWKS URI must use https.", + ); + expect(parseJwksInput("http://localhost:4455/jwks.json").isOk()).toBe(true); + expect(parseJwksInput("https://example.com/jwks.json").isOk()).toBe(true); + }); + + test("rejects a malformed URI", () => { + expect(parseJwksInput("https://")._unsafeUnwrapErr()).toBe( + "The JWKS URI is not a valid URL.", + ); + }); + + test("rejects JSON that is not a JWKS", () => { + expect(parseJwksInput('{"kty":"RSA"}')._unsafeUnwrapErr()).toContain( + "not a valid JWKS", + ); + }); + + test("validateJwksInput reports a verify/key error", () => { + const error = validateJwksInput("nope")._unsafeUnwrapErr(); + + expect(error.message).toContain("not a valid JWKS"); + }); +}); + +describe("verifyJwtWithJwks with JWKS JSON", () => { + test("selects the key by kid", async () => { + const jwks = JSON.stringify(jwksOf(rsa1, rsa2, ec1)); + + for (const key of [rsa1, rsa2, ec1]) { + const result = await verifyJwtWithJwks({ + jwt: await key.sign({ kid: key.kid }), + jwksInput: jwks, + }); + + expect(result.isOk()).toBe(true); + } + }); + + test("tries every candidate when the token has no kid", async () => { + const result = await verifyJwtWithJwks({ + jwt: await rsa2.sign(), + jwksInput: JSON.stringify(jwksOf(rsa1, rsa2)), + }); + + expect(result.isOk()).toBe(true); + }); + + test("reports an invalid signature when no candidate verifies", async () => { + const forged = (await rsa1.sign({ kid: "rsa-1" })) + .split(".") + .slice(0, 2) + .concat((await rsa2.sign({ kid: "rsa-1" })).split(".")[2] as string) + .join("."); + + const result = await verifyJwtWithJwks({ + jwt: forged, + jwksInput: JSON.stringify(jwksOf(rsa1, rsa2)), + }); + + expect(result._unsafeUnwrapErr().message).toBe( + "Signature verification failed. Try again with another value.", + ); + }); + + test("reports when no key matches the kid", async () => { + const result = await verifyJwtWithJwks({ + jwt: await rsa1.sign({ kid: "missing" }), + jwksInput: JSON.stringify(jwksOf(rsa1, rsa2)), + }); + + expect(result._unsafeUnwrapErr().message).toContain('kid "missing"'); + }); + + test("names the kid and alg it could not match", async () => { + const result = await verifyJwtWithJwks({ + jwt: await ec1.sign({ kid: "ec-9" }), + jwksInput: JSON.stringify(jwksOf(rsa1)), + }); + + expect(result._unsafeUnwrapErr().message).toContain( + 'kid "ec-9", alg "ES256"', + ); + }); + + test("reports an empty key set as having no matching key", async () => { + const result = await verifyJwtWithJwks({ + jwt: await rsa1.sign(), + jwksInput: '{"keys":[]}', + }); + + expect(result._unsafeUnwrapErr().message).toContain("No key in the JWKS"); + }); + + test("never accepts a token signed by a key outside the set", async () => { + const outsider = await createTestKey("RS256", "rsa-1"); + + const result = await verifyJwtWithJwks({ + jwt: await outsider.sign({ kid: "rsa-1" }), + jwksInput: JSON.stringify(jwksOf(rsa1)), + }); + + expect(result.isErr()).toBe(true); + }); +}); + +describe("verifyJwtWithJwks with a JWKS URI", () => { + test("fetches the set and verifies the token", async () => { + const fetchMock = vi.fn(async () => jsonResponse(jwksOf(rsa1, ec1))); + vi.stubGlobal("fetch", fetchMock); + + const result = await verifyJwtWithJwks({ + jwt: await ec1.sign({ kid: "ec-1" }), + jwksInput: "https://issuer.example/jwks.json", + }); + + expect(result.isOk()).toBe(true); + expect(fetchMock).toHaveBeenCalledTimes(1); + }); + + test("explains a network or CORS failure and suggests pasting JSON", async () => { + vi.stubGlobal( + "fetch", + vi.fn(async () => { + throw new TypeError("Failed to fetch"); + }), + ); + + const result = await verifyJwtWithJwks({ + jwt: await rsa1.sign({ kid: "rsa-1" }), + jwksInput: "https://no-cors.example/jwks.json", + }); + + const { message } = result._unsafeUnwrapErr(); + + expect(message).toContain("no-cors.example"); + expect(message).toContain("CORS"); + expect(message).toContain("Paste the JWKS JSON"); + }); + + test("reports a non-200 response", async () => { + vi.stubGlobal( + "fetch", + vi.fn(async () => jsonResponse({}, 404)), + ); + + const result = await verifyJwtWithJwks({ + jwt: await rsa1.sign({ kid: "rsa-1" }), + jwksInput: "https://missing.example/jwks.json", + }); + + expect(result._unsafeUnwrapErr().message).toContain("200 OK"); + }); + + test("never sends the token to the JWKS host", async () => { + const jwt = await rsa1.sign({ kid: "rsa-1" }); + const fetchMock = vi.fn(async () => jsonResponse(jwksOf(rsa1))); + vi.stubGlobal("fetch", fetchMock); + + await verifyJwtWithJwks({ + jwt, + jwksInput: "https://privacy.example/jwks.json", + }); + + expect(JSON.stringify(fetchMock.mock.calls)).not.toContain(jwt); + }); +}); + +describe("JWKS through the shared verification entry point", () => { + test("verifyDigitallySignedJwt dispatches the JWKS format", async () => { + const result = await verifyDigitallySignedJwt({ + jwt: await rsa2.sign({ kid: "rsa-2" }), + alg: "RS256", + asymmetricPublicKey: JSON.stringify(jwksOf(rsa1, rsa2)), + asymmetricPublicKeyFormat: AsymmetricKeyFormatValues.JWKS, + }); + + expect(result.isOk()).toBe(true); + }); +}); + +describe("JWKS format conversion", () => { + test("wraps a single JWK into a JWKS", () => { + const wrapped = wrapKeyInJwks(JSON.stringify(rsa1.jwk))._unsafeUnwrap(); + + expect(JSON.parse(wrapped)).toEqual({ keys: [rsa1.jwk] }); + }); + + test("extracts the only key of a one-key JWKS", () => { + const single = extractSingleJwk( + JSON.stringify(jwksOf(rsa1)), + )._unsafeUnwrap(); + + expect(JSON.parse(single)).toEqual(rsa1.jwk); + }); + + test("refuses to collapse a multi-key JWKS or a URI", () => { + expect( + extractSingleJwk(JSON.stringify(jwksOf(rsa1, rsa2)))._unsafeUnwrapErr(), + ).toContain("contains 2 keys"); + expect( + extractSingleJwk("https://example.com/jwks.json")._unsafeUnwrapErr(), + ).toContain("cannot be converted"); + }); + + test("converts JWK to JWKS and back through the public key converter", async () => { + const toJwks = await convertAsymmetricPublicKeyFormat({ + alg: "RS256", + key: JSON.stringify(rsa1.jwk), + sourceFormat: AsymmetricKeyFormatValues.JWK, + targetFormat: AsymmetricKeyFormatValues.JWKS, + }); + + expect(JSON.parse(toJwks._unsafeUnwrap()).keys).toHaveLength(1); + + const back = await convertAsymmetricPublicKeyFormat({ + alg: "RS256", + key: toJwks._unsafeUnwrap(), + sourceFormat: AsymmetricKeyFormatValues.JWKS, + targetFormat: AsymmetricKeyFormatValues.JWK, + }); + + expect(JSON.parse(back._unsafeUnwrap()).kid).toBe("rsa-1"); + }); +}); From fe9fc0f08b77abb857c46b3c7389a5aa87850991 Mon Sep 17 00:00:00 2001 From: Jonathan Puglla Date: Mon, 26 Oct 2026 15:30:11 -0500 Subject: [PATCH 6/8] feat: add toPrivateKeyFormat function and update tests for asymmetric key handling. --- .../services/asymmetric-key-input.service.ts | 7 ++ .../services/sec1-ec-private-key.service.ts | 11 +- .../debugger/services/debugger.store.ts | 4 +- tests/asymmetric-key-input.service.test.ts | 24 ++++- tests/sec1-ec-private-key.test.ts | 101 ++++++++++++++++-- 5 files changed, 125 insertions(+), 22 deletions(-) diff --git a/src/features/common/services/asymmetric-key-input.service.ts b/src/features/common/services/asymmetric-key-input.service.ts index 773ba379..39ad3bf6 100644 --- a/src/features/common/services/asymmetric-key-input.service.ts +++ b/src/features/common/services/asymmetric-key-input.service.ts @@ -8,6 +8,13 @@ export const isJsonKeyFormat = (format: AsymmetricKeyFormatValues): boolean => format === AsymmetricKeyFormatValues.JWK || format === AsymmetricKeyFormatValues.JWKS; +export const toPrivateKeyFormat = ( + publicKeyFormat: AsymmetricKeyFormatValues | undefined, +): AsymmetricKeyFormatValues | undefined => + publicKeyFormat === AsymmetricKeyFormatValues.JWKS + ? AsymmetricKeyFormatValues.PEM + : publicKeyFormat; + export const detectAsymmetricKeyFormat = ( value: string, ): AsymmetricKeyFormatValues | null => { diff --git a/src/features/common/services/sec1-ec-private-key.service.ts b/src/features/common/services/sec1-ec-private-key.service.ts index 20abe71b..d97a0c81 100644 --- a/src/features/common/services/sec1-ec-private-key.service.ts +++ b/src/features/common/services/sec1-ec-private-key.service.ts @@ -106,11 +106,7 @@ const toPkcs8Der = ({ return toDerBytes( createSequence([ - createNode( - asn1.Type.INTEGER, - false, - String.fromCharCode(PKCS8_VERSION), - ), + createNode(asn1.Type.INTEGER, false, String.fromCharCode(PKCS8_VERSION)), algorithmIdentifier, createNode(asn1.Type.OCTETSTRING, false, toDerBytes(ecPrivateKey)), ]), @@ -118,10 +114,7 @@ const toPkcs8Der = ({ }; const toPem = (der: string): string => - pem - .encode({ type: PKCS8_PEM_TYPE, body: der }) - .replace(/\r\n/g, "\n") - .trim(); + pem.encode({ type: PKCS8_PEM_TYPE, body: der }).replace(/\r\n/g, "\n").trim(); export const convertSec1EcPrivateKeyToPkcs8 = ( key: string, diff --git a/src/features/debugger/services/debugger.store.ts b/src/features/debugger/services/debugger.store.ts index 9cfdc67e..629cdf16 100644 --- a/src/features/debugger/services/debugger.store.ts +++ b/src/features/debugger/services/debugger.store.ts @@ -16,6 +16,7 @@ import { ClaimDescriptionVisibilityValues } from "@/features/common/values/claim import { DecoderInputsModel } from "@/features/debugger/models/decoder-inputs.model"; import { EncoderInputsModel } from "@/features/debugger/models/encoder-inputs.model"; import { NOOP_ALG } from "@/features/common/values/constants"; +import { toPrivateKeyFormat } from "@/features/common/services/asymmetric-key-input.service"; export enum DebuggerOutputModalValues { OPEN = "open", @@ -664,8 +665,9 @@ export const useDebuggerStore = create()( algType: SigningAlgCategoryValues.ASYMMETRIC, alg: decoderState.alg, asymmetricPrivateKey: debuggerStash.asymmetricPrivateKey, - asymmetricPrivateKeyFormat: + asymmetricPrivateKeyFormat: toPrivateKeyFormat( debuggerStash.asymmetricPublicKeyFormat, + ), }; } diff --git a/tests/asymmetric-key-input.service.test.ts b/tests/asymmetric-key-input.service.test.ts index f35dcf1b..b42b9611 100644 --- a/tests/asymmetric-key-input.service.test.ts +++ b/tests/asymmetric-key-input.service.test.ts @@ -1,5 +1,8 @@ import { describe, expect, test } from "vitest"; -import { detectAsymmetricKeyFormat } from "@/features/common/services/asymmetric-key-input.service"; +import { + detectAsymmetricKeyFormat, + toPrivateKeyFormat, +} from "@/features/common/services/asymmetric-key-input.service"; import { AsymmetricKeyFormatValues } from "@/features/common/values/asymmetric-key-format.values"; describe("detectAsymmetricKeyFormat", () => { @@ -27,3 +30,22 @@ describe("detectAsymmetricKeyFormat", () => { }, ); }); + +describe("toPrivateKeyFormat", () => { + test("maps JWKS, which has no private key form, to PEM", () => { + expect(toPrivateKeyFormat(AsymmetricKeyFormatValues.JWKS)).toBe( + AsymmetricKeyFormatValues.PEM, + ); + }); + + test.each([AsymmetricKeyFormatValues.PEM, AsymmetricKeyFormatValues.JWK])( + "keeps %s unchanged", + (format) => { + expect(toPrivateKeyFormat(format)).toBe(format); + }, + ); + + test("keeps an unset format unset", () => { + expect(toPrivateKeyFormat(undefined)).toBeUndefined(); + }); +}); diff --git a/tests/sec1-ec-private-key.test.ts b/tests/sec1-ec-private-key.test.ts index b366c854..bdcb6118 100644 --- a/tests/sec1-ec-private-key.test.ts +++ b/tests/sec1-ec-private-key.test.ts @@ -13,24 +13,103 @@ const pem = (lines: string[]) => lines.join("\n"); // and `openssl ec -pubout`. const ecKeys = { ES256: { - sec1: pem(["-----BEGIN EC PRIVATE KEY-----", "MHcCAQEEIGVO/eFcax0cgQ1UE5dMrdKicPsEJB+JEYgsVpzsvXXKoAoGCCqGSM49", "AwEHoUQDQgAE+wqjW5QaNLLkRTnFibBrLmPwmv6Lxl1E6H81YaHw/7qIvtbC71+U", "PRHgx2Lirf6fLp+8iHgyPEbzVCxoVDtdvg==", "-----END EC PRIVATE KEY-----"]), - pkcs8: pem(["-----BEGIN PRIVATE KEY-----", "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgZU794VxrHRyBDVQT", "l0yt0qJw+wQkH4kRiCxWnOy9dcqhRANCAAT7CqNblBo0suRFOcWJsGsuY/Ca/ovG", "XUTofzVhofD/uoi+1sLvX5Q9EeDHYuKt/p8un7yIeDI8RvNULGhUO12+", "-----END PRIVATE KEY-----"]), - pub: pem(["-----BEGIN PUBLIC KEY-----", "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+wqjW5QaNLLkRTnFibBrLmPwmv6L", "xl1E6H81YaHw/7qIvtbC71+UPRHgx2Lirf6fLp+8iHgyPEbzVCxoVDtdvg==", "-----END PUBLIC KEY-----"]), + sec1: pem([ + "-----BEGIN EC PRIVATE KEY-----", + "MHcCAQEEIGVO/eFcax0cgQ1UE5dMrdKicPsEJB+JEYgsVpzsvXXKoAoGCCqGSM49", + "AwEHoUQDQgAE+wqjW5QaNLLkRTnFibBrLmPwmv6Lxl1E6H81YaHw/7qIvtbC71+U", + "PRHgx2Lirf6fLp+8iHgyPEbzVCxoVDtdvg==", + "-----END EC PRIVATE KEY-----", + ]), + pkcs8: pem([ + "-----BEGIN PRIVATE KEY-----", + "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgZU794VxrHRyBDVQT", + "l0yt0qJw+wQkH4kRiCxWnOy9dcqhRANCAAT7CqNblBo0suRFOcWJsGsuY/Ca/ovG", + "XUTofzVhofD/uoi+1sLvX5Q9EeDHYuKt/p8un7yIeDI8RvNULGhUO12+", + "-----END PRIVATE KEY-----", + ]), + pub: pem([ + "-----BEGIN PUBLIC KEY-----", + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE+wqjW5QaNLLkRTnFibBrLmPwmv6L", + "xl1E6H81YaHw/7qIvtbC71+UPRHgx2Lirf6fLp+8iHgyPEbzVCxoVDtdvg==", + "-----END PUBLIC KEY-----", + ]), }, ES384: { - sec1: pem(["-----BEGIN EC PRIVATE KEY-----", "MIGkAgEBBDDOiWzUZhqT3lxKtxnZYyqozvUDfUn1ekYBZYleI7FaEQ4UCNrUVxoG", "wTyUmyIXtvigBwYFK4EEACKhZANiAAQDZ17H1w8LJlNlyIdtJ7dqbcY/z319fARy", "g/90cNqeT2XohpS6GyP8JaXbZlnKLHz88qDBx4CaA3bgrPSjwBAL+uF3CwKjhSs7", "JSb8AGNH2oyUNN80k1FT9T7TAPiI2Rg=", "-----END EC PRIVATE KEY-----"]), - pkcs8: pem(["-----BEGIN PRIVATE KEY-----", "MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDOiWzUZhqT3lxKtxnZ", "YyqozvUDfUn1ekYBZYleI7FaEQ4UCNrUVxoGwTyUmyIXtvihZANiAAQDZ17H1w8L", "JlNlyIdtJ7dqbcY/z319fARyg/90cNqeT2XohpS6GyP8JaXbZlnKLHz88qDBx4Ca", "A3bgrPSjwBAL+uF3CwKjhSs7JSb8AGNH2oyUNN80k1FT9T7TAPiI2Rg=", "-----END PRIVATE KEY-----"]), - pub: pem(["-----BEGIN PUBLIC KEY-----", "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEA2dex9cPCyZTZciHbSe3am3GP899fXwE", "coP/dHDank9l6IaUuhsj/CWl22ZZyix8/PKgwceAmgN24Kz0o8AQC/rhdwsCo4Ur", "OyUm/ABjR9qMlDTfNJNRU/U+0wD4iNkY", "-----END PUBLIC KEY-----"]), + sec1: pem([ + "-----BEGIN EC PRIVATE KEY-----", + "MIGkAgEBBDDOiWzUZhqT3lxKtxnZYyqozvUDfUn1ekYBZYleI7FaEQ4UCNrUVxoG", + "wTyUmyIXtvigBwYFK4EEACKhZANiAAQDZ17H1w8LJlNlyIdtJ7dqbcY/z319fARy", + "g/90cNqeT2XohpS6GyP8JaXbZlnKLHz88qDBx4CaA3bgrPSjwBAL+uF3CwKjhSs7", + "JSb8AGNH2oyUNN80k1FT9T7TAPiI2Rg=", + "-----END EC PRIVATE KEY-----", + ]), + pkcs8: pem([ + "-----BEGIN PRIVATE KEY-----", + "MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDOiWzUZhqT3lxKtxnZ", + "YyqozvUDfUn1ekYBZYleI7FaEQ4UCNrUVxoGwTyUmyIXtvihZANiAAQDZ17H1w8L", + "JlNlyIdtJ7dqbcY/z319fARyg/90cNqeT2XohpS6GyP8JaXbZlnKLHz88qDBx4Ca", + "A3bgrPSjwBAL+uF3CwKjhSs7JSb8AGNH2oyUNN80k1FT9T7TAPiI2Rg=", + "-----END PRIVATE KEY-----", + ]), + pub: pem([ + "-----BEGIN PUBLIC KEY-----", + "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEA2dex9cPCyZTZciHbSe3am3GP899fXwE", + "coP/dHDank9l6IaUuhsj/CWl22ZZyix8/PKgwceAmgN24Kz0o8AQC/rhdwsCo4Ur", + "OyUm/ABjR9qMlDTfNJNRU/U+0wD4iNkY", + "-----END PUBLIC KEY-----", + ]), }, ES512: { - sec1: pem(["-----BEGIN EC PRIVATE KEY-----", "MIHcAgEBBEIBhkQwKFQvB3btYL/BW1Kz387SJXwLQSGvw3q6Afa2EZC0XT2l77Lx", "CCfRz7ilAQbxPc6MyVGKnKTTePCHh6kQv/6gBwYFK4EEACOhgYkDgYYABAFUt5Bx", "ehHtLp9oOXNw8Ds6WWssHSxfTxKiWtGtcpd5JJmkG+177y9JrygsMX6Ojm/ZfrDj", "qFkGONzQ3wRe9YjjZQHc+FN1DhscQ6h4UPBUNndJRKy/wTb/SP0jE8Mc6YWoh5vq", "Lmh6PdrzMGB0UT8p6MmEFQDYhWoIHp6iRlQ/9201fA==", "-----END EC PRIVATE KEY-----"]), - pkcs8: pem(["-----BEGIN PRIVATE KEY-----", "MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBhkQwKFQvB3btYL/B", "W1Kz387SJXwLQSGvw3q6Afa2EZC0XT2l77LxCCfRz7ilAQbxPc6MyVGKnKTTePCH", "h6kQv/6hgYkDgYYABAFUt5BxehHtLp9oOXNw8Ds6WWssHSxfTxKiWtGtcpd5JJmk", "G+177y9JrygsMX6Ojm/ZfrDjqFkGONzQ3wRe9YjjZQHc+FN1DhscQ6h4UPBUNndJ", "RKy/wTb/SP0jE8Mc6YWoh5vqLmh6PdrzMGB0UT8p6MmEFQDYhWoIHp6iRlQ/9201", "fA==", "-----END PRIVATE KEY-----"]), - pub: pem(["-----BEGIN PUBLIC KEY-----", "MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBVLeQcXoR7S6faDlzcPA7OllrLB0s", "X08SolrRrXKXeSSZpBvte+8vSa8oLDF+jo5v2X6w46hZBjjc0N8EXvWI42UB3PhT", "dQ4bHEOoeFDwVDZ3SUSsv8E2/0j9IxPDHOmFqIeb6i5oej3a8zBgdFE/KejJhBUA", "2IVqCB6eokZUP/dtNXw=", "-----END PUBLIC KEY-----"]), + sec1: pem([ + "-----BEGIN EC PRIVATE KEY-----", + "MIHcAgEBBEIBhkQwKFQvB3btYL/BW1Kz387SJXwLQSGvw3q6Afa2EZC0XT2l77Lx", + "CCfRz7ilAQbxPc6MyVGKnKTTePCHh6kQv/6gBwYFK4EEACOhgYkDgYYABAFUt5Bx", + "ehHtLp9oOXNw8Ds6WWssHSxfTxKiWtGtcpd5JJmkG+177y9JrygsMX6Ojm/ZfrDj", + "qFkGONzQ3wRe9YjjZQHc+FN1DhscQ6h4UPBUNndJRKy/wTb/SP0jE8Mc6YWoh5vq", + "Lmh6PdrzMGB0UT8p6MmEFQDYhWoIHp6iRlQ/9201fA==", + "-----END EC PRIVATE KEY-----", + ]), + pkcs8: pem([ + "-----BEGIN PRIVATE KEY-----", + "MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIBhkQwKFQvB3btYL/B", + "W1Kz387SJXwLQSGvw3q6Afa2EZC0XT2l77LxCCfRz7ilAQbxPc6MyVGKnKTTePCH", + "h6kQv/6hgYkDgYYABAFUt5BxehHtLp9oOXNw8Ds6WWssHSxfTxKiWtGtcpd5JJmk", + "G+177y9JrygsMX6Ojm/ZfrDjqFkGONzQ3wRe9YjjZQHc+FN1DhscQ6h4UPBUNndJ", + "RKy/wTb/SP0jE8Mc6YWoh5vqLmh6PdrzMGB0UT8p6MmEFQDYhWoIHp6iRlQ/9201", + "fA==", + "-----END PRIVATE KEY-----", + ]), + pub: pem([ + "-----BEGIN PUBLIC KEY-----", + "MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBVLeQcXoR7S6faDlzcPA7OllrLB0s", + "X08SolrRrXKXeSSZpBvte+8vSa8oLDF+jo5v2X6w46hZBjjc0N8EXvWI42UB3PhT", + "dQ4bHEOoeFDwVDZ3SUSsv8E2/0j9IxPDHOmFqIeb6i5oej3a8zBgdFE/KejJhBUA", + "2IVqCB6eokZUP/dtNXw=", + "-----END PUBLIC KEY-----", + ]), }, } as const; -const sec1WithCurveParameters = pem(["-----BEGIN EC PARAMETERS-----", "BggqhkjOPQMBBw==", "-----END EC PARAMETERS-----", "-----BEGIN EC PRIVATE KEY-----", "MHcCAQEEIEFMkZys1GVqIfKKVh7xD6s7SJMSbVJrnHdYV2mC9w3doAoGCCqGSM49", "AwEHoUQDQgAERBWHf5bkbRrQ4iixJqQjIGzMCx77cGbrSjLPUTOB60qZBlOjk6Uk", "ajaFTz1cHW9qQn6tD2MDbuneX2uVM94O1A==", "-----END EC PRIVATE KEY-----"]); -const encryptedSec1 = pem(["-----BEGIN EC PRIVATE KEY-----", "Proc-Type: 4,ENCRYPTED", "DEK-Info: AES-256-CBC,43350ED0D6F2A3D8198CEA5EB7DEFE64", "", "NaXCpZowzEhUd3JZjW90P/+h/c5jX6667o36NJ+ShaPlGooNyrMco90mGnbM2aEE", "9fRCHAakHfVxQyvd9dgTyjJPqe7Rr2fiIISerTI2sgdYBgrO/w97r9ovcA+8gyTj", "8CrIp1KL3rk1rXJ8gL/9zzhERzndqRDu6iW4pWFhgP4=", "-----END EC PRIVATE KEY-----"]); +const sec1WithCurveParameters = pem([ + "-----BEGIN EC PARAMETERS-----", + "BggqhkjOPQMBBw==", + "-----END EC PARAMETERS-----", + "-----BEGIN EC PRIVATE KEY-----", + "MHcCAQEEIEFMkZys1GVqIfKKVh7xD6s7SJMSbVJrnHdYV2mC9w3doAoGCCqGSM49", + "AwEHoUQDQgAERBWHf5bkbRrQ4iixJqQjIGzMCx77cGbrSjLPUTOB60qZBlOjk6Uk", + "ajaFTz1cHW9qQn6tD2MDbuneX2uVM94O1A==", + "-----END EC PRIVATE KEY-----", +]); +const encryptedSec1 = pem([ + "-----BEGIN EC PRIVATE KEY-----", + "Proc-Type: 4,ENCRYPTED", + "DEK-Info: AES-256-CBC,43350ED0D6F2A3D8198CEA5EB7DEFE64", + "", + "NaXCpZowzEhUd3JZjW90P/+h/c5jX6667o36NJ+ShaPlGooNyrMco90mGnbM2aEE", + "9fRCHAakHfVxQyvd9dgTyjJPqe7Rr2fiIISerTI2sgdYBgrO/w97r9ovcA+8gyTj", + "8CrIp1KL3rk1rXJ8gL/9zzhERzndqRDu6iW4pWFhgP4=", + "-----END EC PRIVATE KEY-----", +]); type EcAlg = keyof typeof ecKeys; From 201bea655b95a4d709f835d01e0dd82e91dce37a Mon Sep 17 00:00:00 2001 From: Jonathan Puglla Date: Mon, 26 Oct 2026 16:24:16 -0500 Subject: [PATCH 7/8] chore: update Node.js version to 24 in workflows and package files. --- next-env.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/next-env.d.ts b/next-env.d.ts index ce4e94a6..a419cbe4 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,7 +1,7 @@ /// /// -import "./.next/types/routes.d.ts"; -import "./.next/types/root-params.d.ts"; +import "./.next/dev/types/routes.d.ts"; +import "./.next/dev/types/root-params.d.ts"; // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. From 0f3e5a4cd1e0d6e0e56a3df7c029a02526fae48f Mon Sep 17 00:00:00 2001 From: Jonathan Puglla Date: Tue, 27 Oct 2026 12:05:04 -0500 Subject: [PATCH 8/8] fix: bump node-forge to 1.4.2 to patch ASN.1. --- package-lock.json | 9 +++++---- package.json | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 09010a21..28ab1c27 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,7 @@ "negotiator": "^1.0.0", "neverthrow": "^8.1.1", "next": "16.3.3", - "node-forge": "^1.3.1", + "node-forge": "^1.4.2", "prismjs": "^1.29.0", "querystring": "^0.2.0", "react": "^18.3.1", @@ -11520,9 +11520,10 @@ } }, "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.2.tgz", + "integrity": "sha512-rRtAR5XLJ8gCKd9du4qrKAsgCG1ObGP4gLeNJMa4vYSeq4bmBMOJk5IHkrviBV0q7uoAJlVd8HEQYQSVUb4FGQ==", + "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" } diff --git a/package.json b/package.json index cb447d98..b8ab53d3 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "negotiator": "^1.0.0", "neverthrow": "^8.1.1", "next": "16.3.3", - "node-forge": "^1.3.1", + "node-forge": "^1.4.2", "prismjs": "^1.29.0", "querystring": "^0.2.0", "react": "^18.3.1",