From 589a95bd5531183b65f66c2b6398e445526f756d Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 23 Jan 2026 10:23:39 +0100 Subject: [PATCH 01/20] feat(database): implement SQLite integration for state management - Updated package.json to include sqlite and sqlite3 as peer dependencies. - Added db.connection.ts to manage SQLite database connections and structure. - Created db.commands.ts for saving application states with expiration handling. - Introduced db.queries.ts for retrieving application states from the database. - Defined db.defaults.ts for default database configurations and table names. --- .github/prompts/npmrefresh.prompt.md | 4 +- package-lock.json | 1421 +++++++++++++++++++++++++- package.json | 6 +- src/node/sqlite/db.commands.ts | 37 + src/node/sqlite/db.connection.ts | 44 + src/node/sqlite/db.defaults.ts | 14 + src/node/sqlite/db.queries.ts | 36 + 7 files changed, 1523 insertions(+), 39 deletions(-) create mode 100644 src/node/sqlite/db.commands.ts create mode 100644 src/node/sqlite/db.connection.ts create mode 100644 src/node/sqlite/db.defaults.ts create mode 100644 src/node/sqlite/db.queries.ts diff --git a/.github/prompts/npmrefresh.prompt.md b/.github/prompts/npmrefresh.prompt.md index cec3910..3ea9ff5 100644 --- a/.github/prompts/npmrefresh.prompt.md +++ b/.github/prompts/npmrefresh.prompt.md @@ -9,8 +9,8 @@ Process: 1. delete `node_modules` folder 2. delete `package-lock.json` file 3. read `package.json` and prepare two CLIs: - - one for installing all dependencies in `peerDependencies` with `npm install --save` - - second for installing all devDependencies in `devDependencies` with `npm install --save-dev` + - one for installing all dependencies in `peerDependencies` with `npm install --save`. Use newest major versions. Dependencies must stay in `peerDependencies`, not in `dependencies`. + - second for installing all devDependencies in `devDependencies` with `npm install --save-dev`. Focus on newest major versions. 5. save both CLIs into `docs/npm-refresh-commands.md` file with header "NPM Refresh Commands" and short description of the process 6. execute both CLIs 7. check if `package.json` needs to be restructured according to `.github/instructions/package-json.instructions.md` file, if yes, restructure it diff --git a/package-lock.json b/package-lock.json index 3b18d03..f8a631a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,7 +35,9 @@ "peerDependencies": { "lodash": "^4.17.21", "luxon": "^3.6.1", - "pino": "^9.6.0" + "pino": "^9.6.0", + "sqlite": "^5.1.1", + "sqlite3": "^5.1.7" } }, "node_modules/@babel/code-frame": { @@ -709,6 +711,13 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "license": "MIT", + "optional": true + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -827,12 +836,37 @@ "node": ">= 8" } }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "license": "MIT", + "optional": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@pinojs/redact": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@rollup/plugin-json": { "version": "6.1.0", @@ -1280,6 +1314,16 @@ "win32" ] }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/@tsconfig/node10": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", @@ -1360,6 +1404,7 @@ "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -1406,6 +1451,7 @@ "integrity": "sha512-iIACsx8pxRnguSYhHiMn2PvhvfpopO9FXHyn1mG5txZIsAaB6F0KwbFnUQN3KCiG3Jcuad/Cao2FAs1Wp7vAyg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.52.0", "@typescript-eslint/types": "8.52.0", @@ -1729,12 +1775,20 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "license": "ISC", + "optional": true + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1765,6 +1819,46 @@ "node": ">=0.4.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "license": "MIT", + "optional": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1782,6 +1876,16 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -1825,6 +1929,28 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/aproba": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", + "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", + "license": "ISC", + "optional": true + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/arg": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", @@ -1864,7 +1990,6 @@ "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -1873,7 +1998,27 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, + "devOptional": true, + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], "license": "MIT" }, "node_modules/binary-extensions": { @@ -1889,6 +2034,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -1912,6 +2077,30 @@ "node": ">=8" } }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -1922,6 +2111,36 @@ "node": ">=8" } }, + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2014,6 +2233,25 @@ "node": ">= 6" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2034,6 +2272,16 @@ "dev": true, "license": "MIT" }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", + "optional": true, + "bin": { + "color-support": "bin.js" + } + }, "node_modules/commander": { "version": "9.5.0", "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", @@ -2048,9 +2296,16 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, + "devOptional": true, "license": "MIT" }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "license": "ISC", + "optional": true + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -2077,7 +2332,7 @@ "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2091,6 +2346,21 @@ } } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/deep-eql": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", @@ -2101,6 +2371,15 @@ "node": ">=6" } }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2118,6 +2397,22 @@ "node": ">=0.10.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT", + "optional": true + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/diff": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", @@ -2141,6 +2436,49 @@ "node": ">=8" } }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT", + "optional": true + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "license": "MIT", + "optional": true + }, "node_modules/es-module-lexer": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", @@ -2209,6 +2547,7 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -2535,6 +2874,15 @@ "node": ">=0.10.0" } }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, "node_modules/expect-type": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", @@ -2637,6 +2985,12 @@ "node": ">=16.0.0" } }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -2688,6 +3042,31 @@ "dev": true, "license": "ISC" }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "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==", + "license": "ISC", + "optional": true + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2713,6 +3092,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/get-tsconfig": { "version": "4.13.0", "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz", @@ -2726,6 +3126,34 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "optional": 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" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2739,17 +3167,41 @@ "node": ">=10.13.0" } }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "optional": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "optional": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globby": { @@ -2790,6 +3242,13 @@ "dev": true, "license": "MIT" }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC", + "optional": true + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2800,6 +3259,13 @@ "node": ">=8" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC", + "optional": true + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -2813,6 +3279,85 @@ "node": ">= 0.4" } }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause", + "optional": true + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", @@ -2844,12 +3389,63 @@ "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "license": "ISC", + "optional": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "optional": 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==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 12" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -2889,6 +3485,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -2902,6 +3508,13 @@ "node": ">=0.10.0" } }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "license": "MIT", + "optional": true + }, "node_modules/is-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", @@ -2923,7 +3536,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, + "devOptional": true, "license": "ISC" }, "node_modules/js-tokens": { @@ -3042,6 +3655,19 @@ "dev": true, "license": "MIT" }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/luxon": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", @@ -3069,6 +3695,34 @@ "dev": true, "license": "ISC" }, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "license": "ISC", + "optional": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -3106,6 +3760,18 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -3126,17 +3792,129 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "license": "MIT", + "optional": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/mylas": { @@ -3172,6 +3950,12 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -3179,6 +3963,75 @@ "dev": true, "license": "MIT" }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "3.87.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.87.0.tgz", + "integrity": "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "license": "MIT", + "optional": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -3189,16 +4042,41 @@ "node": ">=0.10.0" } }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/on-exit-leak-free": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", "license": "MIT", - "peer": true, "engines": { "node": ">=14.0.0" } }, + "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==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -3249,6 +4127,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3272,6 +4166,16 @@ "node": ">=8" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "optional": 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", @@ -3364,7 +4268,6 @@ "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", "license": "MIT", - "peer": true, "dependencies": { "split2": "^4.0.0" } @@ -3373,8 +4276,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/plimit-lit": { "version": "1.6.1", @@ -3418,6 +4320,32 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -3442,8 +4370,38 @@ "url": "https://opencollective.com/fastify" } ], + "license": "MIT" + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "license": "ISC", + "optional": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", "license": "MIT", - "peer": true + "optional": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } }, "node_modules/punycode": { "version": "2.3.1", @@ -3490,8 +4448,45 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "license": "MIT", - "peer": true + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } }, "node_modules/readdirp": { "version": "3.6.0", @@ -3524,7 +4519,6 @@ "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 12.13.0" } @@ -3583,6 +4577,16 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 4" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -3594,12 +4598,30 @@ "node": ">=0.10.0" } }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "optional": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/rollup": { "version": "4.55.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz", "integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -3686,21 +4708,46 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/safe-stable-stringify": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT", + "optional": true + }, "node_modules/semver": { "version": "7.7.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -3709,6 +4756,13 @@ "node": ">=10" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC", + "optional": true + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3739,6 +4793,58 @@ "dev": true, "license": "ISC" }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC", + "optional": true + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -3749,12 +4855,52 @@ "node": ">=8" } }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "license": "MIT", + "optional": true, + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/sonic-boom": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", "license": "MIT", - "peer": true, "dependencies": { "atomic-sleep": "^1.0.0" } @@ -3774,11 +4920,55 @@ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", "license": "ISC", - "peer": true, "engines": { "node": ">= 10.x" } }, + "node_modules/sqlite": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/sqlite/-/sqlite-5.1.1.tgz", + "integrity": "sha512-oBkezXa2hnkfuJwUo44Hl9hS3er+YFtueifoajrgidvqsJRQFpc5fKoAkAor1O5ZnLoa28GBScfHXs8j0K358Q==", + "license": "MIT", + "peer": true + }, + "node_modules/sqlite3": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", + "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.1", + "tar": "^6.1.11" + }, + "optionalDependencies": { + "node-gyp": "8.x" + }, + "peerDependencies": { + "node-gyp": "8.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + } + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -3793,6 +4983,43 @@ "dev": true, "license": "MIT" }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "optional": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", @@ -3862,12 +5089,72 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me", + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, "node_modules/thread-stream": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", "license": "MIT", - "peer": true, "dependencies": { "real-require": "^0.2.0" } @@ -4066,7 +5353,8 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, - "license": "0BSD" + "license": "0BSD", + "peer": true }, "node_modules/tsx": { "version": "4.21.0", @@ -4074,6 +5362,7 @@ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" @@ -4088,6 +5377,18 @@ "fsevents": "~2.3.3" } }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -4107,6 +5408,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -4146,6 +5448,26 @@ "dev": true, "license": "MIT" }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "license": "ISC", + "optional": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -4156,6 +5478,12 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -4169,6 +5497,7 @@ "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -4842,7 +6171,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, + "devOptional": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -4871,6 +6200,16 @@ "node": ">=8" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -4881,6 +6220,18 @@ "node": ">=0.10.0" } }, + "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==", + "license": "ISC" + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index 396e252..d99b13e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@gisatcz/ptr-be-core", "version": "0.0.7", - "description": "Shared core library for PTR BE services", + "description": "Shared core library for PTR BE services and SSR applications", "repository": { "type": "git", "url": "https://github.com/Gisat/ptr-be-core.git" @@ -40,7 +40,9 @@ "peerDependencies": { "lodash": "^4.17.21", "luxon": "^3.6.1", - "pino": "^9.6.0" + "pino": "^9.6.0", + "sqlite": "^5.1.1", + "sqlite3": "^5.1.7" }, "devDependencies": { "@rollup/plugin-json": "^6.1.0", diff --git a/src/node/sqlite/db.commands.ts b/src/node/sqlite/db.commands.ts new file mode 100644 index 0000000..970b7e6 --- /dev/null +++ b/src/node/sqlite/db.commands.ts @@ -0,0 +1,37 @@ +import { AppDb } from "./db.connection"; +import { UsedSqlTables } from "./db.defaults"; + +/** + * Saves a state to the database with the specified key. + * + * @param db - The application database instance. + * @param key - The unique identifier for the state. + * @param state - The state data to be saved as a string. + * @returns A promise that resolves when the state has been saved. + */ +export const dbSaveState = async (db: AppDb, key: string, state: string, expirationSec: number) => { + + // Clean up expired state rows + await db.run( + `DELETE FROM ${UsedSqlTables.APP_STATES} + WHERE expires_at < ?`, + [Math.floor(Date.now() / 1000)] + ); + + // Calculate expiration date by adding seconds to current time + const currentTimestamp = Math.floor(Date.now() / 1000); // Current time in seconds + const expiresAt = currentTimestamp + expirationSec; + + // Convert JSON to string + const jsonString = JSON.stringify(state); + + // Convert string to binary (UTF-8) + const encoder = new TextEncoder(); // Available in most modern environments + const binaryData = encoder.encode(jsonString); // Uint8Array + + await db.run( + `INSERT INTO ${UsedSqlTables.APP_STATES} (key, state, expires_at) + VALUES (?, ?, ?)`, + [key, binaryData, expiresAt] + ); +} \ No newline at end of file diff --git a/src/node/sqlite/db.connection.ts b/src/node/sqlite/db.connection.ts new file mode 100644 index 0000000..daeb5e9 --- /dev/null +++ b/src/node/sqlite/db.connection.ts @@ -0,0 +1,44 @@ +import sqlite3 from 'sqlite3'; +import { Database, open } from 'sqlite'; +import { UsedSqlTables } from './db.defaults'; + +/** + * Represents a SQLite database connection for the application. + * + * This type is a wrapper around the `Database` type from the SQLite library, + * specifying the database and statement types as `sqlite3.Database` and + * `sqlite3.Statement` respectively. + * + * @type {Database} + */ +export type AppDb = Database + +/** + * Opens a connection to a SQLite database. + * + * @param filename - The path to the SQLite database file. If the file does not exist, it will be created. + * @returns A Promise that resolves to an AppDb instance representing the opened database connection. + * + * @example + * ```typescript + * const db = await openDb('./myDatabase.sqlite'); + * ``` + */ +export async function openDb(filename: string): Promise { + const db = await open({ + filename, //database file will be created if it does not exist + driver: sqlite3.Database, + }); + + // create the database structure + await db.run(` + CREATE TABLE IF NOT EXISTS ${UsedSqlTables.APP_STATES} ( + key VARCHAR(50) PRIMARY KEY, + state BLOB, + expires_at DATETIME + )`); + + // return the database connection + // Note: The database connection is automatically closed when the process exits + return db; +} diff --git a/src/node/sqlite/db.defaults.ts b/src/node/sqlite/db.defaults.ts new file mode 100644 index 0000000..c606604 --- /dev/null +++ b/src/node/sqlite/db.defaults.ts @@ -0,0 +1,14 @@ +export const DEFAULT_DB_NAME = "db.sqlite"; +export const DEFAULT_DB_STATE_EXPIRATION_SEC = 60 * 60 * 24; // default to 1 day + +/** + * Enumeration of the SQL table names used throughout the application. + * + * This enum provides a centralized registry of table names to ensure consistent + * references across the application's database operations. + * + * @enum {string} + */ +export enum UsedSqlTables { + APP_STATES = "states" +} \ No newline at end of file diff --git a/src/node/sqlite/db.queries.ts b/src/node/sqlite/db.queries.ts new file mode 100644 index 0000000..dd1ce6b --- /dev/null +++ b/src/node/sqlite/db.queries.ts @@ -0,0 +1,36 @@ +import { AppDb } from "./db.connection"; +import { UsedSqlTables } from "./db.defaults"; + +/** + * Retrieves the application state associated with the specified key from the database. + * + * @param db - The application database instance. + * @param key - The key identifying the state to retrieve. + * @returns A promise that resolves to the requested state data or undefined if not found. + */ +export const dbNeedAppState = async (db: AppDb, key: string): Promise<{ + key: any; + state: any; + expiresAt: number; +} | null> => { + + // read the state from the database + const wantedState = await db.get(`SELECT * FROM ${UsedSqlTables.APP_STATES} WHERE key= ?`, key) + + // if the state is not found, return null + if (!wantedState) + return null + + // convert binary state back to JSON format + const decoder = new TextDecoder("utf-8"); + const decodedString = decoder.decode(wantedState.state); + const jsonState = JSON.parse(decodedString); + + // return the state and metadata + return { + key: wantedState.key, + state: jsonState, + expiresAt: wantedState.expires_at + } + +} \ No newline at end of file From 56167f8ed5dbde791943e57b7907c46f91705059 Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 23 Jan 2026 13:42:57 +0100 Subject: [PATCH 02/20] refactor(database): restructure SQLite integration and move enums to a dedicated file --- src/node/sqlite/{db.defaults.ts => enums.sqlite.ts} | 0 src/node/sqlite/{db.commands.ts => sqlite.commands.ts} | 4 ++-- src/node/sqlite/{db.connection.ts => sqlite.connection.ts} | 2 +- src/node/sqlite/{db.queries.ts => sqlite.queries.ts} | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) rename src/node/sqlite/{db.defaults.ts => enums.sqlite.ts} (100%) rename src/node/sqlite/{db.commands.ts => sqlite.commands.ts} (92%) rename src/node/sqlite/{db.connection.ts => sqlite.connection.ts} (96%) rename src/node/sqlite/{db.queries.ts => sqlite.queries.ts} (91%) diff --git a/src/node/sqlite/db.defaults.ts b/src/node/sqlite/enums.sqlite.ts similarity index 100% rename from src/node/sqlite/db.defaults.ts rename to src/node/sqlite/enums.sqlite.ts diff --git a/src/node/sqlite/db.commands.ts b/src/node/sqlite/sqlite.commands.ts similarity index 92% rename from src/node/sqlite/db.commands.ts rename to src/node/sqlite/sqlite.commands.ts index 970b7e6..d2697ce 100644 --- a/src/node/sqlite/db.commands.ts +++ b/src/node/sqlite/sqlite.commands.ts @@ -1,5 +1,5 @@ -import { AppDb } from "./db.connection"; -import { UsedSqlTables } from "./db.defaults"; +import { AppDb } from "./sqlite.connection"; +import { UsedSqlTables } from "./enums.sqlite"; /** * Saves a state to the database with the specified key. diff --git a/src/node/sqlite/db.connection.ts b/src/node/sqlite/sqlite.connection.ts similarity index 96% rename from src/node/sqlite/db.connection.ts rename to src/node/sqlite/sqlite.connection.ts index daeb5e9..9205ddf 100644 --- a/src/node/sqlite/db.connection.ts +++ b/src/node/sqlite/sqlite.connection.ts @@ -1,6 +1,6 @@ import sqlite3 from 'sqlite3'; import { Database, open } from 'sqlite'; -import { UsedSqlTables } from './db.defaults'; +import { UsedSqlTables } from './enums.sqlite'; /** * Represents a SQLite database connection for the application. diff --git a/src/node/sqlite/db.queries.ts b/src/node/sqlite/sqlite.queries.ts similarity index 91% rename from src/node/sqlite/db.queries.ts rename to src/node/sqlite/sqlite.queries.ts index dd1ce6b..4fe1ebd 100644 --- a/src/node/sqlite/db.queries.ts +++ b/src/node/sqlite/sqlite.queries.ts @@ -1,5 +1,5 @@ -import { AppDb } from "./db.connection"; -import { UsedSqlTables } from "./db.defaults"; +import { AppDb } from "./sqlite.connection"; +import { UsedSqlTables } from "./enums.sqlite"; /** * Retrieves the application state associated with the specified key from the database. From 2dbbb0d6e673fd7946def7b69fb29c8ee7aa3385 Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 23 Jan 2026 13:49:04 +0100 Subject: [PATCH 03/20] feat(sqlite): add enums and default constants for SQLite integration --- src/index.node.ts | 26 +++++++++++++++++++++++++- src/node/sqlite/defaults.sqlite.ts | 11 +++++++++++ src/node/sqlite/enums.sqlite.ts | 3 --- 3 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 src/node/sqlite/defaults.sqlite.ts diff --git a/src/index.node.ts b/src/index.node.ts index 6bc6a5a..70e28b5 100644 --- a/src/index.node.ts +++ b/src/index.node.ts @@ -48,4 +48,28 @@ export { export { type AppSchemaTemplate, SwaggerTypes -} from "./node/api/swagger.universal.js"; \ No newline at end of file +} from "./node/api/swagger.universal.js"; + +// SQLite enums +export { + UsedSqlTables, +} from "./node/sqlite/enums.sqlite.js"; + +// SQLite default constants +export { + DEFAULT_DB_NAME, + DEFAULT_DB_STATE_EXPIRATION_SEC +} from "./node/sqlite/defaults.sqlite.js"; + +// SQLite database connection +export { openDb, type AppDb } from "./node/sqlite/sqlite.connection.js"; + +// SQLite commands for app state management +export { + dbSaveState +} from "./node/sqlite/sqlite.commands.js"; + +// SQLite queries for app state retrieval +export { + dbNeedAppState +} from "./node/sqlite/sqlite.queries.js"; \ No newline at end of file diff --git a/src/node/sqlite/defaults.sqlite.ts b/src/node/sqlite/defaults.sqlite.ts new file mode 100644 index 0000000..0b7db02 --- /dev/null +++ b/src/node/sqlite/defaults.sqlite.ts @@ -0,0 +1,11 @@ +/** + * The default name for the SQLite database file in backend and SSR apps. + * Default value is "db.sqlite". + */ +export const DEFAULT_DB_NAME = "db.sqlite"; + +/** + * The default expiration time for database states, in seconds. For backend and SSR apps. + * Default value is set to 1 day (86400 seconds). + */ +export const DEFAULT_DB_STATE_EXPIRATION_SEC = 60 * 60 * 24; // default to 1 day diff --git a/src/node/sqlite/enums.sqlite.ts b/src/node/sqlite/enums.sqlite.ts index c606604..00954ae 100644 --- a/src/node/sqlite/enums.sqlite.ts +++ b/src/node/sqlite/enums.sqlite.ts @@ -1,6 +1,3 @@ -export const DEFAULT_DB_NAME = "db.sqlite"; -export const DEFAULT_DB_STATE_EXPIRATION_SEC = 60 * 60 * 24; // default to 1 day - /** * Enumeration of the SQL table names used throughout the application. * From 4b9d450aaa7667cd21e6a3e2a33055753a0390e0 Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 23 Jan 2026 14:32:05 +0100 Subject: [PATCH 04/20] chore: update dependencies and add npm refresh commands documentation - Updated peerDependencies and devDependencies in package.json to latest versions. - Added new documentation file npm-refresh-commands.md with instructions for reinstalling peer and dev dependencies using the latest published versions. --- docs/npm-refresh-commands.md | 17 + eslint.config.mjs | 13 +- package-lock.json | 1229 +++++++++------------------------- package.json | 36 +- 4 files changed, 370 insertions(+), 925 deletions(-) create mode 100644 docs/npm-refresh-commands.md diff --git a/docs/npm-refresh-commands.md b/docs/npm-refresh-commands.md new file mode 100644 index 0000000..a8cfc11 --- /dev/null +++ b/docs/npm-refresh-commands.md @@ -0,0 +1,17 @@ +# NPM Refresh Commands + +Short description: commands to reinstall peer and dev dependencies using the newest published majors. The peer-dependency install uses `--no-save` to avoid moving peer entries into `dependencies`. + +1) Install peer dependencies (use latest published versions, do not modify package.json): + +``` +npm install lodash@latest luxon@latest pino@latest sqlite@latest sqlite3@latest --no-save +``` + +2) Install devDependencies (use latest published versions and save to `devDependencies`): + +``` +npm install @rollup/plugin-json@latest @rollup/plugin-node-resolve@latest @rollup/plugin-typescript@latest @types/lodash@latest @types/luxon@latest @types/node@latest @typescript-eslint/eslint-plugin@latest @typescript-eslint/parser@latest eslint@latest eslint-plugin-node@latest rollup@latest rollup-plugin-dts@latest ts-node@latest tsc-alias@latest tsconfig-paths@latest tslib@latest tsx@latest typescript@latest typescript-eslint@latest vite@latest vite-tsconfig-paths@latest vitest@latest --save-dev +``` + +Run these two commands from the repository root to refresh packages. diff --git a/eslint.config.mjs b/eslint.config.mjs index 2e2c74e..eb5571f 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -2,9 +2,16 @@ import { defineConfig } from "eslint/config"; import tseslint from "typescript-eslint"; export default defineConfig([ + { + ignores: [ + '**/dev/*', + '**/dist/*', + '**/tests/*', + 'tsconfig.json', + ] + }, { files: ["**/*.ts"], - ignores: ["**/node_modules/**", "**/dist/**", "vitest.config.ts"], languageOptions: { parser: tseslint.parser, parserOptions: { @@ -16,10 +23,10 @@ export default defineConfig([ "@typescript-eslint": tseslint.plugin, }, extends: [ - "@typescript-eslint/recommended", + "@typescript-eslint/recommended", ], rules: { - "@typescript-eslint/no-explicit-any": "off" + "@typescript-eslint/no-explicit-any": "off", }, }, ]); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f8a631a..51fc9c7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,27 +10,27 @@ "license": "ISC", "devDependencies": { "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^16.0.1", - "@rollup/plugin-typescript": "^12.1.2", - "@types/lodash": "^4.17.13", - "@types/luxon": "^3.6.2", - "@types/node": "^22.15.12", - "@typescript-eslint/eslint-plugin": "^8.32.1", - "@typescript-eslint/parser": "^8.32.1", - "eslint": "^9.27.0", + "@rollup/plugin-node-resolve": "^16.0.3", + "@rollup/plugin-typescript": "^12.3.0", + "@types/lodash": "^4.17.23", + "@types/luxon": "^3.7.1", + "@types/node": "^25.0.10", + "@typescript-eslint/eslint-plugin": "^8.53.1", + "@typescript-eslint/parser": "^8.53.1", + "eslint": "^9.39.2", "eslint-plugin-node": "^11.1.0", - "rollup": "^4.40.2", - "rollup-plugin-dts": "^6.2.3", + "rollup": "^4.56.0", + "rollup-plugin-dts": "^6.3.0", "ts-node": "^10.9.2", "tsc-alias": "^1.8.16", "tsconfig-paths": "^4.2.0", "tslib": "^2.8.1", - "tsx": "^4.19.4", + "tsx": "^4.21.0", "typescript": "^5.9.3", - "typescript-eslint": "^8.32.1", - "vite": "^6.2.5", - "vite-tsconfig-paths": "^5.1.4", - "vitest": "^3.1.1" + "typescript-eslint": "^8.53.1", + "vite": "^7.3.1", + "vite-tsconfig-paths": "^6.0.4", + "vitest": "^4.0.18" }, "peerDependencies": { "lodash": "^4.17.21", @@ -41,14 +41,14 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", + "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -965,9 +965,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz", - "integrity": "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.56.0.tgz", + "integrity": "sha512-LNKIPA5k8PF1+jAFomGe3qN3bbIgJe/IlpDBwuVjrDKrJhVWywgnJvflMt/zkbVNLFtF1+94SljYQS6e99klnw==", "cpu": [ "arm" ], @@ -979,9 +979,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz", - "integrity": "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.56.0.tgz", + "integrity": "sha512-lfbVUbelYqXlYiU/HApNMJzT1E87UPGvzveGg2h0ktUNlOCxKlWuJ9jtfvs1sKHdwU4fzY7Pl8sAl49/XaEk6Q==", "cpu": [ "arm64" ], @@ -993,9 +993,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz", - "integrity": "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.56.0.tgz", + "integrity": "sha512-EgxD1ocWfhoD6xSOeEEwyE7tDvwTgZc8Bss7wCWe+uc7wO8G34HHCUH+Q6cHqJubxIAnQzAsyUsClt0yFLu06w==", "cpu": [ "arm64" ], @@ -1007,9 +1007,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz", - "integrity": "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.56.0.tgz", + "integrity": "sha512-1vXe1vcMOssb/hOF8iv52A7feWW2xnu+c8BV4t1F//m9QVLTfNVpEdja5ia762j/UEJe2Z1jAmEqZAK42tVW3g==", "cpu": [ "x64" ], @@ -1021,9 +1021,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz", - "integrity": "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.56.0.tgz", + "integrity": "sha512-bof7fbIlvqsyv/DtaXSck4VYQ9lPtoWNFCB/JY4snlFuJREXfZnm+Ej6yaCHfQvofJDXLDMTVxWscVSuQvVWUQ==", "cpu": [ "arm64" ], @@ -1035,9 +1035,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz", - "integrity": "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.56.0.tgz", + "integrity": "sha512-KNa6lYHloW+7lTEkYGa37fpvPq+NKG/EHKM8+G/g9WDU7ls4sMqbVRV78J6LdNuVaeeK5WB9/9VAFbKxcbXKYg==", "cpu": [ "x64" ], @@ -1049,9 +1049,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz", - "integrity": "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.56.0.tgz", + "integrity": "sha512-E8jKK87uOvLrrLN28jnAAAChNq5LeCd2mGgZF+fGF5D507WlG/Noct3lP/QzQ6MrqJ5BCKNwI9ipADB6jyiq2A==", "cpu": [ "arm" ], @@ -1063,9 +1063,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz", - "integrity": "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.56.0.tgz", + "integrity": "sha512-jQosa5FMYF5Z6prEpTCCmzCXz6eKr/tCBssSmQGEeozA9tkRUty/5Vx06ibaOP9RCrW1Pvb8yp3gvZhHwTDsJw==", "cpu": [ "arm" ], @@ -1077,9 +1077,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz", - "integrity": "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.56.0.tgz", + "integrity": "sha512-uQVoKkrC1KGEV6udrdVahASIsaF8h7iLG0U0W+Xn14ucFwi6uS539PsAr24IEF9/FoDtzMeeJXJIBo5RkbNWvQ==", "cpu": [ "arm64" ], @@ -1091,9 +1091,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz", - "integrity": "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.56.0.tgz", + "integrity": "sha512-vLZ1yJKLxhQLFKTs42RwTwa6zkGln+bnXc8ueFGMYmBTLfNu58sl5/eXyxRa2RarTkJbXl8TKPgfS6V5ijNqEA==", "cpu": [ "arm64" ], @@ -1105,9 +1105,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz", - "integrity": "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.56.0.tgz", + "integrity": "sha512-FWfHOCub564kSE3xJQLLIC/hbKqHSVxy8vY75/YHHzWvbJL7aYJkdgwD/xGfUlL5UV2SB7otapLrcCj2xnF1dg==", "cpu": [ "loong64" ], @@ -1119,9 +1119,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz", - "integrity": "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.56.0.tgz", + "integrity": "sha512-z1EkujxIh7nbrKL1lmIpqFTc/sr0u8Uk0zK/qIEFldbt6EDKWFk/pxFq3gYj4Bjn3aa9eEhYRlL3H8ZbPT1xvA==", "cpu": [ "loong64" ], @@ -1133,9 +1133,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz", - "integrity": "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.56.0.tgz", + "integrity": "sha512-iNFTluqgdoQC7AIE8Q34R3AuPrJGJirj5wMUErxj22deOcY7XwZRaqYmB6ZKFHoVGqRcRd0mqO+845jAibKCkw==", "cpu": [ "ppc64" ], @@ -1147,9 +1147,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz", - "integrity": "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.56.0.tgz", + "integrity": "sha512-MtMeFVlD2LIKjp2sE2xM2slq3Zxf9zwVuw0jemsxvh1QOpHSsSzfNOTH9uYW9i1MXFxUSMmLpeVeUzoNOKBaWg==", "cpu": [ "ppc64" ], @@ -1161,9 +1161,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz", - "integrity": "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.56.0.tgz", + "integrity": "sha512-in+v6wiHdzzVhYKXIk5U74dEZHdKN9KH0Q4ANHOTvyXPG41bajYRsy7a8TPKbYPl34hU7PP7hMVHRvv/5aCSew==", "cpu": [ "riscv64" ], @@ -1175,9 +1175,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz", - "integrity": "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.56.0.tgz", + "integrity": "sha512-yni2raKHB8m9NQpI9fPVwN754mn6dHQSbDTwxdr9SE0ks38DTjLMMBjrwvB5+mXrX+C0npX0CVeCUcvvvD8CNQ==", "cpu": [ "riscv64" ], @@ -1189,9 +1189,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz", - "integrity": "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.56.0.tgz", + "integrity": "sha512-zhLLJx9nQPu7wezbxt2ut+CI4YlXi68ndEve16tPc/iwoylWS9B3FxpLS2PkmfYgDQtosah07Mj9E0khc3Y+vQ==", "cpu": [ "s390x" ], @@ -1203,9 +1203,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz", - "integrity": "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.56.0.tgz", + "integrity": "sha512-MVC6UDp16ZSH7x4rtuJPAEoE1RwS8N4oK9DLHy3FTEdFoUTCFVzMfJl/BVJ330C+hx8FfprA5Wqx4FhZXkj2Kw==", "cpu": [ "x64" ], @@ -1217,9 +1217,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz", - "integrity": "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.56.0.tgz", + "integrity": "sha512-ZhGH1eA4Qv0lxaV00azCIS1ChedK0V32952Md3FtnxSqZTBTd6tgil4nZT5cU8B+SIw3PFYkvyR4FKo2oyZIHA==", "cpu": [ "x64" ], @@ -1231,9 +1231,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz", - "integrity": "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.56.0.tgz", + "integrity": "sha512-O16XcmyDeFI9879pEcmtWvD/2nyxR9mF7Gs44lf1vGGx8Vg2DRNx11aVXBEqOQhWb92WN4z7fW/q4+2NYzCbBA==", "cpu": [ "x64" ], @@ -1245,9 +1245,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz", - "integrity": "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.56.0.tgz", + "integrity": "sha512-LhN/Reh+7F3RCgQIRbgw8ZMwUwyqJM+8pXNT6IIJAqm2IdKkzpCh/V9EdgOMBKuebIrzswqy4ATlrDgiOwbRcQ==", "cpu": [ "arm64" ], @@ -1259,9 +1259,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz", - "integrity": "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.56.0.tgz", + "integrity": "sha512-kbFsOObXp3LBULg1d3JIUQMa9Kv4UitDmpS+k0tinPBz3watcUiV2/LUDMMucA6pZO3WGE27P7DsfaN54l9ing==", "cpu": [ "arm64" ], @@ -1273,9 +1273,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz", - "integrity": "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.56.0.tgz", + "integrity": "sha512-vSSgny54D6P4vf2izbtFm/TcWYedw7f8eBrOiGGecyHyQB9q4Kqentjaj8hToe+995nob/Wv48pDqL5a62EWtg==", "cpu": [ "ia32" ], @@ -1287,9 +1287,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz", - "integrity": "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.56.0.tgz", + "integrity": "sha512-FeCnkPCTHQJFbiGG49KjV5YGW/8b9rrXAM2Mz2kiIoktq2qsJxRD5giEMEOD2lPdgs72upzefaUvS+nc8E3UzQ==", "cpu": [ "x64" ], @@ -1301,9 +1301,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz", - "integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.56.0.tgz", + "integrity": "sha512-H8AE9Ur/t0+1VXujj90w0HrSOuv0Nq9r1vSZF2t5km20NTfosQsGGUXDaKdQZzwuLts7IyL1fYT4hM95TI9c4g==", "cpu": [ "x64" ], @@ -1314,6 +1314,13 @@ "win32" ] }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", @@ -1385,9 +1392,9 @@ "license": "MIT" }, "node_modules/@types/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-RDvF6wTulMPjrNdCoYRC8gNR880JNGT8uB+REUpC2Ns4pRqQJhGz90wh7rgdXDPpCczF3VGktDuFGVnz8zP7HA==", "dev": true, "license": "MIT" }, @@ -1399,14 +1406,14 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "22.19.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz", - "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "undici-types": "~6.21.0" + "undici-types": "~7.16.0" } }, "node_modules/@types/resolve": { @@ -1417,17 +1424,17 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.52.0.tgz", - "integrity": "sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.53.1.tgz", + "integrity": "sha512-cFYYFZ+oQFi6hUnBTbLRXfTJiaQtYE3t4O692agbBl+2Zy+eqSKWtPjhPXJu1G7j4RLjKgeJPDdq3EqOwmX5Ag==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.52.0", - "@typescript-eslint/type-utils": "8.52.0", - "@typescript-eslint/utils": "8.52.0", - "@typescript-eslint/visitor-keys": "8.52.0", + "@typescript-eslint/scope-manager": "8.53.1", + "@typescript-eslint/type-utils": "8.53.1", + "@typescript-eslint/utils": "8.53.1", + "@typescript-eslint/visitor-keys": "8.53.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" @@ -1440,23 +1447,23 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.52.0", + "@typescript-eslint/parser": "^8.53.1", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.52.0.tgz", - "integrity": "sha512-iIACsx8pxRnguSYhHiMn2PvhvfpopO9FXHyn1mG5txZIsAaB6F0KwbFnUQN3KCiG3Jcuad/Cao2FAs1Wp7vAyg==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.53.1.tgz", + "integrity": "sha512-nm3cvFN9SqZGXjmw5bZ6cGmvJSyJPn0wU9gHAZZHDnZl2wF9PhHv78Xf06E0MaNk4zLVHL8hb2/c32XvyJOLQg==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.52.0", - "@typescript-eslint/types": "8.52.0", - "@typescript-eslint/typescript-estree": "8.52.0", - "@typescript-eslint/visitor-keys": "8.52.0", + "@typescript-eslint/scope-manager": "8.53.1", + "@typescript-eslint/types": "8.53.1", + "@typescript-eslint/typescript-estree": "8.53.1", + "@typescript-eslint/visitor-keys": "8.53.1", "debug": "^4.4.3" }, "engines": { @@ -1472,14 +1479,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.52.0.tgz", - "integrity": "sha512-xD0MfdSdEmeFa3OmVqonHi+Cciab96ls1UhIF/qX/O/gPu5KXD0bY9lu33jj04fjzrXHcuvjBcBC+D3SNSadaw==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.53.1.tgz", + "integrity": "sha512-WYC4FB5Ra0xidsmlPb+1SsnaSKPmS3gsjIARwbEkHkoWloQmuzcfypljaJcR78uyLA1h8sHdWWPHSLDI+MtNog==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.52.0", - "@typescript-eslint/types": "^8.52.0", + "@typescript-eslint/tsconfig-utils": "^8.53.1", + "@typescript-eslint/types": "^8.53.1", "debug": "^4.4.3" }, "engines": { @@ -1494,14 +1501,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.52.0.tgz", - "integrity": "sha512-ixxqmmCcc1Nf8S0mS0TkJ/3LKcC8mruYJPOU6Ia2F/zUUR4pApW7LzrpU3JmtePbRUTes9bEqRc1Gg4iyRnDzA==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.53.1.tgz", + "integrity": "sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.52.0", - "@typescript-eslint/visitor-keys": "8.52.0" + "@typescript-eslint/types": "8.53.1", + "@typescript-eslint/visitor-keys": "8.53.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1512,9 +1519,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.52.0.tgz", - "integrity": "sha512-jl+8fzr/SdzdxWJznq5nvoI7qn2tNYV/ZBAEcaFMVXf+K6jmXvAFrgo/+5rxgnL152f//pDEAYAhhBAZGrVfwg==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.53.1.tgz", + "integrity": "sha512-qfvLXS6F6b1y43pnf0pPbXJ+YoXIC7HKg0UGZ27uMIemKMKA6XH2DTxsEDdpdN29D+vHV07x/pnlPNVLhdhWiA==", "dev": true, "license": "MIT", "engines": { @@ -1529,15 +1536,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.52.0.tgz", - "integrity": "sha512-JD3wKBRWglYRQkAtsyGz1AewDu3mTc7NtRjR/ceTyGoPqmdS5oCdx/oZMWD5Zuqmo6/MpsYs0wp6axNt88/2EQ==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.53.1.tgz", + "integrity": "sha512-MOrdtNvyhy0rHyv0ENzub1d4wQYKb2NmIqG7qEqPWFW7Mpy2jzFC3pQ2yKDvirZB7jypm5uGjF2Qqs6OIqu47w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.52.0", - "@typescript-eslint/typescript-estree": "8.52.0", - "@typescript-eslint/utils": "8.52.0", + "@typescript-eslint/types": "8.53.1", + "@typescript-eslint/typescript-estree": "8.53.1", + "@typescript-eslint/utils": "8.53.1", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, @@ -1554,9 +1561,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.52.0.tgz", - "integrity": "sha512-LWQV1V4q9V4cT4H5JCIx3481iIFxH1UkVk+ZkGGAV1ZGcjGI9IoFOfg3O6ywz8QqCDEp7Inlg6kovMofsNRaGg==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.1.tgz", + "integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==", "dev": true, "license": "MIT", "engines": { @@ -1568,16 +1575,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.52.0.tgz", - "integrity": "sha512-XP3LClsCc0FsTK5/frGjolyADTh3QmsLp6nKd476xNI9CsSsLnmn4f0jrzNoAulmxlmNIpeXuHYeEQv61Q6qeQ==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.1.tgz", + "integrity": "sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.52.0", - "@typescript-eslint/tsconfig-utils": "8.52.0", - "@typescript-eslint/types": "8.52.0", - "@typescript-eslint/visitor-keys": "8.52.0", + "@typescript-eslint/project-service": "8.53.1", + "@typescript-eslint/tsconfig-utils": "8.53.1", + "@typescript-eslint/types": "8.53.1", + "@typescript-eslint/visitor-keys": "8.53.1", "debug": "^4.4.3", "minimatch": "^9.0.5", "semver": "^7.7.3", @@ -1596,16 +1603,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.52.0.tgz", - "integrity": "sha512-wYndVMWkweqHpEpwPhwqE2lnD2DxC6WVLupU/DOt/0/v+/+iQbbzO3jOHjmBMnhu0DgLULvOaU4h4pwHYi2oRQ==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.53.1.tgz", + "integrity": "sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.52.0", - "@typescript-eslint/types": "8.52.0", - "@typescript-eslint/typescript-estree": "8.52.0" + "@typescript-eslint/scope-manager": "8.53.1", + "@typescript-eslint/types": "8.53.1", + "@typescript-eslint/typescript-estree": "8.53.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1620,13 +1627,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.52.0.tgz", - "integrity": "sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.1.tgz", + "integrity": "sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.52.0", + "@typescript-eslint/types": "8.53.1", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -1651,39 +1658,40 @@ } }, "node_modules/@vitest/expect": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", - "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", + "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==", "dev": true, "license": "MIT", "dependencies": { + "@standard-schema/spec": "^1.0.0", "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "tinyrainbow": "^2.0.0" + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", - "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz", + "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.2.4", + "@vitest/spy": "4.0.18", "estree-walker": "^3.0.3", - "magic-string": "^0.30.17" + "magic-string": "^0.30.21" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "msw": "^2.4.9", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + "vite": "^6.0.0 || ^7.0.0-0" }, "peerDependenciesMeta": { "msw": { @@ -1705,42 +1713,41 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", - "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", + "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^2.0.0" + "tinyrainbow": "^3.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", - "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz", + "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.2.4", - "pathe": "^2.0.3", - "strip-literal": "^3.0.0" + "@vitest/utils": "4.0.18", + "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", - "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", + "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.2.4", - "magic-string": "^0.30.17", + "@vitest/pretty-format": "4.0.18", + "magic-string": "^0.30.21", "pathe": "^2.0.3" }, "funding": { @@ -1748,28 +1755,24 @@ } }, "node_modules/@vitest/spy": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", - "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz", + "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==", "dev": true, "license": "MIT", - "dependencies": { - "tinyspy": "^4.0.3" - }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", - "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz", + "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.2.4", - "loupe": "^3.1.4", - "tinyrainbow": "^2.0.0" + "@vitest/pretty-format": "4.0.18", + "tinyrainbow": "^3.0.3" }, "funding": { "url": "https://opencollective.com/vitest" @@ -2101,16 +2104,6 @@ "ieee754": "^1.1.13" } }, - "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/cacache": { "version": "15.3.0", "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", @@ -2152,18 +2145,11 @@ } }, "node_modules/chai": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", - "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", - "dependencies": { - "assertion-error": "^2.0.1", - "check-error": "^2.1.1", - "deep-eql": "^5.0.1", - "loupe": "^3.1.0", - "pathval": "^2.0.0" - }, "engines": { "node": ">=18" } @@ -2185,16 +2171,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/check-error": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", - "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 16" - } - }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -2361,16 +2337,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/deep-eql": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", @@ -2414,9 +2380,9 @@ } }, "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -3635,9 +3601,9 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", "license": "MIT", "peer": true }, @@ -3648,13 +3614,6 @@ "dev": true, "license": "MIT" }, - "node_modules/loupe": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", - "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", - "dev": true, - "license": "MIT" - }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -4059,6 +4018,17 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, "node_modules/on-exit-leak-free": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", @@ -4210,16 +4180,6 @@ "dev": true, "license": "MIT" }, - "node_modules/pathval": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", - "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14.16" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -4273,9 +4233,9 @@ } }, "node_modules/pino-std-serializers": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", - "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", + "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", "license": "MIT" }, "node_modules/plimit-lit": { @@ -4616,9 +4576,9 @@ } }, "node_modules/rollup": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz", - "integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==", + "version": "4.56.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.56.0.tgz", + "integrity": "sha512-9FwVqlgUHzbXtDg9RCMgodF3Ua4Na6Gau+Sdt9vyCN4RhHfVKX2DCHy3BjMLTDd47ITDhYAnTwGulWTblJSDLg==", "dev": true, "license": "MIT", "peer": true, @@ -4633,31 +4593,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.55.1", - "@rollup/rollup-android-arm64": "4.55.1", - "@rollup/rollup-darwin-arm64": "4.55.1", - "@rollup/rollup-darwin-x64": "4.55.1", - "@rollup/rollup-freebsd-arm64": "4.55.1", - "@rollup/rollup-freebsd-x64": "4.55.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", - "@rollup/rollup-linux-arm-musleabihf": "4.55.1", - "@rollup/rollup-linux-arm64-gnu": "4.55.1", - "@rollup/rollup-linux-arm64-musl": "4.55.1", - "@rollup/rollup-linux-loong64-gnu": "4.55.1", - "@rollup/rollup-linux-loong64-musl": "4.55.1", - "@rollup/rollup-linux-ppc64-gnu": "4.55.1", - "@rollup/rollup-linux-ppc64-musl": "4.55.1", - "@rollup/rollup-linux-riscv64-gnu": "4.55.1", - "@rollup/rollup-linux-riscv64-musl": "4.55.1", - "@rollup/rollup-linux-s390x-gnu": "4.55.1", - "@rollup/rollup-linux-x64-gnu": "4.55.1", - "@rollup/rollup-linux-x64-musl": "4.55.1", - "@rollup/rollup-openbsd-x64": "4.55.1", - "@rollup/rollup-openharmony-arm64": "4.55.1", - "@rollup/rollup-win32-arm64-msvc": "4.55.1", - "@rollup/rollup-win32-ia32-msvc": "4.55.1", - "@rollup/rollup-win32-x64-gnu": "4.55.1", - "@rollup/rollup-win32-x64-msvc": "4.55.1", + "@rollup/rollup-android-arm-eabi": "4.56.0", + "@rollup/rollup-android-arm64": "4.56.0", + "@rollup/rollup-darwin-arm64": "4.56.0", + "@rollup/rollup-darwin-x64": "4.56.0", + "@rollup/rollup-freebsd-arm64": "4.56.0", + "@rollup/rollup-freebsd-x64": "4.56.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.56.0", + "@rollup/rollup-linux-arm-musleabihf": "4.56.0", + "@rollup/rollup-linux-arm64-gnu": "4.56.0", + "@rollup/rollup-linux-arm64-musl": "4.56.0", + "@rollup/rollup-linux-loong64-gnu": "4.56.0", + "@rollup/rollup-linux-loong64-musl": "4.56.0", + "@rollup/rollup-linux-ppc64-gnu": "4.56.0", + "@rollup/rollup-linux-ppc64-musl": "4.56.0", + "@rollup/rollup-linux-riscv64-gnu": "4.56.0", + "@rollup/rollup-linux-riscv64-musl": "4.56.0", + "@rollup/rollup-linux-s390x-gnu": "4.56.0", + "@rollup/rollup-linux-x64-gnu": "4.56.0", + "@rollup/rollup-linux-x64-musl": "4.56.0", + "@rollup/rollup-openbsd-x64": "4.56.0", + "@rollup/rollup-openharmony-arm64": "4.56.0", + "@rollup/rollup-win32-arm64-msvc": "4.56.0", + "@rollup/rollup-win32-ia32-msvc": "4.56.0", + "@rollup/rollup-win32-x64-gnu": "4.56.0", + "@rollup/rollup-win32-x64-msvc": "4.56.0", "fsevents": "~2.3.2" } }, @@ -5043,26 +5003,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strip-literal": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", - "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^9.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - } - }, - "node_modules/strip-literal/node_modules/js-tokens": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", - "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", - "dev": true, - "license": "MIT" - }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -5167,11 +5107,14 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/tinyglobby": { "version": "0.2.15", @@ -5190,30 +5133,10 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinypool": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", - "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.0.0 || >=20.0.0" - } - }, "node_modules/tinyrainbow": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tinyspy": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", - "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", "dev": true, "license": "MIT", "engines": { @@ -5418,16 +5341,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.52.0.tgz", - "integrity": "sha512-atlQQJ2YkO4pfTVQmQ+wvYQwexPDOIgo+RaVcD7gHgzy/IQA+XTyuxNM9M9TVXvttkF7koBHmcwisKdOAf2EcA==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.53.1.tgz", + "integrity": "sha512-gB+EVQfP5RDElh9ittfXlhZJdjSU4jUSTyE2+ia8CYyNvet4ElfaLlAIqDvQV9JPknKx0jQH1racTYe/4LaLSg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.52.0", - "@typescript-eslint/parser": "8.52.0", - "@typescript-eslint/typescript-estree": "8.52.0", - "@typescript-eslint/utils": "8.52.0" + "@typescript-eslint/eslint-plugin": "8.53.1", + "@typescript-eslint/parser": "8.53.1", + "@typescript-eslint/typescript-estree": "8.53.1", + "@typescript-eslint/utils": "8.53.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5442,9 +5365,9 @@ } }, "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "dev": true, "license": "MIT" }, @@ -5492,25 +5415,24 @@ "license": "MIT" }, "node_modules/vite": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", - "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -5519,14 +5441,14 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", - "less": "*", + "less": "^4.0.0", "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", + "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" @@ -5567,39 +5489,17 @@ } } }, - "node_modules/vite-node": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", - "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cac": "^6.7.14", - "debug": "^4.4.1", - "es-module-lexer": "^1.7.0", - "pathe": "^2.0.3", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" - }, - "bin": { - "vite-node": "vite-node.mjs" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, "node_modules/vite-tsconfig-paths": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", - "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==", + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-6.0.4.tgz", + "integrity": "sha512-iIsEJ+ek5KqRTK17pmxtgIxXtqr3qDdE6OxrP9mVeGhVDNXRJTKN/l9oMbujTQNzMLe6XZ8qmpztfbkPu2TiFQ==", "dev": true, "license": "MIT", "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", - "tsconfck": "^3.0.3" + "tsconfck": "^3.0.3", + "vite": "*" }, "peerDependencies": { "vite": "*" @@ -5610,536 +5510,51 @@ } } }, - "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/vite/node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" - } - }, "node_modules/vitest": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", - "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz", + "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", "dev": true, "license": "MIT", "dependencies": { - "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.4", - "@vitest/mocker": "3.2.4", - "@vitest/pretty-format": "^3.2.4", - "@vitest/runner": "3.2.4", - "@vitest/snapshot": "3.2.4", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", - "chai": "^5.2.0", - "debug": "^4.4.1", - "expect-type": "^1.2.1", - "magic-string": "^0.30.17", + "@vitest/expect": "4.0.18", + "@vitest/mocker": "4.0.18", + "@vitest/pretty-format": "4.0.18", + "@vitest/runner": "4.0.18", + "@vitest/snapshot": "4.0.18", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "obug": "^2.1.1", "pathe": "^2.0.3", - "picomatch": "^4.0.2", - "std-env": "^3.9.0", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.14", - "tinypool": "^1.1.1", - "tinyrainbow": "^2.0.0", - "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", - "vite-node": "3.2.4", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.4", - "@vitest/ui": "3.2.4", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.18", + "@vitest/browser-preview": "4.0.18", + "@vitest/browser-webdriverio": "4.0.18", + "@vitest/ui": "4.0.18", "happy-dom": "*", "jsdom": "*" }, @@ -6147,13 +5562,19 @@ "@edge-runtime/vm": { "optional": true }, - "@types/debug": { + "@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/ui": { diff --git a/package.json b/package.json index d99b13e..236b6f9 100644 --- a/package.json +++ b/package.json @@ -38,34 +38,34 @@ "author": "", "license": "ISC", "peerDependencies": { + "sqlite": "^5.1.1", + "sqlite3": "^5.1.7", "lodash": "^4.17.21", "luxon": "^3.6.1", - "pino": "^9.6.0", - "sqlite": "^5.1.1", - "sqlite3": "^5.1.7" + "pino": "^9.6.0" }, "devDependencies": { "@rollup/plugin-json": "^6.1.0", - "@rollup/plugin-node-resolve": "^16.0.1", - "@rollup/plugin-typescript": "^12.1.2", - "@types/lodash": "^4.17.13", - "@types/luxon": "^3.6.2", - "@types/node": "^22.15.12", - "@typescript-eslint/eslint-plugin": "^8.32.1", - "@typescript-eslint/parser": "^8.32.1", - "eslint": "^9.27.0", + "@rollup/plugin-node-resolve": "^16.0.3", + "@rollup/plugin-typescript": "^12.3.0", + "@types/lodash": "^4.17.23", + "@types/luxon": "^3.7.1", + "@types/node": "^25.0.10", + "@typescript-eslint/eslint-plugin": "^8.53.1", + "@typescript-eslint/parser": "^8.53.1", + "eslint": "^9.39.2", "eslint-plugin-node": "^11.1.0", - "rollup": "^4.40.2", - "rollup-plugin-dts": "^6.2.3", + "rollup": "^4.56.0", + "rollup-plugin-dts": "^6.3.0", "ts-node": "^10.9.2", "tsc-alias": "^1.8.16", "tsconfig-paths": "^4.2.0", "tslib": "^2.8.1", - "tsx": "^4.19.4", + "tsx": "^4.21.0", "typescript": "^5.9.3", - "typescript-eslint": "^8.32.1", - "vite": "^6.2.5", - "vite-tsconfig-paths": "^5.1.4", - "vitest": "^3.1.1" + "typescript-eslint": "^8.53.1", + "vite": "^7.3.1", + "vite-tsconfig-paths": "^6.0.4", + "vitest": "^4.0.18" } } From 3333c23eaa830fc73f7bb2f40e7ab23a17d4c4c3 Mon Sep 17 00:00:00 2001 From: Lukas Date: Fri, 23 Jan 2026 22:24:20 +0100 Subject: [PATCH 05/20] refactor(errors): move error classes to models.errors and update imports feat(auth): add session and logout fetch handlers feat(auth): implement OpenID Connect server-side context --- src/globals/coding/code.dates.ts | 2 +- src/index.node.ts | 2 +- src/node/api/errors.api.ts | 24 ----- src/node/api/models.errors.ts | 42 ++++++++ src/node/api/parse.arrows.json.ts | 2 +- src/node/api/parse.changeNodes.ts | 2 +- src/node/api/parse.changesEdges.ts | 2 +- src/node/api/parsing.errors.ts | 28 ++++++ src/node/api/validations.shared.ts | 2 +- src/node/auth/handlers.callbackFetch.ts | 26 +++++ src/node/auth/handlers.logoutFetch.ts | 43 +++++++++ src/node/auth/handlers.sessionFetch.ts | 80 ++++++++++++++++ src/node/auth/openid.ssr.ts | 121 ++++++++++++++++++++++++ 13 files changed, 346 insertions(+), 30 deletions(-) delete mode 100644 src/node/api/errors.api.ts create mode 100644 src/node/api/models.errors.ts create mode 100644 src/node/api/parsing.errors.ts create mode 100644 src/node/auth/handlers.callbackFetch.ts create mode 100644 src/node/auth/handlers.logoutFetch.ts create mode 100644 src/node/auth/handlers.sessionFetch.ts create mode 100644 src/node/auth/openid.ssr.ts diff --git a/src/globals/coding/code.dates.ts b/src/globals/coding/code.dates.ts index ee2148b..24e7d7a 100644 --- a/src/globals/coding/code.dates.ts +++ b/src/globals/coding/code.dates.ts @@ -1,5 +1,5 @@ import { DateTime } from "luxon" -import { InvalidRequestError } from "../../node/api/errors.api" +import { InvalidRequestError } from "../../node/api/models.errors" /** * Return epoch timestamp diff --git a/src/index.node.ts b/src/index.node.ts index 70e28b5..8998db9 100644 --- a/src/index.node.ts +++ b/src/index.node.ts @@ -2,7 +2,7 @@ export * from "./index.browser.js"; // API error handling -export { messageFromError, InvalidRequestError, AuthorizationError } from "./node/api/errors.api.js"; +export { InvalidRequestError, AuthorizationError, ServerError, SSROnlyError } from "./node/api/models.errors.js"; // API models and helpers export { type ApiEndpointResponse } from "./node/api/models.api.js"; diff --git a/src/node/api/errors.api.ts b/src/node/api/errors.api.ts deleted file mode 100644 index 8e2716d..0000000 --- a/src/node/api/errors.api.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Extract message from exception error (try-catch) - * @param error error from catch block as any - * @returns - */ - export const messageFromError = (error: any) => error["message"] as string - -/** - * We miss a API parameter needed to process action - */ -export class InvalidRequestError extends Error{ - constructor(message: string){ - super(`Invalid Request: ${message}`) - } -} - -/** - * Where client has general authorization issue - */ -export class AuthorizationError extends Error{ - constructor(){ - super(`Authorization has failed.`) - } -} \ No newline at end of file diff --git a/src/node/api/models.errors.ts b/src/node/api/models.errors.ts new file mode 100644 index 0000000..b389e3e --- /dev/null +++ b/src/node/api/models.errors.ts @@ -0,0 +1,42 @@ +/** + * We miss a API parameter needed to process action + */ +class InvalidRequestError extends Error{ + constructor(message: string){ + super(`Invalid Request: ${message}`) + } +} + +/** + * Where client has general authorization issue + */ +class AuthorizationError extends Error{ + constructor(){ + super(`Authorization has failed.`) + } +} + +/** + * General backend server side error + */ +class ServerError extends Error{ + constructor(message: string){ + super(`Server Error: ${message}`) + } +} + +/** + * Error to indicate that some functionality is SSR only + */ +class SSROnlyError extends Error{ + constructor(message: string){ + super(`SSR Only Error: ${message}`) + } +} + +export { + InvalidRequestError, + AuthorizationError, + ServerError, + SSROnlyError +} \ No newline at end of file diff --git a/src/node/api/parse.arrows.json.ts b/src/node/api/parse.arrows.json.ts index 2ba9073..b2538b3 100644 --- a/src/node/api/parse.arrows.json.ts +++ b/src/node/api/parse.arrows.json.ts @@ -5,7 +5,7 @@ import { parseSinglePantherNode } from "./parse.changeNodes" import { GraphEdge, GraphRelation } from "../../globals/panther/models.edges" import { UsedEdgeLabels } from "../../globals/panther/enums.panther" import { isInEnum } from "../../globals/coding/code.formating" -import { InvalidRequestError } from "./errors.api" +import { InvalidRequestError } from "./models.errors" import { FullPantherEntity, PantherEntity } from "../../globals/panther/models.nodes" /** diff --git a/src/node/api/parse.changeNodes.ts b/src/node/api/parse.changeNodes.ts index 9e10447..0b45d57 100644 --- a/src/node/api/parse.changeNodes.ts +++ b/src/node/api/parse.changeNodes.ts @@ -3,7 +3,7 @@ import _ from "lodash" import { Unsure } from "../../globals/coding/code.types" import { FullPantherEntity, PantherEntity } from "../../globals/panther/models.nodes" import { HasConfiguration, HasGeometry, HasInterval, HasLevels, HasUnits } from "../../globals/panther/models.nodes.properties.general" -import { InvalidRequestError } from "./errors.api" +import { InvalidRequestError } from "./models.errors" import { HasBands, HasColor, HasDocumentId, HasSpecificName, HasTimeseries, HasUrl } from "../../globals/panther/models.nodes.properties.datasources" import { UsedDatasourceLabels, UsedNodeLabels } from "../../globals/panther/enums.panther" import { validateNodeLabels } from "./validations.shared" diff --git a/src/node/api/parse.changesEdges.ts b/src/node/api/parse.changesEdges.ts index e93916d..566a86d 100644 --- a/src/node/api/parse.changesEdges.ts +++ b/src/node/api/parse.changesEdges.ts @@ -1,5 +1,5 @@ import _ from "lodash" -import { InvalidRequestError } from "./errors.api" +import { InvalidRequestError } from "./models.errors" import { GraphEdge, GraphRelation } from "../../globals/panther/models.edges" import { enumValuesToString, isInEnum } from "../../globals/coding/code.formating" import { UsedEdgeLabels } from "../../globals/panther/enums.panther" diff --git a/src/node/api/parsing.errors.ts b/src/node/api/parsing.errors.ts new file mode 100644 index 0000000..102c4c3 --- /dev/null +++ b/src/node/api/parsing.errors.ts @@ -0,0 +1,28 @@ +import { AuthorizationError, InvalidRequestError } from "./models.errors" + +export const handleRouteError = (error: unknown) => { + + // lets work with the error as an object + const processedError = error as any; + + // need to know the type of error to handle it properly + const errorType = processedError.constructor; + + // switch on the error type as we know how to handle our errors + switch (errorType) { + case InvalidRequestError: + return { message: processedError.message, status: 400 }; + case AuthorizationError: + return { message: processedError.message, status: 401 }; + default: + return { message: processedError.message, status: 500 }; // default status code for server errors + } +}; + + +/** + * Extract message from exception error (try-catch) + * @param error error from catch block as any + * @returns + */ + export const messageFromError = (error: any) => error["message"] as string diff --git a/src/node/api/validations.shared.ts b/src/node/api/validations.shared.ts index 14f4994..febc05d 100644 --- a/src/node/api/validations.shared.ts +++ b/src/node/api/validations.shared.ts @@ -1,6 +1,6 @@ import _ from "lodash" import { enumCombineValuesToString, isInEnum } from "../../globals/coding/code.formating" -import { InvalidRequestError } from "./errors.api" +import { InvalidRequestError } from "./models.errors" import { UsedDatasourceLabels, UsedEdgeLabels, UsedNodeLabels } from "../../globals/panther/enums.panther" diff --git a/src/node/auth/handlers.callbackFetch.ts b/src/node/auth/handlers.callbackFetch.ts new file mode 100644 index 0000000..6aaf3da --- /dev/null +++ b/src/node/auth/handlers.callbackFetch.ts @@ -0,0 +1,26 @@ +/** + * Fetch from API handler to backend service with value included in cookies + * @param endpointUrl Endpoint URL + * @param body Body for POST request + * @param method Optional - method for fetch request + * @returns Cookie header from response + */ +export const fetchForCookies = async (endpointUrl: string, body: unknown, method = 'POST') => { + // execute fetch request + const response = await fetch(endpointUrl as string, { + method, + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }); + + // get session cookie from response + const setCookieHeader = response.headers.get('set-cookie'); + + // check for cookie header + if (!setCookieHeader) throw new Error('Missing session cookie in response'); + + // return cookie header + return setCookieHeader; +}; diff --git a/src/node/auth/handlers.logoutFetch.ts b/src/node/auth/handlers.logoutFetch.ts new file mode 100644 index 0000000..dd48c62 --- /dev/null +++ b/src/node/auth/handlers.logoutFetch.ts @@ -0,0 +1,43 @@ +interface LogoutFetchProps { + identityServiceUrl: string; + browserCookies: any; +} + +/** + * Fetch from API handler to backend service with session ID included in cookies + * @param url URL of target endpoint + * @param browserCookies Cookies from browser request (SPA part of Next) + * @param headers Optional - any headers added to request + * @returns Response from backend back to Next API route handler + */ +export const fetchLogoutNotification = async (props: LogoutFetchProps): Promise => { + const { browserCookies, identityServiceUrl } = props; + + // get session cookie from browser + const sessionCookie = (browserCookies as any).get('sid'); + + if (!sessionCookie) console.warn('Logout: Missing SID from browser'); + + //prepare logout URL + const logoutUrl = `${identityServiceUrl}/oid/logout`; + + // prepare body for session exchange + const body = { + sid: sessionCookie, + }; + + // make notify POST request to backend for session exchange + try { + const response = await fetch(logoutUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(body), + }); + + if (!response.ok) console.warn('Logout: Backend logout failed'); + } catch (error: any) { + console.warn(`Logout: Backend logout failed: ${error['message']}`); + } +}; diff --git a/src/node/auth/handlers.sessionFetch.ts b/src/node/auth/handlers.sessionFetch.ts new file mode 100644 index 0000000..c32ef45 --- /dev/null +++ b/src/node/auth/handlers.sessionFetch.ts @@ -0,0 +1,80 @@ +import { Nullable } from '@gisatcz/ptr-be-core/browser'; +import { ErrorBehavior } from '../../../globals/shared/errors/enums.errorBehavior'; +import { HttpStatusCode } from '../../../globals/shared/errors/enums.httpStatusCode'; +import { BaseHttpError } from '../../../globals/shared/errors/models.error'; + +// Define the interface for the properties of the fetchWithSessions function +interface FetchWithBrowserSessionProps { + method: 'GET' | 'POST'; + url: string; + browserCookies: any; + body?: object; + headers?: any; + requireSessionId: boolean; +} + +// Define the interface for the response of the fetchWithSessions function +interface FetchWithSessionsResponse { + status: number; + backendContent: Nullable; + setCookieHeader: Nullable; +} + +/** + * Fetch from API handler to backend service with session ID included in cookies + * @param url URL of target endpoint + * @param browserCookies Cookies from browser request (SPA part of Next) + * @param headers Optional - any headers added to request + * @returns Response from backend back to Next API route handler + */ +export const fetchWithSessions = async (props: FetchWithBrowserSessionProps): Promise => { + const { url, browserCookies, method, body, headers } = props; + + // Check if the URL is provided + if (!url) + throw new BaseHttpError('Missing URL for session fetch', HttpStatusCode.INTERNAL_SERVER_ERROR, ErrorBehavior.SSR); + + // Get the session cookie from the browser cookies + const sessionCookie = (browserCookies as any).get('sid'); + + // Check if the session cookie is required but missing + if (!sessionCookie && props.requireSessionId) + throw new BaseHttpError('Missing session from browser', HttpStatusCode.UNAUTHORIZED, ErrorBehavior.BE); + + // Add the session cookie to the headers if it exists + const headersWithCookies = sessionCookie + ? { ...headers, Cookie: `${sessionCookie.name}=${sessionCookie.value}` } + : { ...headers }; + + // Make the fetch request to the backend + const response = await fetch(url, { + method, + body: body ? JSON.stringify(body) : undefined, + headers: headersWithCookies, + }); + + // Parse the response from the backend + const backendContent = await response.json(); + + // Check if the response is successful + if (response.ok) { + const setCookieHeader = response.headers.get('set-cookie'); + + // Check if the set-cookie header is missing when a session ID is required + if (!setCookieHeader && props.requireSessionId) { + console.error('Sessions Fetch: Missing cookies with SID'); + + throw new BaseHttpError( + 'Fetch response with new session cookie missing', + HttpStatusCode.INTERNAL_SERVER_ERROR, + ErrorBehavior.BE + ); + } + + // Return the response status, content, and set-cookie header + return { status: response.status, backendContent, setCookieHeader }; + } else { + console.error('Error in fetchWithSessions', response.status, backendContent); + throw new BaseHttpError(backendContent, response.status, ErrorBehavior.BE); + } +}; diff --git a/src/node/auth/openid.ssr.ts b/src/node/auth/openid.ssr.ts new file mode 100644 index 0000000..c9a1493 --- /dev/null +++ b/src/node/auth/openid.ssr.ts @@ -0,0 +1,121 @@ +import { Issuer } from 'openid-client'; +import { SSROnlyError } from '../../index.node'; +import { Unsure } from '../../index.browser'; + + +/** + * OpenID Connect functional context (as server side JS closure) + * @returns Set of functions to handle OpenID (OAuth2) operations using official openid library with server side environments + */ +export const ssrOpenidContext = (clientId: string, issuerUrl: string, redirectUrl: string) => { + + /** + * Read server side environment values and validate them + * @returns + */ + const checkContextEnvironmens = () => { + if (!issuerUrl) + throw new SSROnlyError('Missing OID issuer URL'); + + if (!clientId) + throw new SSROnlyError('Missing OID client ID'); + + if (!redirectUrl) + throw new SSROnlyError('Missing OID redirect url back to this app (OAuth2 callback path)'); + return { + issuerUrl, + clientId, + redirectUrl, + }; + }; + + /** + * Creates the active client for OpenID operations + * @returns OpenID set up client ready to be used + */ + async function oidSetupClient() { + // check environments + const oidEnvironments = checkContextEnvironmens(); + + // prepare Open ID Issuer client + const oidIssuer = await Issuer.discover(oidEnvironments.issuerUrl); + const oidClient = new oidIssuer.Client({ + client_id: oidEnvironments.clientId, + redirect_uris: [oidEnvironments.redirectUrl], + response_types: ['code'], + token_endpoint_auth_method: 'none', + }); + + // return client + return oidClient; + } + + /** + * Exported handler for authorisation process usinf OpenID Connect code flow + */ + async function handleInternalKeycloak() { + const oidClient = await oidSetupClient(); + const url = oidClient.authorizationUrl({ + scope: 'openid email profile', + }); + return url; + } + + async function handleLogout(tokenExchangeUrl: Unsure) { + if (!tokenExchangeUrl) + throw new SSROnlyError('Missing URL for exchange'); + + return { tokenExchangeUrl }; + } + + /** + * Obtain tokens from redirect by sending OAuth2 code back to issuer + * @param params Use NextRequest + * @param urlOrigin Origin of the URL from callback route + * @returns Tokens from the IAM + */ + async function handleAuthCallback(params: any, tokenExchangeUrl: Unsure) { + // initialize Open ID client + const oidClient = await oidSetupClient(); + + // prepare OID callback params for provider + const callbackParams = oidClient.callbackParams(params); + + // OID Provider response with tokens + const tokens = await oidClient.callback(checkContextEnvironmens().redirectUrl, callbackParams); + + // do we have tokens and all values? + if (!tokens.access_token) + throw new SSROnlyError('Missing OID information'); + if (!tokens.refresh_token) + throw new SSROnlyError('Missing OID information'); + if (!tokens.id_token) + throw new SSROnlyError('Missing OID information'); + if (!tokens.refresh_token) + throw new SSROnlyError('Missing OID information'); + if (!clientId) + throw new SSROnlyError('Missing client ID'); + if (!tokenExchangeUrl) + throw new SSROnlyError('Missing URL for excgange'); + if (!issuerUrl) + throw new SSROnlyError('Missing issuer URL'); + + // prepare output for route + return { + tokens: { + access_token: tokens.access_token, + refresh_token: tokens.refresh_token, + id_token: tokens.id_token, + }, + clientId, + issuerUrl, + tokenExchangeUrl, + }; + } + + return { + handleInternalKeycloak, + handleAuthCallback, + handleLogout, + }; +} From 8f8d84807b181ebb4b519df35a07f6a0cada23b2 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 24 Jan 2026 11:17:25 +0100 Subject: [PATCH 06/20] feat(dependencies): add openid-client as a peer dependency --- package-lock.json | 33 +++++++++++++++++++++++++++++++++ package.json | 1 + 2 files changed, 34 insertions(+) diff --git a/package-lock.json b/package-lock.json index 51fc9c7..bca8012 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,6 +35,7 @@ "peerDependencies": { "lodash": "^4.17.21", "luxon": "^3.6.1", + "openid-client": "^6.8.1", "pino": "^9.6.0", "sqlite": "^5.1.1", "sqlite3": "^5.1.7" @@ -3505,6 +3506,15 @@ "devOptional": true, "license": "ISC" }, + "node_modules/jose": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.1.3.tgz", + "integrity": "sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -4018,6 +4028,15 @@ "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, + "node_modules/oauth4webapi": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.3.tgz", + "integrity": "sha512-pQ5BsX3QRTgnt5HxgHwgunIRaDXBdkT23tf8dfzmtTIL2LTpdmxgbpbBm0VgFWAIDlezQvQCTgnVIUmHupXHxw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/obug": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", @@ -4047,6 +4066,20 @@ "wrappy": "1" } }, + "node_modules/openid-client": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-6.8.1.tgz", + "integrity": "sha512-VoYT6enBo6Vj2j3Q5Ec0AezS+9YGzQo1f5Xc42lreMGlfP4ljiXPKVDvCADh+XHCV/bqPu/wWSiCVXbJKvrODw==", + "license": "MIT", + "peer": true, + "dependencies": { + "jose": "^6.1.0", + "oauth4webapi": "^3.8.2" + }, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", diff --git a/package.json b/package.json index 236b6f9..ef95c59 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "author": "", "license": "ISC", "peerDependencies": { + "openid-client": "^6.8.1", "sqlite": "^5.1.1", "sqlite3": "^5.1.7", "lodash": "^4.17.21", From e80c9714b8bb28017201985d36c71c3f3e8bb0b4 Mon Sep 17 00:00:00 2001 From: Lukas Date: Thu, 19 Feb 2026 16:36:25 +0100 Subject: [PATCH 07/20] feat(auth): implement OpenID Connect functionality and update handlers --- .gitignore | 41 +++++++- src/index.node.ts | 9 +- src/node/auth/handlers.callbackFetch.ts | 46 ++++---- src/node/auth/handlers.logoutFetch.ts | 72 ++++++------- src/node/auth/handlers.sessionFetch.ts | 134 ++++++++++++------------ src/node/auth/openid.ssr.ts | 133 +++++++++++------------ 6 files changed, 240 insertions(+), 195 deletions(-) diff --git a/.gitignore b/.gitignore index 1574346..fd7844f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,38 @@ -node_modules -dist -.rollup.cache \ No newline at end of file +.DS_Store +Thumbs.db + +# Node +node_modules/ +npm-debug.log +yarn-error.log +package-lock.json +yarn.lock +pnpm-lock.yaml +yalc.lock +.yalc/ + +# TypeScript / build output +dist/ +build/ +*.tsbuildinfo + +# Environment files +.env +.env.local +.env.* + +# Logs +logs +*.log +.nyc_output +coverage/ + +# IDE / devcontainer +.vscode/ +.idea/ +.devcontainer/ + +# Misc +*.nogit.* +CHANGELOG.md +CONTRIBUTING.md \ No newline at end of file diff --git a/src/index.node.ts b/src/index.node.ts index 8998db9..32b17eb 100644 --- a/src/index.node.ts +++ b/src/index.node.ts @@ -1,3 +1,4 @@ + // Re-export browser-specific exports export * from "./index.browser.js"; @@ -72,4 +73,10 @@ export { // SQLite queries for app state retrieval export { dbNeedAppState -} from "./node/sqlite/sqlite.queries.js"; \ No newline at end of file +} from "./node/sqlite/sqlite.queries.js"; + + +// OpenID Connect functionality for server-side rendering apps (FE API routes) +export { + ssrOpenidContext +} from "./node/auth/openid.ssr.js"; \ No newline at end of file diff --git a/src/node/auth/handlers.callbackFetch.ts b/src/node/auth/handlers.callbackFetch.ts index 6aaf3da..0fd523a 100644 --- a/src/node/auth/handlers.callbackFetch.ts +++ b/src/node/auth/handlers.callbackFetch.ts @@ -1,26 +1,26 @@ -/** - * Fetch from API handler to backend service with value included in cookies - * @param endpointUrl Endpoint URL - * @param body Body for POST request - * @param method Optional - method for fetch request - * @returns Cookie header from response - */ -export const fetchForCookies = async (endpointUrl: string, body: unknown, method = 'POST') => { - // execute fetch request - const response = await fetch(endpointUrl as string, { - method, - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(body), - }); +// /** +// * Fetch from API handler to backend service with value included in cookies +// * @param endpointUrl Endpoint URL +// * @param body Body for POST request +// * @param method Optional - method for fetch request +// * @returns Cookie header from response +// */ +// export const fetchForCookies = async (endpointUrl: string, body: unknown, method = 'POST') => { +// // execute fetch request +// const response = await fetch(endpointUrl as string, { +// method, +// headers: { +// 'Content-Type': 'application/json', +// }, +// body: JSON.stringify(body), +// }); - // get session cookie from response - const setCookieHeader = response.headers.get('set-cookie'); +// // get session cookie from response +// const setCookieHeader = response.headers.get('set-cookie'); - // check for cookie header - if (!setCookieHeader) throw new Error('Missing session cookie in response'); +// // check for cookie header +// if (!setCookieHeader) throw new Error('Missing session cookie in response'); - // return cookie header - return setCookieHeader; -}; +// // return cookie header +// return setCookieHeader; +// }; diff --git a/src/node/auth/handlers.logoutFetch.ts b/src/node/auth/handlers.logoutFetch.ts index dd48c62..f489431 100644 --- a/src/node/auth/handlers.logoutFetch.ts +++ b/src/node/auth/handlers.logoutFetch.ts @@ -1,43 +1,43 @@ -interface LogoutFetchProps { - identityServiceUrl: string; - browserCookies: any; -} +// interface LogoutFetchProps { +// identityServiceUrl: string; +// browserCookies: any; +// } -/** - * Fetch from API handler to backend service with session ID included in cookies - * @param url URL of target endpoint - * @param browserCookies Cookies from browser request (SPA part of Next) - * @param headers Optional - any headers added to request - * @returns Response from backend back to Next API route handler - */ -export const fetchLogoutNotification = async (props: LogoutFetchProps): Promise => { - const { browserCookies, identityServiceUrl } = props; +// /** +// * Fetch from API handler to backend service with session ID included in cookies +// * @param url URL of target endpoint +// * @param browserCookies Cookies from browser request (SPA part of Next) +// * @param headers Optional - any headers added to request +// * @returns Response from backend back to Next API route handler +// */ +// export const fetchLogoutNotification = async (props: LogoutFetchProps): Promise => { +// const { browserCookies, identityServiceUrl } = props; - // get session cookie from browser - const sessionCookie = (browserCookies as any).get('sid'); +// // get session cookie from browser +// const sessionCookie = (browserCookies as any).get('sid'); - if (!sessionCookie) console.warn('Logout: Missing SID from browser'); +// if (!sessionCookie) console.warn('Logout: Missing SID from browser'); - //prepare logout URL - const logoutUrl = `${identityServiceUrl}/oid/logout`; +// //prepare logout URL +// const logoutUrl = `${identityServiceUrl}/oid/logout`; - // prepare body for session exchange - const body = { - sid: sessionCookie, - }; +// // prepare body for session exchange +// const body = { +// sid: sessionCookie, +// }; - // make notify POST request to backend for session exchange - try { - const response = await fetch(logoutUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(body), - }); +// // make notify POST request to backend for session exchange +// try { +// const response = await fetch(logoutUrl, { +// method: 'POST', +// headers: { +// 'Content-Type': 'application/json', +// }, +// body: JSON.stringify(body), +// }); - if (!response.ok) console.warn('Logout: Backend logout failed'); - } catch (error: any) { - console.warn(`Logout: Backend logout failed: ${error['message']}`); - } -}; +// if (!response.ok) console.warn('Logout: Backend logout failed'); +// } catch (error: any) { +// console.warn(`Logout: Backend logout failed: ${error['message']}`); +// } +// }; diff --git a/src/node/auth/handlers.sessionFetch.ts b/src/node/auth/handlers.sessionFetch.ts index c32ef45..e1f5836 100644 --- a/src/node/auth/handlers.sessionFetch.ts +++ b/src/node/auth/handlers.sessionFetch.ts @@ -1,80 +1,80 @@ -import { Nullable } from '@gisatcz/ptr-be-core/browser'; -import { ErrorBehavior } from '../../../globals/shared/errors/enums.errorBehavior'; -import { HttpStatusCode } from '../../../globals/shared/errors/enums.httpStatusCode'; -import { BaseHttpError } from '../../../globals/shared/errors/models.error'; +// import { Nullable } from '@gisatcz/ptr-be-core/browser'; +// import { ErrorBehavior } from '../../../globals/shared/errors/enums.errorBehavior'; +// import { HttpStatusCode } from '../../../globals/shared/errors/enums.httpStatusCode'; +// import { BaseHttpError } from '../../../globals/shared/errors/models.error'; -// Define the interface for the properties of the fetchWithSessions function -interface FetchWithBrowserSessionProps { - method: 'GET' | 'POST'; - url: string; - browserCookies: any; - body?: object; - headers?: any; - requireSessionId: boolean; -} +// // Define the interface for the properties of the fetchWithSessions function +// interface FetchWithBrowserSessionProps { +// method: 'GET' | 'POST'; +// url: string; +// browserCookies: any; +// body?: object; +// headers?: any; +// requireSessionId: boolean; +// } -// Define the interface for the response of the fetchWithSessions function -interface FetchWithSessionsResponse { - status: number; - backendContent: Nullable; - setCookieHeader: Nullable; -} +// // Define the interface for the response of the fetchWithSessions function +// interface FetchWithSessionsResponse { +// status: number; +// backendContent: Nullable; +// setCookieHeader: Nullable; +// } -/** - * Fetch from API handler to backend service with session ID included in cookies - * @param url URL of target endpoint - * @param browserCookies Cookies from browser request (SPA part of Next) - * @param headers Optional - any headers added to request - * @returns Response from backend back to Next API route handler - */ -export const fetchWithSessions = async (props: FetchWithBrowserSessionProps): Promise => { - const { url, browserCookies, method, body, headers } = props; +// /** +// * Fetch from API handler to backend service with session ID included in cookies +// * @param url URL of target endpoint +// * @param browserCookies Cookies from browser request (SPA part of Next) +// * @param headers Optional - any headers added to request +// * @returns Response from backend back to Next API route handler +// */ +// export const fetchWithSessions = async (props: FetchWithBrowserSessionProps): Promise => { +// const { url, browserCookies, method, body, headers } = props; - // Check if the URL is provided - if (!url) - throw new BaseHttpError('Missing URL for session fetch', HttpStatusCode.INTERNAL_SERVER_ERROR, ErrorBehavior.SSR); +// // Check if the URL is provided +// if (!url) +// throw new BaseHttpError('Missing URL for session fetch', HttpStatusCode.INTERNAL_SERVER_ERROR, ErrorBehavior.SSR); - // Get the session cookie from the browser cookies - const sessionCookie = (browserCookies as any).get('sid'); +// // Get the session cookie from the browser cookies +// const sessionCookie = (browserCookies as any).get('sid'); - // Check if the session cookie is required but missing - if (!sessionCookie && props.requireSessionId) - throw new BaseHttpError('Missing session from browser', HttpStatusCode.UNAUTHORIZED, ErrorBehavior.BE); +// // Check if the session cookie is required but missing +// if (!sessionCookie && props.requireSessionId) +// throw new BaseHttpError('Missing session from browser', HttpStatusCode.UNAUTHORIZED, ErrorBehavior.BE); - // Add the session cookie to the headers if it exists - const headersWithCookies = sessionCookie - ? { ...headers, Cookie: `${sessionCookie.name}=${sessionCookie.value}` } - : { ...headers }; +// // Add the session cookie to the headers if it exists +// const headersWithCookies = sessionCookie +// ? { ...headers, Cookie: `${sessionCookie.name}=${sessionCookie.value}` } +// : { ...headers }; - // Make the fetch request to the backend - const response = await fetch(url, { - method, - body: body ? JSON.stringify(body) : undefined, - headers: headersWithCookies, - }); +// // Make the fetch request to the backend +// const response = await fetch(url, { +// method, +// body: body ? JSON.stringify(body) : undefined, +// headers: headersWithCookies, +// }); - // Parse the response from the backend - const backendContent = await response.json(); +// // Parse the response from the backend +// const backendContent = await response.json(); - // Check if the response is successful - if (response.ok) { - const setCookieHeader = response.headers.get('set-cookie'); +// // Check if the response is successful +// if (response.ok) { +// const setCookieHeader = response.headers.get('set-cookie'); - // Check if the set-cookie header is missing when a session ID is required - if (!setCookieHeader && props.requireSessionId) { - console.error('Sessions Fetch: Missing cookies with SID'); +// // Check if the set-cookie header is missing when a session ID is required +// if (!setCookieHeader && props.requireSessionId) { +// console.error('Sessions Fetch: Missing cookies with SID'); - throw new BaseHttpError( - 'Fetch response with new session cookie missing', - HttpStatusCode.INTERNAL_SERVER_ERROR, - ErrorBehavior.BE - ); - } +// throw new BaseHttpError( +// 'Fetch response with new session cookie missing', +// HttpStatusCode.INTERNAL_SERVER_ERROR, +// ErrorBehavior.BE +// ); +// } - // Return the response status, content, and set-cookie header - return { status: response.status, backendContent, setCookieHeader }; - } else { - console.error('Error in fetchWithSessions', response.status, backendContent); - throw new BaseHttpError(backendContent, response.status, ErrorBehavior.BE); - } -}; +// // Return the response status, content, and set-cookie header +// return { status: response.status, backendContent, setCookieHeader }; +// } else { +// console.error('Error in fetchWithSessions', response.status, backendContent); +// throw new BaseHttpError(backendContent, response.status, ErrorBehavior.BE); +// } +// }; diff --git a/src/node/auth/openid.ssr.ts b/src/node/auth/openid.ssr.ts index c9a1493..f8a8055 100644 --- a/src/node/auth/openid.ssr.ts +++ b/src/node/auth/openid.ssr.ts @@ -1,6 +1,7 @@ -import { Issuer } from 'openid-client'; -import { SSROnlyError } from '../../index.node'; +import * as openid from 'openid-client'; import { Unsure } from '../../index.browser'; +import { SSROnlyError } from '../api/models.errors'; +import { messageFromError } from '../api/parsing.errors'; /** @@ -8,81 +9,76 @@ import { Unsure } from '../../index.browser'; * @returns Set of functions to handle OpenID (OAuth2) operations using official openid library with server side environments */ export const ssrOpenidContext = (clientId: string, issuerUrl: string, redirectUrl: string) => { - - /** - * Read server side environment values and validate them - * @returns - */ - const checkContextEnvironmens = () => { - if (!issuerUrl) - throw new SSROnlyError('Missing OID issuer URL'); - - if (!clientId) - throw new SSROnlyError('Missing OID client ID'); - - if (!redirectUrl) - throw new SSROnlyError('Missing OID redirect url back to this app (OAuth2 callback path)'); - return { - issuerUrl, - clientId, - redirectUrl, - }; - }; /** * Creates the active client for OpenID operations * @returns OpenID set up client ready to be used */ - async function oidSetupClient() { - // check environments - const oidEnvironments = checkContextEnvironmens(); - - // prepare Open ID Issuer client - const oidIssuer = await Issuer.discover(oidEnvironments.issuerUrl); - const oidClient = new oidIssuer.Client({ - client_id: oidEnvironments.clientId, - redirect_uris: [oidEnvironments.redirectUrl], - response_types: ['code'], - token_endpoint_auth_method: 'none', - }); - - // return client - return oidClient; - } + async function oidSetupIssuerConfiguration() { + try { - /** - * Exported handler for authorisation process usinf OpenID Connect code flow - */ - async function handleInternalKeycloak() { - const oidClient = await oidSetupClient(); - const url = oidClient.authorizationUrl({ - scope: 'openid email profile', - }); - return url; + console.log('OIDC Setup Issuer Configuration:', { clientId, issuerUrl, redirectUrl }); + + if (!issuerUrl) + throw new SSROnlyError('OIDC ENV: Missing OID issuer URL'); + + if (!clientId) + throw new SSROnlyError('OIDC ENV: Missing OID client ID'); + + if (!redirectUrl) + throw new SSROnlyError('OIDC ENV: Missing OID redirect url back to this app (OAuth2 callback path)'); + + // prepare Open ID Issuer client + const url = new URL(issuerUrl) + + console.log('OIDC Discovering from URL:', url.toString()); + + const config = await openid.discovery(url, clientId); + + console.log('OIDC Config Complete:', config); + + // return configuration + return config; + } catch (error: unknown) { + console.error('OIDC Setup Error:', error); + console.error('OIDC Setup Error Message:', messageFromError(error)); + console.error('OIDC Setup Error Stack:', (error instanceof Error) ? error.stack : 'no stack'); + console.error('OIDC Setup Error Type:', (error instanceof Error) ? error.cause : 'no cause'); + throw new SSROnlyError(`OIDC Config Setup: Failed to setup OID client: ${messageFromError(error)}`); + } } - - async function handleLogout(tokenExchangeUrl: Unsure) { - if (!tokenExchangeUrl) - throw new SSROnlyError('Missing URL for exchange'); - return { tokenExchangeUrl }; + async function handleUserOpenID() { + try { + + console.log('OIDC Handle User OpenID'); + + // get issuer configuration + const oidConfig = await oidSetupIssuerConfiguration(); + + // prepare parameters for authorisation URL + // TODO: state, nonce, PKCE + const parameters: Record = { + redirect_uri: redirectUrl, + scope: 'openid email' + } + + console.log('OIDC Build Authorization URL:', parameters); + const url = openid.buildAuthorizationUrl(oidConfig, parameters) + + console.log('OIDC Auth URL:', url.toString()); + + return url; + } catch (error: unknown) { + throw new SSROnlyError(`OIDC Build Authorization: Failed to build authorisation URL: ${messageFromError(error)}`); + } } - /** - * Obtain tokens from redirect by sending OAuth2 code back to issuer - * @param params Use NextRequest - * @param urlOrigin Origin of the URL from callback route - * @returns Tokens from the IAM - */ async function handleAuthCallback(params: any, tokenExchangeUrl: Unsure) { - // initialize Open ID client - const oidClient = await oidSetupClient(); - // prepare OID callback params for provider - const callbackParams = oidClient.callbackParams(params); + const oidConfig = await oidSetupIssuerConfiguration(); - // OID Provider response with tokens - const tokens = await oidClient.callback(checkContextEnvironmens().redirectUrl, callbackParams); + const tokens = await openid.authorizationCodeGrant(oidConfig, new URL(redirectUrl), { idTokenExpected: true }); // do we have tokens and all values? if (!tokens.access_token) @@ -113,8 +109,15 @@ export const ssrOpenidContext = (clientId: string, issuerUrl: string, redirectUr }; } + async function handleLogout(tokenExchangeUrl: Unsure) { + if (!tokenExchangeUrl) + throw new SSROnlyError('Missing URL for exchange'); + + return { tokenExchangeUrl }; + } + return { - handleInternalKeycloak, + handleAuthCode: handleUserOpenID, handleAuthCallback, handleLogout, }; From eb088d758f87a61d577e0124b40faf8da9d2ce26 Mon Sep 17 00:00:00 2001 From: Lukas Date: Thu, 19 Feb 2026 17:28:13 +0100 Subject: [PATCH 08/20] refactor(docs): update README for clarity and remove AGENTS.md --- .gitignore | 7 ++++++- AGENTS.md | 50 -------------------------------------------------- Readme.md | 31 +++++++++++++++++++++++++++---- 3 files changed, 33 insertions(+), 55 deletions(-) delete mode 100644 AGENTS.md diff --git a/.gitignore b/.gitignore index fd7844f..c6af40e 100644 --- a/.gitignore +++ b/.gitignore @@ -35,4 +35,9 @@ coverage/ # Misc *.nogit.* CHANGELOG.md -CONTRIBUTING.md \ No newline at end of file +CONTRIBUTING.md + +# AI Agent Specific +GEMINI.md +CLAUDE.md +AGENTS.md \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index 70c512a..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,50 +0,0 @@ -# Copilot / AI Agent Instructions — ptr-be-core -Shared TypeScript library for backend and frontend applications of the Panther project. - -## Important Rule -- Read and follow all `.github/instructions/*` files for code and practises clarity. -- Predefined prompts can be found in `.github/prompts/` - - -## Repository Summary -- This repo is a shared TypeScript library used by backend and frontend apps. -- It builds two entrypoints: `browser` and `node` (see `src/index.browser.ts` and `src/index.node.ts`). -- Node entrypoint includes server-only APIs and helpers (e.g., logging, API response models, Arrows.json parsing). -- As build system, it uses `rollup` to produce ESM bundles in `dist/`. -- The package is published locally with `yalc` for local integration and testing. - -## Mandatory folders / files -- `src/`: main source code in TypeScript. -- `tests/`: unit tests using Vitest. -- `src/globals/`: global types and interfaces for both frontend and backend. -- `src/node/`: Node.js specific code -- `src/index.browser.ts`: exports for browser runtime. -- `src/index.node.ts`: exports for Node.js runtime. -- `dist/`: built artifacts (do not edit directly). -- `package.json`: project metadata, scripts, and dependencies. -- `rollup.config.js`: build configuration for rollup. -- `tsconfig.json`: TypeScript configuration. -- `vitest.config.ts`: configuration for Vitest test runner. -- `docs/`: documentation files (if any). -- `.github/`: GitHub-specific files including workflows and instructions. - -# Testing and Linting -- Using Vitest as the test framework and typescript for type safety. -- Please follow `.github/instructions/testing.instructions.md` for testing-related guidelines. -- This project uses ESLint major version 8 for code linting. -- Read `eslint.config.js` for detailed linting rules and configurations. - -# Build and Publish goals -- Always build two bundles: browser and node. -- Use `rollup` for bundling. -- Build declaration files (`.d.ts`) for type safety. -- Publish the package locally using `yalc` for testing in other projects before actual publishing. -- Read `.github/instructions/build.instructions.md` for detailed build and publish guidelines. - -# Delivered Features -- Logging utilities for Node.js applications (`src/node/logging/logger.ts`). -- API response models for consistent API responses (`src/node/api/models.api.ts`). -- Functions to parse and validate Arrows.app JSON format (`src/node/api/parse.arrows.json.ts`). -- Utility functions for parsing and validating raw input data (`src/node/api/parse.raw.input.ts`). -- Global types and interfaces shared between frontend and backend (`src/globals/arrows/`, `src/globals/panther/`). -- General shared coding helpers and formatters (`src/globals/coding/`) diff --git a/Readme.md b/Readme.md index 4ace779..d4e31b0 100644 --- a/Readme.md +++ b/Readme.md @@ -1,5 +1,25 @@ -# Shared Library for Panther Backend -This repository contains source of shared functionalities across Panther backend services. +# NPM Library for Panther Backends +NPM package with shared functionalities across Panther projects (FE and BE). + +## Build and Outputs +The NPM contains two standalone builds made from Typescript by Rollup. + +- Globals NPM (FE + BE): + - From `index.browser.ts` containing exports from `src/globals/**/*`. + - This is shared functionality between FE and BE services like models, code helpers etc.. + +- Node NPM: + - Containes all from globals + - By `index.node.ts` containing exports from `src/node/**/*`. This is Node JS only funtionality. Everything with peer dependency to Node.js packages should be here. + +Production build and publishing is realised by Github Workflow. + +### Rollup and Package.json +As mentioned, Rollup set up in `rollup.config.js` make two standalone builds named by purpose. + +Important is setup in `package.json` +- `type` with value `module` +- `exports`, each for one NPM target ## Installation (DEV) - Install Node and NPM @@ -12,11 +32,14 @@ This repository contains source of shared functionalities across Panther backend - Run `npm run dev` for development mode with Rollup build watcher and auto yalc publishing. ## Usage in Applications -- FE based apps should use imports from `@gisatcz/ptr-be-core/browser` -- NodeJS based apps should use imports from `@gisatcz/ptr-be-core/node` +- FE apps (in browser) should use imports from `@gisatcz/ptr-be-core/browser` +- Node.js apps and NextJS backend routes should use imports from `@gisatcz/ptr-be-core/node` ## Resources - YALC (local NPM): https://github.com/wclr/yalc - Vitest (testing): https://vitest.dev - Barrelsby (TS Imports into one `index.ts`): https://github.com/bencoveney/barrelsby - Rollup (NPM package build): https://rollupjs.org + +## AI Agents (Important) +Please folow instructions in `.github/` for better code results. \ No newline at end of file From 327535ce3b2b5ed114555713e0ba64b7413e4f12 Mon Sep 17 00:00:00 2001 From: Lukas Date: Thu, 19 Feb 2026 17:31:42 +0100 Subject: [PATCH 09/20] refactor(docs): update README structure for clarity and improve configuration section --- Readme.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index d4e31b0..1015466 100644 --- a/Readme.md +++ b/Readme.md @@ -14,12 +14,16 @@ The NPM contains two standalone builds made from Typescript by Rollup. Production build and publishing is realised by Github Workflow. -### Rollup and Package.json +## Configuration Files As mentioned, Rollup set up in `rollup.config.js` make two standalone builds named by purpose. Important is setup in `package.json` - `type` with value `module` - `exports`, each for one NPM target +- Empty `dependencies`. +- All required dependencies defined in `peerDependencies` field. + +Each build platform has defined standalone `tsconfig` in the repository root. Final `tsconfig.json` is merged together by platform or purpose. ## Installation (DEV) - Install Node and NPM From baec643ce4aa4d431e7b6f11783f3f63eae098b7 Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 23 Feb 2026 16:14:49 +0100 Subject: [PATCH 10/20] feat(auth): enhance OpenID handling with PKCE and add tests for authorization flow --- src/node/auth/openid.ssr.ts | 109 ++++++++++++++++++---- tests/functional/openid.ssr.spec.ts | 139 ++++++++++++++++++++++++++++ 2 files changed, 231 insertions(+), 17 deletions(-) create mode 100644 tests/functional/openid.ssr.spec.ts diff --git a/src/node/auth/openid.ssr.ts b/src/node/auth/openid.ssr.ts index f8a8055..eb75407 100644 --- a/src/node/auth/openid.ssr.ts +++ b/src/node/auth/openid.ssr.ts @@ -3,23 +3,42 @@ import { Unsure } from '../../index.browser'; import { SSROnlyError } from '../api/models.errors'; import { messageFromError } from '../api/parsing.errors'; +type AuthRequestChecks = { + pkceCodeVerifier: string; + state: string; + nonce: string; +}; + +type AuthCallbackParams = { + currentUrl?: URL | string; + query?: Record; + pkceCodeVerifier?: string; + expectedState?: string; + expectedNonce?: string; +} | URL | string | undefined; + +type AuthCodeResult = { + authorizationUrl: URL; + checks: AuthRequestChecks; +}; /** * OpenID Connect functional context (as server side JS closure) * @returns Set of functions to handle OpenID (OAuth2) operations using official openid library with server side environments */ -export const ssrOpenidContext = (clientId: string, issuerUrl: string, redirectUrl: string) => { +export const ssrOpenidContext = (clientId: string, issuerUrl: string, redirectUrl: string, clientSecret?: string) => { /** * Creates the active client for OpenID operations * @returns OpenID set up client ready to be used */ - async function oidSetupIssuerConfiguration() { + async function oidSetupIssuerConfiguration(): Promise { try { console.log('OIDC Setup Issuer Configuration:', { clientId, issuerUrl, redirectUrl }); if (!issuerUrl) + throw new SSROnlyError('OIDC ENV: Missing OID issuer URL'); if (!clientId) @@ -33,7 +52,7 @@ export const ssrOpenidContext = (clientId: string, issuerUrl: string, redirectUr console.log('OIDC Discovering from URL:', url.toString()); - const config = await openid.discovery(url, clientId); + const config = await openid.discovery(url, clientId, clientSecret); console.log('OIDC Config Complete:', config); @@ -48,37 +67,95 @@ export const ssrOpenidContext = (clientId: string, issuerUrl: string, redirectUr } } - async function handleUserOpenID() { + async function handleUserOpenID(): Promise { try { console.log('OIDC Handle User OpenID'); - // get issuer configuration const oidConfig = await oidSetupIssuerConfiguration(); + const pkceCodeVerifier = openid.randomPKCECodeVerifier(); + const codeChallenge = await openid.calculatePKCECodeChallenge(pkceCodeVerifier); + const state = openid.randomState(); + const nonce = openid.randomNonce(); - // prepare parameters for authorisation URL - // TODO: state, nonce, PKCE const parameters: Record = { redirect_uri: redirectUrl, - scope: 'openid email' + scope: 'openid email', + code_challenge: codeChallenge, + code_challenge_method: 'S256', + state, + nonce } console.log('OIDC Build Authorization URL:', parameters); - const url = openid.buildAuthorizationUrl(oidConfig, parameters) - - console.log('OIDC Auth URL:', url.toString()); - - return url; + const authorizationUrl = openid.buildAuthorizationUrl(oidConfig, parameters) + + console.log('OIDC Auth URL:', authorizationUrl.toString()); + + return { + authorizationUrl, + checks: { + pkceCodeVerifier, + state, + nonce + } + }; } catch (error: unknown) { throw new SSROnlyError(`OIDC Build Authorization: Failed to build authorisation URL: ${messageFromError(error)}`); } } - async function handleAuthCallback(params: any, tokenExchangeUrl: Unsure) { + function parseCurrentCallbackUrl(params: AuthCallbackParams): URL { + if (params instanceof URL) + return params; + + if (typeof params === 'string') + return new URL(params, redirectUrl); + + if (params && typeof params === 'object' && 'currentUrl' in params && params.currentUrl) { + const currentUrl = params.currentUrl; + if (currentUrl instanceof URL) + return currentUrl; + if (typeof currentUrl === 'string') + return new URL(currentUrl, redirectUrl); + } + + const queryInput = (params && typeof params === 'object') + ? ('query' in params && params.query ? params.query : params) + : undefined; + + if (!queryInput || typeof queryInput !== 'object') + throw new SSROnlyError('Missing callback URL or query params from OpenID callback'); + + const callbackUrl = new URL(redirectUrl); + + for (const [key, value] of Object.entries(queryInput)) { + if (typeof value === 'string' && value.length > 0) + callbackUrl.searchParams.set(key, value); + } + + if (!callbackUrl.searchParams.has('code') && !callbackUrl.searchParams.has('error')) + throw new SSROnlyError('Missing OpenID callback query param `code`'); + + return callbackUrl; + } + + async function handleAuthCallback(params: AuthCallbackParams, tokenExchangeUrl: Unsure) { const oidConfig = await oidSetupIssuerConfiguration(); + const currentCallbackUrl = parseCurrentCallbackUrl(params); + const checks: openid.AuthorizationCodeGrantChecks = { idTokenExpected: true }; + + if (params && typeof params === 'object' && !(params instanceof URL)) { + if (typeof params.pkceCodeVerifier === 'string') + checks.pkceCodeVerifier = params.pkceCodeVerifier; + if (typeof params.expectedState === 'string') + checks.expectedState = params.expectedState; + if (typeof params.expectedNonce === 'string') + checks.expectedNonce = params.expectedNonce; + } - const tokens = await openid.authorizationCodeGrant(oidConfig, new URL(redirectUrl), { idTokenExpected: true }); + const tokens = await openid.authorizationCodeGrant(oidConfig, currentCallbackUrl, checks); // do we have tokens and all values? if (!tokens.access_token) @@ -87,8 +164,6 @@ export const ssrOpenidContext = (clientId: string, issuerUrl: string, redirectUr throw new SSROnlyError('Missing OID information'); if (!tokens.id_token) throw new SSROnlyError('Missing OID information'); - if (!tokens.refresh_token) - throw new SSROnlyError('Missing OID information'); if (!clientId) throw new SSROnlyError('Missing client ID'); if (!tokenExchangeUrl) diff --git a/tests/functional/openid.ssr.spec.ts b/tests/functional/openid.ssr.spec.ts new file mode 100644 index 0000000..ca4e971 --- /dev/null +++ b/tests/functional/openid.ssr.spec.ts @@ -0,0 +1,139 @@ +import * as openid from 'openid-client'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { ssrOpenidContext } from '../../src/node/auth/openid.ssr'; + +vi.mock('openid-client', () => ({ + discovery: vi.fn(), + buildAuthorizationUrl: vi.fn(), + authorizationCodeGrant: vi.fn(), + randomPKCECodeVerifier: vi.fn(), + calculatePKCECodeChallenge: vi.fn(), + randomState: vi.fn(), + randomNonce: vi.fn(), +})); + +describe('ssrOpenidContext', () => { + const mockConfig = { issuer: 'https://issuer.example' } as unknown as openid.Configuration; + + beforeEach(() => { + vi.resetAllMocks(); + + vi.mocked(openid.discovery).mockResolvedValue(mockConfig); + vi.mocked(openid.randomPKCECodeVerifier).mockReturnValue('pkce-verifier'); + vi.mocked(openid.calculatePKCECodeChallenge).mockResolvedValue('pkce-challenge'); + vi.mocked(openid.randomState).mockReturnValue('state-123'); + vi.mocked(openid.randomNonce).mockReturnValue('nonce-123'); + vi.mocked(openid.buildAuthorizationUrl).mockImplementation((_, parameters) => { + const url = new URL('https://issuer.example/authorize'); + const mapped = parameters as Record; + for (const [key, value] of Object.entries(mapped)) + url.searchParams.set(key, value); + return url; + }); + vi.mocked(openid.authorizationCodeGrant).mockResolvedValue({ + access_token: 'access', + refresh_token: 'refresh', + id_token: 'id', + } as openid.TokenEndpointResponse & openid.TokenEndpointResponseHelpers); + }); + + it('builds auth URL with PKCE/state/nonce in handleAuthCode', async () => { + const context = ssrOpenidContext( + 'client-1', + 'https://issuer.example', + 'https://app.example/auth/callback' + ); + + const result = await context.handleAuthCode(); + + expect(result.authorizationUrl.toString()).toContain('https://issuer.example/authorize'); + expect(result.checks).toEqual({ + pkceCodeVerifier: 'pkce-verifier', + state: 'state-123', + nonce: 'nonce-123', + }); + expect(vi.mocked(openid.buildAuthorizationUrl)).toHaveBeenCalledWith( + mockConfig, + expect.objectContaining({ + redirect_uri: 'https://app.example/auth/callback', + scope: 'openid email', + code_challenge: 'pkce-challenge', + code_challenge_method: 'S256', + state: 'state-123', + nonce: 'nonce-123', + }) + ); + }); + + it('uses callback currentUrl and expected checks in authorizationCodeGrant', async () => { + const context = ssrOpenidContext( + 'client-1', + 'https://issuer.example', + 'https://app.example/auth/callback' + ); + + const output = await context.handleAuthCallback( + { + currentUrl: 'https://app.example/auth/callback?code=abc&state=state-123', + pkceCodeVerifier: 'pkce-verifier', + expectedState: 'state-123', + expectedNonce: 'nonce-123', + }, + 'https://identity.example/oid/token-exchange' + ); + + expect(vi.mocked(openid.authorizationCodeGrant)).toHaveBeenCalledTimes(1); + const grantCall = vi.mocked(openid.authorizationCodeGrant).mock.calls[0]; + const callbackUrl = grantCall[1] as URL; + const checks = grantCall[2] as openid.AuthorizationCodeGrantChecks; + + expect(callbackUrl.toString()).toBe('https://app.example/auth/callback?code=abc&state=state-123'); + expect(checks).toEqual({ + idTokenExpected: true, + pkceCodeVerifier: 'pkce-verifier', + expectedState: 'state-123', + expectedNonce: 'nonce-123', + }); + expect(output.tokens.id_token).toBe('id'); + expect(output.tokenExchangeUrl).toBe('https://identity.example/oid/token-exchange'); + }); + + it('builds callback URL from query object when currentUrl is not provided', async () => { + const context = ssrOpenidContext( + 'client-1', + 'https://issuer.example', + 'https://app.example/auth/callback' + ); + + await context.handleAuthCallback( + { + query: { code: 'query-code', state: 'query-state' }, + pkceCodeVerifier: 'pkce-verifier', + expectedState: 'query-state', + }, + 'https://identity.example/oid/token-exchange' + ); + + const grantCall = vi.mocked(openid.authorizationCodeGrant).mock.calls[0]; + const callbackUrl = grantCall[1] as URL; + + expect(callbackUrl.toString()).toBe('https://app.example/auth/callback?code=query-code&state=query-state'); + }); + + it('passes optional client secret to discovery', async () => { + const context = ssrOpenidContext( + 'client-1', + 'https://issuer.example', + 'https://app.example/auth/callback', + 'client-secret-1' + ); + + await context.handleAuthCode(); + + expect(vi.mocked(openid.discovery)).toHaveBeenCalledTimes(1); + const call = vi.mocked(openid.discovery).mock.calls[0]; + expect((call[0] as URL).toString()).toBe('https://issuer.example/'); + expect(call[1]).toBe('client-1'); + expect(call[2]).toBe('client-secret-1'); + }); +}); From 28538725941772e9f32b31d243e97fd461cf0d39 Mon Sep 17 00:00:00 2001 From: Lukas Date: Tue, 24 Feb 2026 15:56:57 +0100 Subject: [PATCH 11/20] refactor(auth): remove unused OpenID handlers and clean up related code --- package.json | 1 - src/index.browser.ts | 1 - src/index.node.ts | 5 + src/node/auth/handlers.callbackFetch.ts | 26 ---- src/node/auth/handlers.logoutFetch.ts | 43 ----- src/node/auth/handlers.sessionFetch.ts | 80 ---------- src/node/auth/openid.ssr.ts | 199 ------------------------ 7 files changed, 5 insertions(+), 350 deletions(-) delete mode 100644 src/node/auth/handlers.callbackFetch.ts delete mode 100644 src/node/auth/handlers.logoutFetch.ts delete mode 100644 src/node/auth/handlers.sessionFetch.ts delete mode 100644 src/node/auth/openid.ssr.ts diff --git a/package.json b/package.json index ef95c59..236b6f9 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,6 @@ "author": "", "license": "ISC", "peerDependencies": { - "openid-client": "^6.8.1", "sqlite": "^5.1.1", "sqlite3": "^5.1.7", "lodash": "^4.17.21", diff --git a/src/index.browser.ts b/src/index.browser.ts index 61ab664..42fd9e1 100644 --- a/src/index.browser.ts +++ b/src/index.browser.ts @@ -1,4 +1,3 @@ - // shared code helpers export { enumCombineValuesToString, diff --git a/src/index.node.ts b/src/index.node.ts index 32b17eb..fa93a89 100644 --- a/src/index.node.ts +++ b/src/index.node.ts @@ -5,6 +5,11 @@ export * from "./index.browser.js"; // API error handling export { InvalidRequestError, AuthorizationError, ServerError, SSROnlyError } from "./node/api/models.errors.js"; +export { + messageFromError +} from "./node/api/parsing.errors.js"; + + // API models and helpers export { type ApiEndpointResponse } from "./node/api/models.api.js"; diff --git a/src/node/auth/handlers.callbackFetch.ts b/src/node/auth/handlers.callbackFetch.ts deleted file mode 100644 index 0fd523a..0000000 --- a/src/node/auth/handlers.callbackFetch.ts +++ /dev/null @@ -1,26 +0,0 @@ -// /** -// * Fetch from API handler to backend service with value included in cookies -// * @param endpointUrl Endpoint URL -// * @param body Body for POST request -// * @param method Optional - method for fetch request -// * @returns Cookie header from response -// */ -// export const fetchForCookies = async (endpointUrl: string, body: unknown, method = 'POST') => { -// // execute fetch request -// const response = await fetch(endpointUrl as string, { -// method, -// headers: { -// 'Content-Type': 'application/json', -// }, -// body: JSON.stringify(body), -// }); - -// // get session cookie from response -// const setCookieHeader = response.headers.get('set-cookie'); - -// // check for cookie header -// if (!setCookieHeader) throw new Error('Missing session cookie in response'); - -// // return cookie header -// return setCookieHeader; -// }; diff --git a/src/node/auth/handlers.logoutFetch.ts b/src/node/auth/handlers.logoutFetch.ts deleted file mode 100644 index f489431..0000000 --- a/src/node/auth/handlers.logoutFetch.ts +++ /dev/null @@ -1,43 +0,0 @@ -// interface LogoutFetchProps { -// identityServiceUrl: string; -// browserCookies: any; -// } - -// /** -// * Fetch from API handler to backend service with session ID included in cookies -// * @param url URL of target endpoint -// * @param browserCookies Cookies from browser request (SPA part of Next) -// * @param headers Optional - any headers added to request -// * @returns Response from backend back to Next API route handler -// */ -// export const fetchLogoutNotification = async (props: LogoutFetchProps): Promise => { -// const { browserCookies, identityServiceUrl } = props; - -// // get session cookie from browser -// const sessionCookie = (browserCookies as any).get('sid'); - -// if (!sessionCookie) console.warn('Logout: Missing SID from browser'); - -// //prepare logout URL -// const logoutUrl = `${identityServiceUrl}/oid/logout`; - -// // prepare body for session exchange -// const body = { -// sid: sessionCookie, -// }; - -// // make notify POST request to backend for session exchange -// try { -// const response = await fetch(logoutUrl, { -// method: 'POST', -// headers: { -// 'Content-Type': 'application/json', -// }, -// body: JSON.stringify(body), -// }); - -// if (!response.ok) console.warn('Logout: Backend logout failed'); -// } catch (error: any) { -// console.warn(`Logout: Backend logout failed: ${error['message']}`); -// } -// }; diff --git a/src/node/auth/handlers.sessionFetch.ts b/src/node/auth/handlers.sessionFetch.ts deleted file mode 100644 index e1f5836..0000000 --- a/src/node/auth/handlers.sessionFetch.ts +++ /dev/null @@ -1,80 +0,0 @@ -// import { Nullable } from '@gisatcz/ptr-be-core/browser'; -// import { ErrorBehavior } from '../../../globals/shared/errors/enums.errorBehavior'; -// import { HttpStatusCode } from '../../../globals/shared/errors/enums.httpStatusCode'; -// import { BaseHttpError } from '../../../globals/shared/errors/models.error'; - -// // Define the interface for the properties of the fetchWithSessions function -// interface FetchWithBrowserSessionProps { -// method: 'GET' | 'POST'; -// url: string; -// browserCookies: any; -// body?: object; -// headers?: any; -// requireSessionId: boolean; -// } - -// // Define the interface for the response of the fetchWithSessions function -// interface FetchWithSessionsResponse { -// status: number; -// backendContent: Nullable; -// setCookieHeader: Nullable; -// } - -// /** -// * Fetch from API handler to backend service with session ID included in cookies -// * @param url URL of target endpoint -// * @param browserCookies Cookies from browser request (SPA part of Next) -// * @param headers Optional - any headers added to request -// * @returns Response from backend back to Next API route handler -// */ -// export const fetchWithSessions = async (props: FetchWithBrowserSessionProps): Promise => { -// const { url, browserCookies, method, body, headers } = props; - -// // Check if the URL is provided -// if (!url) -// throw new BaseHttpError('Missing URL for session fetch', HttpStatusCode.INTERNAL_SERVER_ERROR, ErrorBehavior.SSR); - -// // Get the session cookie from the browser cookies -// const sessionCookie = (browserCookies as any).get('sid'); - -// // Check if the session cookie is required but missing -// if (!sessionCookie && props.requireSessionId) -// throw new BaseHttpError('Missing session from browser', HttpStatusCode.UNAUTHORIZED, ErrorBehavior.BE); - -// // Add the session cookie to the headers if it exists -// const headersWithCookies = sessionCookie -// ? { ...headers, Cookie: `${sessionCookie.name}=${sessionCookie.value}` } -// : { ...headers }; - -// // Make the fetch request to the backend -// const response = await fetch(url, { -// method, -// body: body ? JSON.stringify(body) : undefined, -// headers: headersWithCookies, -// }); - -// // Parse the response from the backend -// const backendContent = await response.json(); - -// // Check if the response is successful -// if (response.ok) { -// const setCookieHeader = response.headers.get('set-cookie'); - -// // Check if the set-cookie header is missing when a session ID is required -// if (!setCookieHeader && props.requireSessionId) { -// console.error('Sessions Fetch: Missing cookies with SID'); - -// throw new BaseHttpError( -// 'Fetch response with new session cookie missing', -// HttpStatusCode.INTERNAL_SERVER_ERROR, -// ErrorBehavior.BE -// ); -// } - -// // Return the response status, content, and set-cookie header -// return { status: response.status, backendContent, setCookieHeader }; -// } else { -// console.error('Error in fetchWithSessions', response.status, backendContent); -// throw new BaseHttpError(backendContent, response.status, ErrorBehavior.BE); -// } -// }; diff --git a/src/node/auth/openid.ssr.ts b/src/node/auth/openid.ssr.ts deleted file mode 100644 index eb75407..0000000 --- a/src/node/auth/openid.ssr.ts +++ /dev/null @@ -1,199 +0,0 @@ -import * as openid from 'openid-client'; -import { Unsure } from '../../index.browser'; -import { SSROnlyError } from '../api/models.errors'; -import { messageFromError } from '../api/parsing.errors'; - -type AuthRequestChecks = { - pkceCodeVerifier: string; - state: string; - nonce: string; -}; - -type AuthCallbackParams = { - currentUrl?: URL | string; - query?: Record; - pkceCodeVerifier?: string; - expectedState?: string; - expectedNonce?: string; -} | URL | string | undefined; - -type AuthCodeResult = { - authorizationUrl: URL; - checks: AuthRequestChecks; -}; - -/** - * OpenID Connect functional context (as server side JS closure) - * @returns Set of functions to handle OpenID (OAuth2) operations using official openid library with server side environments - */ -export const ssrOpenidContext = (clientId: string, issuerUrl: string, redirectUrl: string, clientSecret?: string) => { - - /** - * Creates the active client for OpenID operations - * @returns OpenID set up client ready to be used - */ - async function oidSetupIssuerConfiguration(): Promise { - try { - - console.log('OIDC Setup Issuer Configuration:', { clientId, issuerUrl, redirectUrl }); - - if (!issuerUrl) - - throw new SSROnlyError('OIDC ENV: Missing OID issuer URL'); - - if (!clientId) - throw new SSROnlyError('OIDC ENV: Missing OID client ID'); - - if (!redirectUrl) - throw new SSROnlyError('OIDC ENV: Missing OID redirect url back to this app (OAuth2 callback path)'); - - // prepare Open ID Issuer client - const url = new URL(issuerUrl) - - console.log('OIDC Discovering from URL:', url.toString()); - - const config = await openid.discovery(url, clientId, clientSecret); - - console.log('OIDC Config Complete:', config); - - // return configuration - return config; - } catch (error: unknown) { - console.error('OIDC Setup Error:', error); - console.error('OIDC Setup Error Message:', messageFromError(error)); - console.error('OIDC Setup Error Stack:', (error instanceof Error) ? error.stack : 'no stack'); - console.error('OIDC Setup Error Type:', (error instanceof Error) ? error.cause : 'no cause'); - throw new SSROnlyError(`OIDC Config Setup: Failed to setup OID client: ${messageFromError(error)}`); - } - } - - async function handleUserOpenID(): Promise { - try { - - console.log('OIDC Handle User OpenID'); - - const oidConfig = await oidSetupIssuerConfiguration(); - const pkceCodeVerifier = openid.randomPKCECodeVerifier(); - const codeChallenge = await openid.calculatePKCECodeChallenge(pkceCodeVerifier); - const state = openid.randomState(); - const nonce = openid.randomNonce(); - - const parameters: Record = { - redirect_uri: redirectUrl, - scope: 'openid email', - code_challenge: codeChallenge, - code_challenge_method: 'S256', - state, - nonce - } - - console.log('OIDC Build Authorization URL:', parameters); - const authorizationUrl = openid.buildAuthorizationUrl(oidConfig, parameters) - - console.log('OIDC Auth URL:', authorizationUrl.toString()); - - return { - authorizationUrl, - checks: { - pkceCodeVerifier, - state, - nonce - } - }; - } catch (error: unknown) { - throw new SSROnlyError(`OIDC Build Authorization: Failed to build authorisation URL: ${messageFromError(error)}`); - } - } - - function parseCurrentCallbackUrl(params: AuthCallbackParams): URL { - if (params instanceof URL) - return params; - - if (typeof params === 'string') - return new URL(params, redirectUrl); - - if (params && typeof params === 'object' && 'currentUrl' in params && params.currentUrl) { - const currentUrl = params.currentUrl; - if (currentUrl instanceof URL) - return currentUrl; - if (typeof currentUrl === 'string') - return new URL(currentUrl, redirectUrl); - } - - const queryInput = (params && typeof params === 'object') - ? ('query' in params && params.query ? params.query : params) - : undefined; - - if (!queryInput || typeof queryInput !== 'object') - throw new SSROnlyError('Missing callback URL or query params from OpenID callback'); - - const callbackUrl = new URL(redirectUrl); - - for (const [key, value] of Object.entries(queryInput)) { - if (typeof value === 'string' && value.length > 0) - callbackUrl.searchParams.set(key, value); - } - - if (!callbackUrl.searchParams.has('code') && !callbackUrl.searchParams.has('error')) - throw new SSROnlyError('Missing OpenID callback query param `code`'); - - return callbackUrl; - } - - async function handleAuthCallback(params: AuthCallbackParams, tokenExchangeUrl: Unsure) { - - const oidConfig = await oidSetupIssuerConfiguration(); - const currentCallbackUrl = parseCurrentCallbackUrl(params); - const checks: openid.AuthorizationCodeGrantChecks = { idTokenExpected: true }; - - if (params && typeof params === 'object' && !(params instanceof URL)) { - if (typeof params.pkceCodeVerifier === 'string') - checks.pkceCodeVerifier = params.pkceCodeVerifier; - if (typeof params.expectedState === 'string') - checks.expectedState = params.expectedState; - if (typeof params.expectedNonce === 'string') - checks.expectedNonce = params.expectedNonce; - } - - const tokens = await openid.authorizationCodeGrant(oidConfig, currentCallbackUrl, checks); - - // do we have tokens and all values? - if (!tokens.access_token) - throw new SSROnlyError('Missing OID information'); - if (!tokens.refresh_token) - throw new SSROnlyError('Missing OID information'); - if (!tokens.id_token) - throw new SSROnlyError('Missing OID information'); - if (!clientId) - throw new SSROnlyError('Missing client ID'); - if (!tokenExchangeUrl) - throw new SSROnlyError('Missing URL for excgange'); - if (!issuerUrl) - throw new SSROnlyError('Missing issuer URL'); - - // prepare output for route - return { - tokens: { - access_token: tokens.access_token, - refresh_token: tokens.refresh_token, - id_token: tokens.id_token, - }, - clientId, - issuerUrl, - tokenExchangeUrl, - }; - } - - async function handleLogout(tokenExchangeUrl: Unsure) { - if (!tokenExchangeUrl) - throw new SSROnlyError('Missing URL for exchange'); - - return { tokenExchangeUrl }; - } - - return { - handleAuthCode: handleUserOpenID, - handleAuthCallback, - handleLogout, - }; -} From 6795a08aceb88393df424a386d9a0e4632c8ce59 Mon Sep 17 00:00:00 2001 From: Lukas Date: Tue, 24 Feb 2026 15:57:17 +0100 Subject: [PATCH 12/20] refactor(auth): remove OpenID Connect export for server-side rendering --- src/index.node.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/index.node.ts b/src/index.node.ts index fa93a89..e6b0dff 100644 --- a/src/index.node.ts +++ b/src/index.node.ts @@ -78,10 +78,4 @@ export { // SQLite queries for app state retrieval export { dbNeedAppState -} from "./node/sqlite/sqlite.queries.js"; - - -// OpenID Connect functionality for server-side rendering apps (FE API routes) -export { - ssrOpenidContext -} from "./node/auth/openid.ssr.js"; \ No newline at end of file +} from "./node/sqlite/sqlite.queries.js"; \ No newline at end of file From e46834fa4004ac938182ae7ce04d9b3d5e9a26af Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 2 Mar 2026 11:35:44 +0100 Subject: [PATCH 13/20] feat(workflows): add testing workflows for code and push events --- .github/workflows/code-test.yaml | 28 ++++++++++++++++++++++++++++ .github/workflows/testing-push.yaml | 8 ++++++++ .github/workflows/tests.yml | 25 +++++++------------------ 3 files changed, 43 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/code-test.yaml create mode 100644 .github/workflows/testing-push.yaml diff --git a/.github/workflows/code-test.yaml b/.github/workflows/code-test.yaml new file mode 100644 index 0000000..e114563 --- /dev/null +++ b/.github/workflows/code-test.yaml @@ -0,0 +1,28 @@ +name: test-the-code-with-infrastructure +on: workflow_call + +jobs: + integration-test: + runs-on: ubuntu-latest + cancel-timeout-minutes: 5 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: 'lts/*' + + - name: Install dependencies + run: npm install + + - name: Run linter (ESLint) + run: npm run lint + + - name: Build project + run: npm run build + + - name: Run tests + run: npm run test \ No newline at end of file diff --git a/.github/workflows/testing-push.yaml b/.github/workflows/testing-push.yaml new file mode 100644 index 0000000..d0a63b3 --- /dev/null +++ b/.github/workflows/testing-push.yaml @@ -0,0 +1,8 @@ +name: testing-push-workflow +on: + push: + branches: + - test/* +jobs: + test: + uses: ./.github/workflows/integration-test.yaml \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 41d4e00..7dd2044 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -9,28 +9,17 @@ on: - rc jobs: - code: - runs-on: ubuntu-latest - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install dependencies - run: npm install + tests-and-linting: + uses: ./.github/workflows/code-test.yaml - - name: Run tests - run: npm test - - build: + docker-build: runs-on: ubuntu-latest - steps: - name: Checkout code uses: actions/checkout@v4 - - name: Install dependencies - run: npm install + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 - - name: Build project - run: npm run build + - name: Build Docker image + run: docker build -t image:test . From 77f538c9ae12f4687f138753a2077acf0c5838ff Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 2 Mar 2026 11:40:24 +0100 Subject: [PATCH 14/20] feat(workflows): enhance CI workflows with permissions and concurrency settings --- .github/workflows/code-test.yaml | 9 ++++++--- .github/workflows/tests.yml | 8 ++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/code-test.yaml b/.github/workflows/code-test.yaml index e114563..7e941e6 100644 --- a/.github/workflows/code-test.yaml +++ b/.github/workflows/code-test.yaml @@ -1,10 +1,13 @@ name: test-the-code-with-infrastructure on: workflow_call +permissions: + contents: read + jobs: integration-test: runs-on: ubuntu-latest - cancel-timeout-minutes: 5 + timeout-minutes: 5 steps: - name: Checkout code @@ -13,7 +16,7 @@ jobs: - name: Install Node.js uses: actions/setup-node@v4 with: - node-version: 'lts/*' + node-version: '24' - name: Install dependencies run: npm install @@ -25,4 +28,4 @@ jobs: run: npm run build - name: Run tests - run: npm run test \ No newline at end of file + run: npm run test diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7dd2044..d8edf64 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,11 +8,19 @@ on: - dev - rc +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: tests-and-linting: uses: ./.github/workflows/code-test.yaml docker-build: + needs: tests-and-linting runs-on: ubuntu-latest steps: - name: Checkout code From f2a1ce6e00d831ab68da8a3782a77cfe0e0e84c7 Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 2 Mar 2026 11:42:00 +0100 Subject: [PATCH 15/20] feat(auth): add OpenID Connect SSR context for authorization code grant flow --- src/index.node.ts | 11 +++- src/node/auth/openid.ssr.ts | 122 ++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 src/node/auth/openid.ssr.ts diff --git a/src/index.node.ts b/src/index.node.ts index e6b0dff..0daa3f8 100644 --- a/src/index.node.ts +++ b/src/index.node.ts @@ -78,4 +78,13 @@ export { // SQLite queries for app state retrieval export { dbNeedAppState -} from "./node/sqlite/sqlite.queries.js"; \ No newline at end of file +} from "./node/sqlite/sqlite.queries.js"; + +// OpenID Connect SSR context for authorization code grant flow +export { + ssrOpenidContext, + type AuthCodeCheckValues, + type AuthCodeHandleResult, + type AuthCallbackInput, + type AuthCallbackResult +} from "./node/auth/openid.ssr.js"; \ No newline at end of file diff --git a/src/node/auth/openid.ssr.ts b/src/node/auth/openid.ssr.ts new file mode 100644 index 0000000..554f86e --- /dev/null +++ b/src/node/auth/openid.ssr.ts @@ -0,0 +1,122 @@ +import * as openid from 'openid-client'; + +export interface AuthCodeCheckValues { + pkceCodeVerifier: string; + state: string; + nonce: string; +} + +export interface AuthCodeHandleResult { + authorizationUrl: URL; + checks: AuthCodeCheckValues; +} + +export interface AuthCallbackInput { + currentUrl?: string; + query?: Record; + pkceCodeVerifier: string; + expectedState: string; + expectedNonce?: string; +} + +export interface AuthCallbackResult { + tokens: openid.TokenEndpointResponse & openid.TokenEndpointResponseHelpers; + tokenExchangeUrl: string; +} + +/** + * Creates an OpenID Connect context for server-side rendering (SSR) flows. + * Handles authorization code grant with PKCE, state, and nonce parameters. + * + * @param clientId - The OpenID Connect client ID + * @param issuerUrl - The OpenID Connect issuer URL + * @param callbackUrl - The callback URL for authorization code grant + * @param clientSecret - Optional client secret for confidential clients + */ +export function ssrOpenidContext( + clientId: string, + issuerUrl: string, + callbackUrl: string, + clientSecret?: string +) { + return { + /** + * Handle the initial authorization code request. + * Generates PKCE code verifier, state, and nonce. + */ + async handleAuthCode(): Promise { + const issuerUrlWithSlash = issuerUrl.endsWith('/') ? issuerUrl : `${issuerUrl}/`; + const issuerUri = new URL(issuerUrlWithSlash); + + const config = await openid.discovery(issuerUri, clientId, clientSecret); + + const pkceCodeVerifier = openid.randomPKCECodeVerifier(); + const pkceCodeChallenge = await openid.calculatePKCECodeChallenge(pkceCodeVerifier); + const state = openid.randomState(); + const nonce = openid.randomNonce(); + + const authorizationUrl = openid.buildAuthorizationUrl(config, { + redirect_uri: callbackUrl, + scope: 'openid email', + code_challenge: pkceCodeChallenge, + code_challenge_method: 'S256', + state, + nonce, + }); + + return { + authorizationUrl, + checks: { + pkceCodeVerifier, + state, + nonce, + }, + }; + }, + + /** + * Handle the authorization callback (code and state in query parameters). + * Exchanges the authorization code for tokens. + * + * @param input - Callback input with either currentUrl or query parameters + * @param tokenExchangeUrl - The token exchange URL (e.g., for token refresh) + */ + async handleAuthCallback( + input: AuthCallbackInput, + tokenExchangeUrl: string + ): Promise { + const issuerUrlWithSlash = issuerUrl.endsWith('/') ? issuerUrl : `${issuerUrl}/`; + const issuerUri = new URL(issuerUrlWithSlash); + + const config = await openid.discovery(issuerUri, clientId, clientSecret); + + // Build callback URL from either currentUrl or query object + let callbackUrlObj: URL; + if (input.currentUrl) { + callbackUrlObj = new URL(input.currentUrl); + } else if (input.query) { + callbackUrlObj = new URL(callbackUrl); + for (const [key, value] of Object.entries(input.query)) { + const stringValue = Array.isArray(value) ? value[0] : value; + callbackUrlObj.searchParams.set(key, stringValue); + } + } else { + throw new Error('Either currentUrl or query must be provided'); + } + + const checks: openid.AuthorizationCodeGrantChecks = { + idTokenExpected: true, + pkceCodeVerifier: input.pkceCodeVerifier, + expectedState: input.expectedState, + expectedNonce: input.expectedNonce, + }; + + const tokens = await openid.authorizationCodeGrant(config, callbackUrlObj, checks); + + return { + tokens, + tokenExchangeUrl, + }; + }, + }; +} From 966429bbf9c427f4aac38822a6bfeba2e46caff5 Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 2 Mar 2026 11:44:24 +0100 Subject: [PATCH 16/20] refactor(workflows): rename workflow for clarity and consistency --- .github/workflows/code-test.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/code-test.yaml b/.github/workflows/code-test.yaml index 7e941e6..20fa9c5 100644 --- a/.github/workflows/code-test.yaml +++ b/.github/workflows/code-test.yaml @@ -1,11 +1,11 @@ -name: test-the-code-with-infrastructure +name: test-build-and-lint on: workflow_call permissions: contents: read jobs: - integration-test: + pull-request-checker: runs-on: ubuntu-latest timeout-minutes: 5 From bcd7a96f4c0276ef7458e65207e9b07b6e02aa34 Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 2 Mar 2026 11:46:06 +0100 Subject: [PATCH 17/20] refactor(auth): remove OpenID Connect SSR context and related tests --- src/index.node.ts | 11 +-- src/node/auth/openid.ssr.ts | 122 ------------------------ tests/functional/openid.ssr.spec.ts | 139 ---------------------------- 3 files changed, 1 insertion(+), 271 deletions(-) delete mode 100644 src/node/auth/openid.ssr.ts delete mode 100644 tests/functional/openid.ssr.spec.ts diff --git a/src/index.node.ts b/src/index.node.ts index 0daa3f8..e6b0dff 100644 --- a/src/index.node.ts +++ b/src/index.node.ts @@ -78,13 +78,4 @@ export { // SQLite queries for app state retrieval export { dbNeedAppState -} from "./node/sqlite/sqlite.queries.js"; - -// OpenID Connect SSR context for authorization code grant flow -export { - ssrOpenidContext, - type AuthCodeCheckValues, - type AuthCodeHandleResult, - type AuthCallbackInput, - type AuthCallbackResult -} from "./node/auth/openid.ssr.js"; \ No newline at end of file +} from "./node/sqlite/sqlite.queries.js"; \ No newline at end of file diff --git a/src/node/auth/openid.ssr.ts b/src/node/auth/openid.ssr.ts deleted file mode 100644 index 554f86e..0000000 --- a/src/node/auth/openid.ssr.ts +++ /dev/null @@ -1,122 +0,0 @@ -import * as openid from 'openid-client'; - -export interface AuthCodeCheckValues { - pkceCodeVerifier: string; - state: string; - nonce: string; -} - -export interface AuthCodeHandleResult { - authorizationUrl: URL; - checks: AuthCodeCheckValues; -} - -export interface AuthCallbackInput { - currentUrl?: string; - query?: Record; - pkceCodeVerifier: string; - expectedState: string; - expectedNonce?: string; -} - -export interface AuthCallbackResult { - tokens: openid.TokenEndpointResponse & openid.TokenEndpointResponseHelpers; - tokenExchangeUrl: string; -} - -/** - * Creates an OpenID Connect context for server-side rendering (SSR) flows. - * Handles authorization code grant with PKCE, state, and nonce parameters. - * - * @param clientId - The OpenID Connect client ID - * @param issuerUrl - The OpenID Connect issuer URL - * @param callbackUrl - The callback URL for authorization code grant - * @param clientSecret - Optional client secret for confidential clients - */ -export function ssrOpenidContext( - clientId: string, - issuerUrl: string, - callbackUrl: string, - clientSecret?: string -) { - return { - /** - * Handle the initial authorization code request. - * Generates PKCE code verifier, state, and nonce. - */ - async handleAuthCode(): Promise { - const issuerUrlWithSlash = issuerUrl.endsWith('/') ? issuerUrl : `${issuerUrl}/`; - const issuerUri = new URL(issuerUrlWithSlash); - - const config = await openid.discovery(issuerUri, clientId, clientSecret); - - const pkceCodeVerifier = openid.randomPKCECodeVerifier(); - const pkceCodeChallenge = await openid.calculatePKCECodeChallenge(pkceCodeVerifier); - const state = openid.randomState(); - const nonce = openid.randomNonce(); - - const authorizationUrl = openid.buildAuthorizationUrl(config, { - redirect_uri: callbackUrl, - scope: 'openid email', - code_challenge: pkceCodeChallenge, - code_challenge_method: 'S256', - state, - nonce, - }); - - return { - authorizationUrl, - checks: { - pkceCodeVerifier, - state, - nonce, - }, - }; - }, - - /** - * Handle the authorization callback (code and state in query parameters). - * Exchanges the authorization code for tokens. - * - * @param input - Callback input with either currentUrl or query parameters - * @param tokenExchangeUrl - The token exchange URL (e.g., for token refresh) - */ - async handleAuthCallback( - input: AuthCallbackInput, - tokenExchangeUrl: string - ): Promise { - const issuerUrlWithSlash = issuerUrl.endsWith('/') ? issuerUrl : `${issuerUrl}/`; - const issuerUri = new URL(issuerUrlWithSlash); - - const config = await openid.discovery(issuerUri, clientId, clientSecret); - - // Build callback URL from either currentUrl or query object - let callbackUrlObj: URL; - if (input.currentUrl) { - callbackUrlObj = new URL(input.currentUrl); - } else if (input.query) { - callbackUrlObj = new URL(callbackUrl); - for (const [key, value] of Object.entries(input.query)) { - const stringValue = Array.isArray(value) ? value[0] : value; - callbackUrlObj.searchParams.set(key, stringValue); - } - } else { - throw new Error('Either currentUrl or query must be provided'); - } - - const checks: openid.AuthorizationCodeGrantChecks = { - idTokenExpected: true, - pkceCodeVerifier: input.pkceCodeVerifier, - expectedState: input.expectedState, - expectedNonce: input.expectedNonce, - }; - - const tokens = await openid.authorizationCodeGrant(config, callbackUrlObj, checks); - - return { - tokens, - tokenExchangeUrl, - }; - }, - }; -} diff --git a/tests/functional/openid.ssr.spec.ts b/tests/functional/openid.ssr.spec.ts deleted file mode 100644 index ca4e971..0000000 --- a/tests/functional/openid.ssr.spec.ts +++ /dev/null @@ -1,139 +0,0 @@ -import * as openid from 'openid-client'; -import { beforeEach, describe, expect, it, vi } from 'vitest'; -import { ssrOpenidContext } from '../../src/node/auth/openid.ssr'; - -vi.mock('openid-client', () => ({ - discovery: vi.fn(), - buildAuthorizationUrl: vi.fn(), - authorizationCodeGrant: vi.fn(), - randomPKCECodeVerifier: vi.fn(), - calculatePKCECodeChallenge: vi.fn(), - randomState: vi.fn(), - randomNonce: vi.fn(), -})); - -describe('ssrOpenidContext', () => { - const mockConfig = { issuer: 'https://issuer.example' } as unknown as openid.Configuration; - - beforeEach(() => { - vi.resetAllMocks(); - - vi.mocked(openid.discovery).mockResolvedValue(mockConfig); - vi.mocked(openid.randomPKCECodeVerifier).mockReturnValue('pkce-verifier'); - vi.mocked(openid.calculatePKCECodeChallenge).mockResolvedValue('pkce-challenge'); - vi.mocked(openid.randomState).mockReturnValue('state-123'); - vi.mocked(openid.randomNonce).mockReturnValue('nonce-123'); - vi.mocked(openid.buildAuthorizationUrl).mockImplementation((_, parameters) => { - const url = new URL('https://issuer.example/authorize'); - const mapped = parameters as Record; - for (const [key, value] of Object.entries(mapped)) - url.searchParams.set(key, value); - return url; - }); - vi.mocked(openid.authorizationCodeGrant).mockResolvedValue({ - access_token: 'access', - refresh_token: 'refresh', - id_token: 'id', - } as openid.TokenEndpointResponse & openid.TokenEndpointResponseHelpers); - }); - - it('builds auth URL with PKCE/state/nonce in handleAuthCode', async () => { - const context = ssrOpenidContext( - 'client-1', - 'https://issuer.example', - 'https://app.example/auth/callback' - ); - - const result = await context.handleAuthCode(); - - expect(result.authorizationUrl.toString()).toContain('https://issuer.example/authorize'); - expect(result.checks).toEqual({ - pkceCodeVerifier: 'pkce-verifier', - state: 'state-123', - nonce: 'nonce-123', - }); - expect(vi.mocked(openid.buildAuthorizationUrl)).toHaveBeenCalledWith( - mockConfig, - expect.objectContaining({ - redirect_uri: 'https://app.example/auth/callback', - scope: 'openid email', - code_challenge: 'pkce-challenge', - code_challenge_method: 'S256', - state: 'state-123', - nonce: 'nonce-123', - }) - ); - }); - - it('uses callback currentUrl and expected checks in authorizationCodeGrant', async () => { - const context = ssrOpenidContext( - 'client-1', - 'https://issuer.example', - 'https://app.example/auth/callback' - ); - - const output = await context.handleAuthCallback( - { - currentUrl: 'https://app.example/auth/callback?code=abc&state=state-123', - pkceCodeVerifier: 'pkce-verifier', - expectedState: 'state-123', - expectedNonce: 'nonce-123', - }, - 'https://identity.example/oid/token-exchange' - ); - - expect(vi.mocked(openid.authorizationCodeGrant)).toHaveBeenCalledTimes(1); - const grantCall = vi.mocked(openid.authorizationCodeGrant).mock.calls[0]; - const callbackUrl = grantCall[1] as URL; - const checks = grantCall[2] as openid.AuthorizationCodeGrantChecks; - - expect(callbackUrl.toString()).toBe('https://app.example/auth/callback?code=abc&state=state-123'); - expect(checks).toEqual({ - idTokenExpected: true, - pkceCodeVerifier: 'pkce-verifier', - expectedState: 'state-123', - expectedNonce: 'nonce-123', - }); - expect(output.tokens.id_token).toBe('id'); - expect(output.tokenExchangeUrl).toBe('https://identity.example/oid/token-exchange'); - }); - - it('builds callback URL from query object when currentUrl is not provided', async () => { - const context = ssrOpenidContext( - 'client-1', - 'https://issuer.example', - 'https://app.example/auth/callback' - ); - - await context.handleAuthCallback( - { - query: { code: 'query-code', state: 'query-state' }, - pkceCodeVerifier: 'pkce-verifier', - expectedState: 'query-state', - }, - 'https://identity.example/oid/token-exchange' - ); - - const grantCall = vi.mocked(openid.authorizationCodeGrant).mock.calls[0]; - const callbackUrl = grantCall[1] as URL; - - expect(callbackUrl.toString()).toBe('https://app.example/auth/callback?code=query-code&state=query-state'); - }); - - it('passes optional client secret to discovery', async () => { - const context = ssrOpenidContext( - 'client-1', - 'https://issuer.example', - 'https://app.example/auth/callback', - 'client-secret-1' - ); - - await context.handleAuthCode(); - - expect(vi.mocked(openid.discovery)).toHaveBeenCalledTimes(1); - const call = vi.mocked(openid.discovery).mock.calls[0]; - expect((call[0] as URL).toString()).toBe('https://issuer.example/'); - expect(call[1]).toBe('client-1'); - expect(call[2]).toBe('client-secret-1'); - }); -}); From 48dcb0a50375e0140589a48fab5b573810ece369 Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 2 Mar 2026 11:48:55 +0100 Subject: [PATCH 18/20] refactor(tests): remove unused docker-build job from tests workflow --- .github/workflows/tests.yml | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d8edf64..5df27e3 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,17 +17,4 @@ concurrency: jobs: tests-and-linting: - uses: ./.github/workflows/code-test.yaml - - docker-build: - needs: tests-and-linting - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build Docker image - run: docker build -t image:test . + uses: ./.github/workflows/code-test.yaml \ No newline at end of file From 17777849909e1982489458e20ace10a62e6c427e Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 2 Mar 2026 11:50:04 +0100 Subject: [PATCH 19/20] refactor(tests): replace code-test workflow with inline steps for clarity --- .github/workflows/code-test.yaml | 31 ------------------------------- .github/workflows/tests.yml | 23 ++++++++++++++++++++++- 2 files changed, 22 insertions(+), 32 deletions(-) delete mode 100644 .github/workflows/code-test.yaml diff --git a/.github/workflows/code-test.yaml b/.github/workflows/code-test.yaml deleted file mode 100644 index 20fa9c5..0000000 --- a/.github/workflows/code-test.yaml +++ /dev/null @@ -1,31 +0,0 @@ -name: test-build-and-lint -on: workflow_call - -permissions: - contents: read - -jobs: - pull-request-checker: - runs-on: ubuntu-latest - timeout-minutes: 5 - - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Install Node.js - uses: actions/setup-node@v4 - with: - node-version: '24' - - - name: Install dependencies - run: npm install - - - name: Run linter (ESLint) - run: npm run lint - - - name: Build project - run: npm run build - - - name: Run tests - run: npm run test diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5df27e3..0e548e4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,4 +17,25 @@ concurrency: jobs: tests-and-linting: - uses: ./.github/workflows/code-test.yaml \ No newline at end of file + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + + - name: Install dependencies + run: npm install + + - name: Run linter (ESLint) + run: npm run lint + + - name: Build project + run: npm run build + + - name: Run tests + run: npm run test From 22ad10f4d63ec791085170fc1b2e6a7cf0255912 Mon Sep 17 00:00:00 2001 From: Lukas Date: Mon, 2 Mar 2026 11:55:23 +0100 Subject: [PATCH 20/20] refactor(workflows): remove obsolete testing-push workflow --- .github/workflows/testing-push.yaml | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 .github/workflows/testing-push.yaml diff --git a/.github/workflows/testing-push.yaml b/.github/workflows/testing-push.yaml deleted file mode 100644 index d0a63b3..0000000 --- a/.github/workflows/testing-push.yaml +++ /dev/null @@ -1,8 +0,0 @@ -name: testing-push-workflow -on: - push: - branches: - - test/* -jobs: - test: - uses: ./.github/workflows/integration-test.yaml \ No newline at end of file