From ad303f3924c740502bc83cacf27b877912da31db Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Sat, 27 Jun 2026 14:11:24 +0530 Subject: [PATCH 01/31] feat(svelte): scaffold package --- packages/svelte/.editorconfig | 1 + packages/svelte/.gitignore | 8 + packages/svelte/LICENSE | 1 + packages/svelte/README.md | 35 ++++ packages/svelte/eslint.config.js | 33 ++++ packages/svelte/package.json | 69 ++++++++ packages/svelte/prettier.config.js | 21 +++ packages/svelte/src/index.ts | 19 +++ packages/svelte/svelte.config.js | 23 +++ packages/svelte/tsconfig.json | 33 ++++ packages/svelte/tsconfig.lib.json | 11 ++ packages/svelte/vitest.config.ts | 25 +++ pnpm-lock.yaml | 264 ++++++++++++++++++++++++++++- pnpm-workspace.yaml | 4 + 14 files changed, 545 insertions(+), 2 deletions(-) create mode 100644 packages/svelte/.editorconfig create mode 100644 packages/svelte/.gitignore create mode 100644 packages/svelte/LICENSE create mode 100644 packages/svelte/README.md create mode 100644 packages/svelte/eslint.config.js create mode 100644 packages/svelte/package.json create mode 100644 packages/svelte/prettier.config.js create mode 100644 packages/svelte/src/index.ts create mode 100644 packages/svelte/svelte.config.js create mode 100644 packages/svelte/tsconfig.json create mode 100644 packages/svelte/tsconfig.lib.json create mode 100644 packages/svelte/vitest.config.ts diff --git a/packages/svelte/.editorconfig b/packages/svelte/.editorconfig new file mode 100644 index 0000000..1b3ce07 --- /dev/null +++ b/packages/svelte/.editorconfig @@ -0,0 +1 @@ +../../.editorconfig \ No newline at end of file diff --git a/packages/svelte/.gitignore b/packages/svelte/.gitignore new file mode 100644 index 0000000..556c98e --- /dev/null +++ b/packages/svelte/.gitignore @@ -0,0 +1,8 @@ +dist/ +node_modules/ +*.tsbuildinfo +.svelte-kit/ +.svelte-kit +coverage/ +.eslintcache +.pnpm-store/ diff --git a/packages/svelte/LICENSE b/packages/svelte/LICENSE new file mode 100644 index 0000000..30cff74 --- /dev/null +++ b/packages/svelte/LICENSE @@ -0,0 +1 @@ +../../LICENSE \ No newline at end of file diff --git a/packages/svelte/README.md b/packages/svelte/README.md new file mode 100644 index 0000000..3de2a54 --- /dev/null +++ b/packages/svelte/README.md @@ -0,0 +1,35 @@ +# @thunderid/svelte + +> **Svelte 5 SDK for [ThunderID](https://thunderid.dev)** — reactive auth with runes. + +## Installation + +```bash +pnpm add @thunderid/svelte +``` + +## Quick Start + +```svelte + + + + +

Welcome!

+ +
+ + + +
+``` + +## Documentation + +Coming soon. + +## License + +Apache-2.0 diff --git a/packages/svelte/eslint.config.js b/packages/svelte/eslint.config.js new file mode 100644 index 0000000..a70b578 --- /dev/null +++ b/packages/svelte/eslint.config.js @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import thunderIdPlugin from '@thunderid/eslint-plugin'; + +export default [ + { + ignores: [ + 'dist/**', + 'build/**', + 'node_modules/**', + 'coverage/**', + '.svelte-kit/**', + 'vitest.config.ts', + ], + }, + ...thunderIdPlugin.configs.vue, +]; diff --git a/packages/svelte/package.json b/packages/svelte/package.json new file mode 100644 index 0000000..dbb355f --- /dev/null +++ b/packages/svelte/package.json @@ -0,0 +1,69 @@ +{ + "name": "@thunderid/svelte", + "version": "0.1.0", + "description": "Svelte SDK for ThunderID", + "keywords": [ + "thunderid", + "svelte", + "sveltejs", + "svelteauth", + "spa" + ], + "homepage": "https://github.com/thunder-id/javascript-sdks/tree/main/packages/svelte#readme", + "bugs": { + "url": "https://github.com/thunder-id/thunderid/issues" + }, + "author": "WSO2", + "license": "Apache-2.0", + "type": "module", + "svelte": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "svelte": "./dist/index.js", + "default": "./dist/index.js" + } + }, + "files": [ + "dist", + "README.md", + "LICENSE" + ], + "scripts": { + "build": "pnpm clean:dist && svelte-package -i src -o dist", + "clean": "pnpm clean:node_modules && pnpm clean:dist", + "clean:dist": "rimraf dist", + "clean:node_modules": "rimraf node_modules", + "format:check": "prettier --check --cache .", + "format:fix": "prettier --write --cache .", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "test": "vitest --passWithNoTests", + "typecheck": "svelte-check --tsconfig ./tsconfig.lib.json" + }, + "devDependencies": { + "@sveltejs/package": "catalog:", + "@sveltejs/vite-plugin-svelte": "catalog:", + "@thunderid/eslint-plugin": "catalog:", + "@thunderid/prettier-config": "catalog:", + "@types/node": "catalog:", + "eslint": "catalog:", + "prettier": "catalog:", + "rimraf": "catalog:", + "svelte": "catalog:", + "svelte-check": "catalog:", + "typescript": "catalog:", + "vitest": "catalog:" + }, + "peerDependencies": { + "svelte": ">=5.0.0" + }, + "dependencies": { + "@thunderid/browser": "workspace:^", + "tslib": "catalog:" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/svelte/prettier.config.js b/packages/svelte/prettier.config.js new file mode 100644 index 0000000..7e978e9 --- /dev/null +++ b/packages/svelte/prettier.config.js @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import config from '@thunderid/prettier-config'; + +export default config; diff --git a/packages/svelte/src/index.ts b/packages/svelte/src/index.ts new file mode 100644 index 0000000..1fbd589 --- /dev/null +++ b/packages/svelte/src/index.ts @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export const __VERSION__ = '0.1.0'; diff --git a/packages/svelte/svelte.config.js b/packages/svelte/svelte.config.js new file mode 100644 index 0000000..bc82cc5 --- /dev/null +++ b/packages/svelte/svelte.config.js @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {vitePreprocess} from '@sveltejs/vite-plugin-svelte'; + +export default { + preprocess: vitePreprocess(), +}; diff --git a/packages/svelte/tsconfig.json b/packages/svelte/tsconfig.json new file mode 100644 index 0000000..311d020 --- /dev/null +++ b/packages/svelte/tsconfig.json @@ -0,0 +1,33 @@ +{ + "compileOnSave": false, + "compilerOptions": { + "declaration": false, + "emitDecoratorMetadata": true, + "esModuleInterop": true, + "experimentalDecorators": true, + "importHelpers": true, + "jsx": "preserve", + "lib": ["ESNext", "DOM"], + "module": "ESNext", + "moduleResolution": "bundler", + "skipLibCheck": true, + "skipDefaultLibCheck": true, + "sourceMap": true, + "target": "ESNext", + "forceConsistentCasingInFileNames": true, + "strict": false, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "strictNullChecks": true + }, + "exclude": ["node_modules", "tmp", "dist"], + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ] +} diff --git a/packages/svelte/tsconfig.lib.json b/packages/svelte/tsconfig.lib.json new file mode 100644 index 0000000..e3443f2 --- /dev/null +++ b/packages/svelte/tsconfig.lib.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "outDir": "dist", + "declarationDir": "dist", + "types": ["node", "svelte"] + }, + "include": ["src/**/*.js", "src/**/*.ts", "src/**/*.svelte"] +} diff --git a/packages/svelte/vitest.config.ts b/packages/svelte/vitest.config.ts new file mode 100644 index 0000000..bac935f --- /dev/null +++ b/packages/svelte/vitest.config.ts @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {defineConfig} from 'vitest/config'; + +export default defineConfig({ + test: { + passWithNoTests: true, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3c1fec5..ec9defb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,6 +15,12 @@ catalogs: '@playwright/test': specifier: 1.60.0 version: 1.60.0 + '@sveltejs/package': + specifier: 2.5.8 + version: 2.5.8 + '@sveltejs/vite-plugin-svelte': + specifier: 6.2.4 + version: 6.2.4 '@testing-library/react': specifier: 16.3.0 version: 16.3.0 @@ -99,6 +105,12 @@ catalogs: stream-browserify: specifier: 3.0.0 version: 3.0.0 + svelte: + specifier: 5.56.4 + version: 5.56.4 + svelte-check: + specifier: 4.7.1 + version: 4.7.1 tslib: specifier: 2.8.1 version: 2.8.1 @@ -575,6 +587,52 @@ importers: specifier: 'catalog:' version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + packages/svelte: + dependencies: + '@thunderid/browser': + specifier: workspace:^ + version: link:../browser + tslib: + specifier: 'catalog:' + version: 2.8.1 + devDependencies: + '@sveltejs/package': + specifier: 'catalog:' + version: 2.5.8(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3) + '@sveltejs/vite-plugin-svelte': + specifier: 'catalog:' + version: 6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@thunderid/eslint-plugin': + specifier: 'catalog:' + version: 0.0.2(@typescript-eslint/utils@8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)(vitest@4.1.8)(vue-eslint-parser@10.4.1(eslint@9.39.4(jiti@2.7.0))) + '@thunderid/prettier-config': + specifier: 'catalog:' + version: 0.0.2 + '@types/node': + specifier: 'catalog:' + version: 24.7.2 + eslint: + specifier: 'catalog:' + version: 9.39.4(jiti@2.7.0) + prettier: + specifier: 'catalog:' + version: 3.6.2 + rimraf: + specifier: 'catalog:' + version: 6.1.3 + svelte: + specifier: 'catalog:' + version: 5.56.4(@typescript-eslint/types@8.61.1) + svelte-check: + specifier: 'catalog:' + version: 4.7.1(picomatch@4.0.4)(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3) + typescript: + specifier: 'catalog:' + version: 5.9.3 + vitest: + specifier: 'catalog:' + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + packages/tanstack-router: dependencies: tslib: @@ -2811,6 +2869,37 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@sveltejs/acorn-typescript@1.0.10': + resolution: {integrity: sha512-4WfKk68eTih+MiJD4fSbxN7E8kVBmTMPWHUPYjvl2N0rMs53YLTT8/YjKU5Dtnz5LqDjl7LEw4U7lXR2W3J5WA==} + peerDependencies: + acorn: ^8.9.0 + + '@sveltejs/load-config@0.2.0': + resolution: {integrity: sha512-1LgZ/qUqSoq+QorD83lk2hka79Px0wXNW2q5V1nZlxGhQgw1jrsIbVz5YiCeucVLo4XvFLjXukUaQjIiqowkcg==} + engines: {node: '>= 18.0.0'} + + '@sveltejs/package@2.5.8': + resolution: {integrity: sha512-zeBbsXYvHiBu56v4gJaGQoEHzg96w0E1j3dOMX8vo56s6vI5eQ57ZEZhudjwjnegnVitRRu5MrmhO0eNvaonIw==} + engines: {node: ^16.14 || >=18} + hasBin: true + peerDependencies: + svelte: ^3.44.0 || ^4.0.0 || ^5.0.0-next.1 + + '@sveltejs/vite-plugin-svelte-inspector@5.0.2': + resolution: {integrity: sha512-TZzRTcEtZffICSAoZGkPSl6Etsj2torOVrx6Uw0KpXxrec9Gg6jFWQ60Q3+LmNGfZSxHRCZL7vXVZIWmuV50Ig==} + engines: {node: ^20.19 || ^22.12 || >=24} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^6.0.0-next.0 + svelte: ^5.0.0 + vite: ^6.3.0 || ^7.0.0 + + '@sveltejs/vite-plugin-svelte@6.2.4': + resolution: {integrity: sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA==} + engines: {node: ^20.19 || ^22.12 || >=24} + peerDependencies: + svelte: ^5.0.0 + vite: ^6.3.0 || ^7.0.0 + '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} @@ -3464,6 +3553,10 @@ packages: aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + aria-query@5.3.1: + resolution: {integrity: sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==} + engines: {node: '>= 0.4'} + aria-query@5.3.2: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} @@ -3767,6 +3860,10 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + cluster-key-slot@1.1.1: resolution: {integrity: sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==} engines: {node: '>=0.10.0'} @@ -4016,6 +4113,9 @@ packages: decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + dedent-js@1.0.1: + resolution: {integrity: sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -4390,6 +4490,9 @@ packages: jiti: optional: true + esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + espree@10.4.0: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -4402,6 +4505,14 @@ packages: resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} + esrap@2.2.13: + resolution: {integrity: sha512-m8jH5hZgJE2RRUK/jjkGPcJEDAV+dYnZYFkosQaPTcE+Yw4xynXHOo6FUdwaWBtdR3b1MMa7wEDTSHeR2VWsGA==} + peerDependencies: + '@typescript-eslint/types': ^8.2.0 + peerDependenciesMeta: + '@typescript-eslint/types': + optional: true + esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} @@ -4959,6 +5070,9 @@ packages: is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -5169,6 +5283,9 @@ packages: resolution: {integrity: sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==} engines: {node: '>=14'} + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -5346,6 +5463,10 @@ packages: mocked-exports@0.1.1: resolution: {integrity: sha512-aF7yRQr/Q0O2/4pIXm6PZ5G+jAd7QS4Yu8m+WEeEHGnbo+7mE36CbLSDQiXYV8bVL3NfmdeqPJct0tUlnjVSnA==} + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + mrmime@2.0.1: resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} @@ -6144,6 +6265,10 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + safe-array-concat@1.1.4: resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} engines: {node: '>=0.4'} @@ -6474,6 +6599,24 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + svelte-check@4.7.1: + resolution: {integrity: sha512-FGUOmAqxXdN/H9Zm8slrqO7SLtFisXRB7rfOsHNJ3MLTD2po/+Stg8XyErkpumPHbuUiYTcqrEIzxpVWKTLqtg==} + engines: {node: '>= 18.0.0'} + hasBin: true + peerDependencies: + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: '>=5.0.0' + + svelte2tsx@0.7.57: + resolution: {integrity: sha512-nQo0xEfUpSVjfkxan2UmC6Wl9UZVe9+/pglUiyBNMJ7KDQ9DDooucmXdToBOvzSfgYjj/kMYwf6CTTDz/z048w==} + peerDependencies: + svelte: ^3.55 || ^4.0.0-next.0 || ^4.0 || ^5.0.0-next.0 + typescript: ^4.9.4 || ^5.0.0 || ^6.0.0 + + svelte@5.56.4: + resolution: {integrity: sha512-/d0QHehmRuJW8gVz395MTkPcPozxzdjBMBE8oEYGz8O3b9KTMzzQ9ZHJQLuFKOHOPQbU6kx/X4iid/EBBzH7iw==} + engines: {node: '>=18'} + svgo@4.0.1: resolution: {integrity: sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==} engines: {node: '>=16'} @@ -6987,6 +7130,14 @@ packages: yaml: optional: true + vitefu@1.1.3: + resolution: {integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + vite: + optional: true + vitest-environment-nuxt@1.0.1: resolution: {integrity: sha512-eBCwtIQriXW5/M49FjqNKfnlJYlG2LWMSNFsRVKomc8CaMqmhQPBS5LZ9DlgYL9T8xIVsiA6RZn2lk7vxov3Ow==} @@ -7261,6 +7412,9 @@ packages: youch@4.1.1: resolution: {integrity: sha512-mxW3qiSnl+GRxXsaUMzv2Mbada1Y8CDltET9UxejDQe6DBYlSekghl5U5K0ReAikcHDi0G1vKZEmmo/NWAGKLA==} + zimmerframe@1.1.4: + resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} + zip-stream@6.0.1: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} engines: {node: '>= 14'} @@ -9171,6 +9325,40 @@ snapshots: '@standard-schema/spec@1.1.0': {} + '@sveltejs/acorn-typescript@1.0.10(acorn@8.17.0)': + dependencies: + acorn: 8.17.0 + + '@sveltejs/load-config@0.2.0': {} + + '@sveltejs/package@2.5.8(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3)': + dependencies: + chokidar: 5.0.0 + kleur: 4.1.5 + sade: 1.8.1 + semver: 7.8.5 + svelte: 5.56.4(@typescript-eslint/types@8.61.1) + svelte2tsx: 0.7.57(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3) + transitivePeerDependencies: + - typescript + + '@sveltejs/vite-plugin-svelte-inspector@5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + dependencies: + '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + obug: 2.1.3 + svelte: 5.56.4(@typescript-eslint/types@8.61.1) + vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + + '@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + deepmerge: 4.3.1 + magic-string: 0.30.21 + obug: 2.1.3 + svelte: 5.56.4(@typescript-eslint/types@8.61.1) + vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vitefu: 1.1.3(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 @@ -9349,8 +9537,7 @@ snapshots: '@types/http-errors': 2.0.5 '@types/node': 22.15.3 - '@types/trusted-types@2.0.7': - optional: true + '@types/trusted-types@2.0.7': {} '@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': dependencies: @@ -10046,6 +10233,8 @@ snapshots: dependencies: dequal: 2.0.3 + aria-query@5.3.1: {} + aria-query@5.3.2: {} array-buffer-byte-length@1.0.2: @@ -10397,6 +10586,8 @@ snapshots: clone@1.0.4: {} + clsx@2.1.1: {} + cluster-key-slot@1.1.1: {} color-convert@2.0.1: @@ -10625,6 +10816,8 @@ snapshots: decimal.js@10.6.0: {} + dedent-js@1.0.1: {} + deep-is@0.1.4: {} deepmerge@4.3.1: {} @@ -11166,6 +11359,8 @@ snapshots: transitivePeerDependencies: - supports-color + esm-env@1.2.2: {} + espree@10.4.0: dependencies: acorn: 8.17.0 @@ -11182,6 +11377,12 @@ snapshots: dependencies: estraverse: 5.3.0 + esrap@2.2.13(@typescript-eslint/types@8.61.1): + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + optionalDependencies: + '@typescript-eslint/types': 8.61.1 + esrecurse@4.3.0: dependencies: estraverse: 5.3.0 @@ -11761,6 +11962,10 @@ snapshots: dependencies: '@types/estree': 1.0.9 + is-reference@3.0.3: + dependencies: + '@types/estree': 1.0.9 + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -11982,6 +12187,8 @@ snapshots: pkg-types: 2.3.1 quansync: 0.2.11 + locate-character@3.0.0: {} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -12148,6 +12355,8 @@ snapshots: mocked-exports@0.1.1: {} + mri@1.2.0: {} + mrmime@2.0.1: {} ms@2.1.3: {} @@ -13368,6 +13577,10 @@ snapshots: dependencies: queue-microtask: 1.2.3 + sade@1.8.1: + dependencies: + mri: 1.2.0 + safe-array-concat@1.1.4: dependencies: call-bind: 1.0.9 @@ -13750,6 +13963,47 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + svelte-check@4.7.1(picomatch@4.0.4)(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@sveltejs/load-config': 0.2.0 + chokidar: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picocolors: 1.1.1 + sade: 1.8.1 + svelte: 5.56.4(@typescript-eslint/types@8.61.1) + typescript: 5.9.3 + transitivePeerDependencies: + - picomatch + + svelte2tsx@0.7.57(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3): + dependencies: + dedent-js: 1.0.1 + scule: 1.3.0 + svelte: 5.56.4(@typescript-eslint/types@8.61.1) + typescript: 5.9.3 + + svelte@5.56.4(@typescript-eslint/types@8.61.1): + dependencies: + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.5.5 + '@sveltejs/acorn-typescript': 1.0.10(acorn@8.17.0) + '@types/estree': 1.0.9 + '@types/trusted-types': 2.0.7 + acorn: 8.17.0 + aria-query: 5.3.1 + axobject-query: 4.1.0 + clsx: 2.1.1 + devalue: 5.8.1 + esm-env: 1.2.2 + esrap: 2.2.13(@typescript-eslint/types@8.61.1) + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.21 + zimmerframe: 1.1.4 + transitivePeerDependencies: + - '@typescript-eslint/types' + svgo@4.0.1: dependencies: commander: 11.1.0 @@ -14308,6 +14562,10 @@ snapshots: terser: 5.48.0 yaml: 2.9.0 + vitefu@1.1.3(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): + optionalDependencies: + vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vitest-environment-nuxt@1.0.1(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0): dependencies: '@nuxt/test-utils': 3.17.2(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0) @@ -14674,6 +14932,8 @@ snapshots: cookie-es: 3.1.1 youch-core: 0.3.3 + zimmerframe@1.1.4: {} + zip-stream@6.0.1: dependencies: archiver-utils: 5.0.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index b2a30d1..a2f80cc 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -45,6 +45,10 @@ catalog: rimraf: 6.1.3 rolldown: 1.0.0-beta.45 secure-random-bytes: 5.0.1 + svelte: 5.56.4 + '@sveltejs/package': 2.5.8 + '@sveltejs/vite-plugin-svelte': 6.2.4 + svelte-check: 4.7.1 stream-browserify: 3.0.0 tslib: 2.8.1 typescript: 5.9.3 From f59bb9e70f9d8cd7f6e312a8262d6387b6e95518 Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Sat, 27 Jun 2026 14:15:44 +0530 Subject: [PATCH 02/31] feat(svelte): add client, models, api wrappers, context keys --- packages/svelte/src/ThunderIDSvelteClient.ts | 442 ++++++++++++++++++ .../svelte/src/api/getAllOrganizations.ts | 62 +++ packages/svelte/src/api/getMeOrganizations.ts | 62 +++ packages/svelte/src/api/getSchemas.ts | 58 +++ packages/svelte/src/api/getScim2Me.ts | 58 +++ packages/svelte/src/context.ts | 52 +++ packages/svelte/src/index.ts | 10 +- packages/svelte/src/models/config.ts | 21 + packages/svelte/src/models/contexts.ts | 85 ++++ 9 files changed, 849 insertions(+), 1 deletion(-) create mode 100644 packages/svelte/src/ThunderIDSvelteClient.ts create mode 100644 packages/svelte/src/api/getAllOrganizations.ts create mode 100644 packages/svelte/src/api/getMeOrganizations.ts create mode 100644 packages/svelte/src/api/getSchemas.ts create mode 100644 packages/svelte/src/api/getScim2Me.ts create mode 100644 packages/svelte/src/context.ts create mode 100644 packages/svelte/src/models/config.ts create mode 100644 packages/svelte/src/models/contexts.ts diff --git a/packages/svelte/src/ThunderIDSvelteClient.ts b/packages/svelte/src/ThunderIDSvelteClient.ts new file mode 100644 index 0000000..8d46ff6 --- /dev/null +++ b/packages/svelte/src/ThunderIDSvelteClient.ts @@ -0,0 +1,442 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + ThunderIDBrowserClient, + flattenUserSchema, + generateFlattenedUserProfile, + UserProfile, + User, + generateUserProfile, + SignUpOptions, + ThunderIDRuntimeError, + executeEmbeddedSignUpFlow, + executeEmbeddedSignInFlow, + Organization, + IdToken, + AllOrganizationsApiResponse, + extractUserClaimsFromIdToken, + TokenResponse, + HttpRequestConfig, + HttpResponse, + TokenExchangeRequestConfig, + isEmpty, + EmbeddedSignInFlowResponse, + EmbeddedSignInFlowStatus, + EmbeddedSignUpFlowStatus, + deriveOrganizationHandleFromBaseUrl, + StorageManager, +} from '@thunderid/browser'; +import getAllOrganizations from './api/getAllOrganizations'; +import getMeOrganizations from './api/getMeOrganizations'; +import getSchemas from './api/getSchemas'; +import getScim2Me from './api/getScim2Me'; +import type {ThunderIDSvelteConfig} from './models/config'; + +class ThunderIDSvelteClient extends ThunderIDBrowserClient { + private loadingState = false; + + constructor(instanceId = 0) { + super(instanceId); + } + + private setLoading(loading: boolean): void { + this.loadingState = loading; + } + + private async withLoading(operation: () => Promise): Promise { + this.setLoading(true); + try { + return await operation(); + } finally { + this.setLoading(false); + } + } + + override initialize(config: ThunderIDSvelteConfig): Promise { + let resolvedOrganizationHandle: string | undefined = config?.organizationHandle; + + if (!resolvedOrganizationHandle) { + resolvedOrganizationHandle = deriveOrganizationHandleFromBaseUrl(config?.baseUrl); + } + + return this.withLoading(async () => + super.initialize({...config, organizationHandle: resolvedOrganizationHandle} as unknown as T), + ); + } + + override reInitialize(config: Partial): Promise { + return this.withLoading(async () => { + try { + await super.reInitialize(config as Partial); + return true; + } catch (error) { + throw new ThunderIDRuntimeError( + `Failed to check if the client is initialized: ${error instanceof Error ? error.message : String(error)}`, + 'ThunderIDSvelteClient-reInitialize-RuntimeError-001', + 'svelte', + 'An error occurred while checking the initialization status of the client.', + ); + } + }); + } + + override async updateUserProfile(): Promise { + throw new Error('Not implemented'); + } + + override async getUser(options?: any): Promise { + try { + let baseUrl: string = options?.baseUrl; + + if (!baseUrl) { + const configData: any = await this.getStorageManager().getConfigData(); + baseUrl = configData?.baseUrl; + } + + const profile: User = await getScim2Me({baseUrl}); + const schemas: any = await getSchemas({baseUrl}); + + return generateUserProfile(profile, flattenUserSchema(schemas)); + } catch (error) { + return extractUserClaimsFromIdToken(await this.getDecodedIdToken()); + } + } + + override async getDecodedIdToken(sessionId?: string): Promise { + return await super.getDecodedIdToken(sessionId); + } + + override async getIdToken(): Promise { + return this.withLoading(async () => super.getIdToken()); + } + + override async getUserProfile(options?: any): Promise { + return this.withLoading(async () => { + try { + let baseUrl: string = options?.baseUrl; + + if (!baseUrl) { + const configData: any = await this.getStorageManager().getConfigData(); + baseUrl = configData?.baseUrl; + } + + const profile: User = await getScim2Me({baseUrl, instanceId: this.getInstanceId()}); + const schemas: any = await getSchemas({baseUrl, instanceId: this.getInstanceId()}); + + const processedSchemas: any = flattenUserSchema(schemas); + + return { + flattenedProfile: generateFlattenedUserProfile(profile, processedSchemas), + profile, + schemas: processedSchemas, + } as UserProfile; + } catch (error) { + return { + flattenedProfile: extractUserClaimsFromIdToken(await this.getDecodedIdToken()), + profile: extractUserClaimsFromIdToken(await this.getDecodedIdToken()), + schemas: [], + } as UserProfile; + } + }); + } + + override async getMyOrganizations(options?: any): Promise { + try { + let baseUrl: string = options?.baseUrl; + + if (!baseUrl) { + const configData: any = await this.getStorageManager().getConfigData(); + baseUrl = configData?.baseUrl; + } + + return await getMeOrganizations({baseUrl, instanceId: this.getInstanceId()}); + } catch (error) { + throw new ThunderIDRuntimeError( + `Failed to fetch the user's associated organizations: ${ + error instanceof Error ? error.message : String(error) + }`, + 'ThunderIDSvelteClient-getMyOrganizations-RuntimeError-001', + 'svelte', + 'An error occurred while fetching associated organizations of the signed-in user.', + ); + } + } + + override async getAllOrganizations(options?: any): Promise { + try { + let baseUrl: string = options?.baseUrl; + + if (!baseUrl) { + const configData: any = await this.getStorageManager().getConfigData(); + baseUrl = configData?.baseUrl; + } + + return await getAllOrganizations({baseUrl, instanceId: this.getInstanceId()}); + } catch (error) { + throw new ThunderIDRuntimeError( + `Failed to fetch all organizations: ${error instanceof Error ? error.message : String(error)}`, + 'ThunderIDSvelteClient-getAllOrganizations-RuntimeError-001', + 'svelte', + 'An error occurred while fetching all the organizations associated with the user.', + ); + } + } + + override async getCurrentOrganization(): Promise { + try { + return await this.withLoading(async () => { + const idToken: IdToken = await this.getDecodedIdToken(); + return { + id: idToken?.org_id ?? '', + name: idToken?.org_name ?? '', + orgHandle: idToken?.org_handle ?? '', + }; + }); + } catch (error) { + throw new ThunderIDRuntimeError( + `Failed to fetch the current organization: ${error instanceof Error ? error.message : String(error)}`, + 'ThunderIDSvelteClient-getCurrentOrganization-RuntimeError-001', + 'svelte', + 'An error occurred while fetching the current organization of the signed-in user.', + ); + } + } + + override async switchOrganization(organization: Organization): Promise { + return this.withLoading(async () => { + try { + const configData: any = await this.getStorageManager().getConfigData(); + const sourceInstanceId: number | undefined = configData?.organizationChain?.sourceInstanceId; + + if (!organization.id) { + throw new ThunderIDRuntimeError( + 'Organization ID is required for switching organizations', + 'svelte-ThunderIDSvelteClient-SwitchOrganizationError-001', + 'svelte', + 'The organization object must contain a valid ID to perform the organization switch.', + ); + } + + const exchangeConfig: TokenExchangeRequestConfig = { + attachToken: false, + data: { + client_id: '{{clientId}}', + grant_type: 'organization_switch', + scope: '{{scopes}}', + switching_organization: organization.id, + token: '{{accessToken}}', + }, + id: 'organization-switch', + returnsSession: true, + signInRequired: sourceInstanceId === undefined, + }; + + return (await super.exchangeToken(exchangeConfig as any)) as unknown as TokenResponse | Response; + } catch (error) { + throw new ThunderIDRuntimeError( + `Failed to switch organization: ${error.message || error}`, + 'svelte-ThunderIDSvelteClient-SwitchOrganizationError-003', + 'svelte', + 'An error occurred while switching to the specified organization. Please try again.', + ); + } + }); + } + + override isLoading(): boolean { + return this.loadingState || super.isLoading(); + } + + override async isInitialized(): Promise { + return super.isInitialized(); + } + + override async isSignedIn(): Promise { + return await super.isSignedIn(); + } + + override async exchangeToken(config: TokenExchangeRequestConfig): Promise { + return this.withLoading( + async () => (await super.exchangeToken(config as any)) as unknown as TokenResponse | Response, + ); + } + + override async signIn(...args: any[]): Promise { + return this.withLoading(async () => { + const arg1: any = args[0]; + const arg2: any = args[1]; + + if (typeof arg1 === 'object' && arg1 !== null && arg1.callOnlyOnRedirect === true) { + return undefined; + } + + if ( + typeof arg1 === 'object' && + arg1 !== null && + !isEmpty(arg1) && + ('executionId' in arg1 || 'applicationId' in arg1) + ) { + const configData: any = await this.getStorageManager().getConfigData(); + const authIdFromUrl: string | null = new URL(window.location.href).searchParams.get('authId'); + const authIdFromStorage: string | null = (await this.getStorageManager().getHybridDataParameter('authId')) as + | string + | null; + const authId: string = authIdFromUrl || authIdFromStorage || ''; + const baseUrl: string = configData?.baseUrl || ''; + + const response: EmbeddedSignInFlowResponse = await executeEmbeddedSignInFlow({ + authId, + baseUrl, + payload: arg1, + url: arg2?.url, + }); + + if ( + response && + typeof response === 'object' && + response.flowStatus === EmbeddedSignInFlowStatus.Complete && + response.assertion + ) { + const decodedAssertion: { + [key: string]: unknown; + exp?: number; + iat?: number; + scope?: string; + } = await this.decodeJwtToken<{ + [key: string]: unknown; + exp?: number; + iat?: number; + scope?: string; + }>(response.assertion); + + const createdAt: number = decodedAssertion.iat ? decodedAssertion.iat * 1000 : Date.now(); + const expiresIn: number = + decodedAssertion.exp && decodedAssertion.iat ? decodedAssertion.exp - decodedAssertion.iat : 3600; + + await this.setSession({ + access_token: response.assertion, + created_at: createdAt, + expires_in: expiresIn, + id_token: response.assertion, + scope: decodedAssertion.scope, + token_type: 'Bearer', + }); + + this.notifySignIn(extractUserClaimsFromIdToken(decodedAssertion as IdToken) as User); + } + + return response; + } + + return (await super.signIn(arg1))!; + }); + } + + override async signInSilently(options?: any): Promise { + return (await super.signInSilently(options as Record))!; + } + + override async signUp(...args: any[]): Promise { + const configData: any = await this.getStorageManager().getConfigData(); + const firstArg: any = args[0]; + const baseUrl: string = configData?.baseUrl || ''; + + const authIdFromUrl: string | null = new URL(window.location.href).searchParams.get('authId'); + const authIdFromStorage: string | null = (await this.getStorageManager().getHybridDataParameter('authId')) as + | string + | null; + const authId: string = authIdFromUrl || authIdFromStorage || ''; + + if (authIdFromUrl && !authIdFromStorage) { + await this.getStorageManager().setHybridDataParameter('authId', authIdFromUrl); + } + + const response: any = await executeEmbeddedSignUpFlow({ + authId, + baseUrl, + payload: typeof firstArg === 'object' && 'flowType' in firstArg ? {...firstArg, verbose: true} : firstArg, + }); + + if ( + response && + typeof response === 'object' && + response.flowStatus === EmbeddedSignUpFlowStatus.Complete && + response.assertion + ) { + const decodedAssertion: { + [key: string]: unknown; + exp?: number; + iat?: number; + scope?: string; + } = await this.decodeJwtToken<{ + [key: string]: unknown; + exp?: number; + iat?: number; + scope?: string; + }>(response.assertion); + + const createdAt: number = decodedAssertion.iat ? decodedAssertion.iat * 1000 : Date.now(); + const expiresIn: number = + decodedAssertion.exp && decodedAssertion.iat ? decodedAssertion.exp - decodedAssertion.iat : 3600; + + await this.setSession({ + access_token: response.assertion, + created_at: createdAt, + expires_in: expiresIn, + id_token: response.assertion, + scope: decodedAssertion.scope, + token_type: 'Bearer', + }); + + this.notifySignIn(extractUserClaimsFromIdToken(decodedAssertion as IdToken) as User); + } + + return response; + } + + async request(requestConfig?: HttpRequestConfig): Promise> { + return (await this.httpRequest(requestConfig!))!; + } + + async requestAll(requestConfigs?: HttpRequestConfig[]): Promise[]> { + return (await this.httpRequestAll(requestConfigs!))!; + } + + override async getAccessToken(sessionId?: string): Promise { + return super.getAccessToken(sessionId); + } + + override clearSession(sessionId?: string): void { + super.clearSession(sessionId); + } + + override async setSession(sessionData: Record, sessionId?: string): Promise { + return this.getStorageManager().setSessionData(sessionData, sessionId); + } + + override decodeJwtToken>(token: string): Promise { + return super.decodeJwtToken(token); + } + + public override getStorageManager(): StorageManager { + return super.getStorageManager(); + } +} + +export default ThunderIDSvelteClient; diff --git a/packages/svelte/src/api/getAllOrganizations.ts b/packages/svelte/src/api/getAllOrganizations.ts new file mode 100644 index 0000000..64938fd --- /dev/null +++ b/packages/svelte/src/api/getAllOrganizations.ts @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + HttpResponse, + FetchHttpClient, + HttpRequestConfig, + getAllOrganizations as baseGetAllOrganizations, + GetAllOrganizationsConfig as BaseGetAllOrganizationsConfig, + AllOrganizationsApiResponse, +} from '@thunderid/browser'; + +export interface GetAllOrganizationsConfig extends Omit { + fetcher?: (url: string, config: RequestInit) => Promise; + instanceId?: number; +} + +const getAllOrganizations = async ({ + fetcher, + instanceId = 0, + ...requestConfig +}: GetAllOrganizationsConfig): Promise => { + const defaultFetcher = async (url: string, config: RequestInit): Promise => { + const httpClient: FetchHttpClient = FetchHttpClient.getInstance(instanceId); + + const response: HttpResponse = await httpClient.request({ + headers: config.headers as Record, + method: config.method || 'GET', + url, + } as HttpRequestConfig); + + return { + json: () => Promise.resolve(response.data), + ok: response.status >= 200 && response.status < 300, + status: response.status, + statusText: response.statusText || '', + text: () => Promise.resolve(typeof response.data === 'string' ? response.data : JSON.stringify(response.data)), + } as Response; + }; + + return baseGetAllOrganizations({ + ...requestConfig, + fetcher: fetcher || defaultFetcher, + }); +}; + +export default getAllOrganizations; diff --git a/packages/svelte/src/api/getMeOrganizations.ts b/packages/svelte/src/api/getMeOrganizations.ts new file mode 100644 index 0000000..bbb05f7 --- /dev/null +++ b/packages/svelte/src/api/getMeOrganizations.ts @@ -0,0 +1,62 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + Organization, + HttpResponse, + FetchHttpClient, + HttpRequestConfig, + getMeOrganizations as baseGetMeOrganizations, + GetMeOrganizationsConfig as BaseGetMeOrganizationsConfig, +} from '@thunderid/browser'; + +export interface GetMeOrganizationsConfig extends Omit { + fetcher?: (url: string, config: RequestInit) => Promise; + instanceId?: number; +} + +const getMeOrganizations = async ({ + fetcher, + instanceId = 0, + ...requestConfig +}: GetMeOrganizationsConfig): Promise => { + const defaultFetcher = async (url: string, config: RequestInit): Promise => { + const httpClient: FetchHttpClient = FetchHttpClient.getInstance(instanceId); + + const response: HttpResponse = await httpClient.request({ + headers: config.headers as Record, + method: config.method || 'GET', + url, + } as HttpRequestConfig); + + return { + json: () => Promise.resolve(response.data), + ok: response.status >= 200 && response.status < 300, + status: response.status, + statusText: response.statusText || '', + text: () => Promise.resolve(typeof response.data === 'string' ? response.data : JSON.stringify(response.data)), + } as Response; + }; + + return baseGetMeOrganizations({ + ...requestConfig, + fetcher: fetcher || defaultFetcher, + }); +}; + +export default getMeOrganizations; diff --git a/packages/svelte/src/api/getSchemas.ts b/packages/svelte/src/api/getSchemas.ts new file mode 100644 index 0000000..365cd28 --- /dev/null +++ b/packages/svelte/src/api/getSchemas.ts @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + Schema, + HttpResponse, + FetchHttpClient, + HttpRequestConfig, + getSchemas as baseGetSchemas, + GetSchemasConfig as BaseGetSchemasConfig, +} from '@thunderid/browser'; + +export interface GetSchemasConfig extends Omit { + fetcher?: (url: string, config: RequestInit) => Promise; + instanceId?: number; +} + +const getSchemas = async ({fetcher, instanceId = 0, ...requestConfig}: GetSchemasConfig): Promise => { + const defaultFetcher = async (url: string, config: RequestInit): Promise => { + const httpClient: FetchHttpClient = FetchHttpClient.getInstance(instanceId); + + const response: HttpResponse = await httpClient.request({ + headers: config.headers as Record, + method: config.method || 'GET', + url, + } as HttpRequestConfig); + + return { + json: () => Promise.resolve(response.data), + ok: response.status >= 200 && response.status < 300, + status: response.status, + statusText: response.statusText || '', + text: () => Promise.resolve(typeof response.data === 'string' ? response.data : JSON.stringify(response.data)), + } as Response; + }; + + return baseGetSchemas({ + ...requestConfig, + fetcher: fetcher || defaultFetcher, + }); +}; + +export default getSchemas; diff --git a/packages/svelte/src/api/getScim2Me.ts b/packages/svelte/src/api/getScim2Me.ts new file mode 100644 index 0000000..8ddc5e7 --- /dev/null +++ b/packages/svelte/src/api/getScim2Me.ts @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + User, + HttpResponse, + FetchHttpClient, + HttpRequestConfig, + getScim2Me as baseGetScim2Me, + GetScim2MeConfig as BaseGetScim2MeConfig, +} from '@thunderid/browser'; + +export interface GetScim2MeConfig extends Omit { + fetcher?: (url: string, config: RequestInit) => Promise; + instanceId?: number; +} + +const getScim2Me = async ({fetcher, instanceId = 0, ...requestConfig}: GetScim2MeConfig): Promise => { + const defaultFetcher = async (url: string, config: RequestInit): Promise => { + const httpClient: FetchHttpClient = FetchHttpClient.getInstance(instanceId); + + const response: HttpResponse = await httpClient.request({ + headers: config.headers as Record, + method: config.method || 'GET', + url, + } as HttpRequestConfig); + + return { + json: () => Promise.resolve(response.data), + ok: response.status >= 200 && response.status < 300, + status: response.status, + statusText: response.statusText || '', + text: () => Promise.resolve(typeof response.data === 'string' ? response.data : JSON.stringify(response.data)), + } as Response; + }; + + return baseGetScim2Me({ + ...requestConfig, + fetcher: fetcher || defaultFetcher, + }); +}; + +export default getScim2Me; diff --git a/packages/svelte/src/context.ts b/packages/svelte/src/context.ts new file mode 100644 index 0000000..cec8042 --- /dev/null +++ b/packages/svelte/src/context.ts @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {getContext, setContext} from 'svelte'; +import type {ThunderIDContext, UserContextValue} from './models/contexts'; + +export const THUNDERID_KEY = Symbol('thunderid'); +export const USER_KEY = Symbol('thunderid-user'); + +export function setThunderIDContext(context: ThunderIDContext): void { + setContext(THUNDERID_KEY, context); +} + +export function getThunderIDContext(): ThunderIDContext { + const context = getContext(THUNDERID_KEY); + if (!context) { + throw new Error( + '[ThunderID] No ThunderID context found. Ensure you are inside a provider component.', + ); + } + return context; +} + +export function setUserContext(context: UserContextValue): void { + setContext(USER_KEY, context); +} + +export function getUserContext(): UserContextValue { + const context = getContext(USER_KEY); + if (!context) { + throw new Error( + '[ThunderID] useUser() was called outside of . ' + + 'Make sure to wrap your app with the ThunderID provider.', + ); + } + return context; +} diff --git a/packages/svelte/src/index.ts b/packages/svelte/src/index.ts index 1fbd589..326a64b 100644 --- a/packages/svelte/src/index.ts +++ b/packages/svelte/src/index.ts @@ -16,4 +16,12 @@ * under the License. */ -export const __VERSION__ = '0.1.0'; +// ── Client ── +export {default as ThunderIDSvelteClient} from './ThunderIDSvelteClient'; + +// ── Context ── +export {THUNDERID_KEY, USER_KEY} from './context'; + +// ── Models / Types ── +export type {ThunderIDSvelteConfig} from './models/config'; +export type {ThunderIDContext, UserContextValue} from './models/contexts'; diff --git a/packages/svelte/src/models/config.ts b/packages/svelte/src/models/config.ts new file mode 100644 index 0000000..98bd11e --- /dev/null +++ b/packages/svelte/src/models/config.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import type {ThunderIDBrowserConfig} from '@thunderid/browser'; + +export type ThunderIDSvelteConfig = ThunderIDBrowserConfig; diff --git a/packages/svelte/src/models/contexts.ts b/packages/svelte/src/models/contexts.ts new file mode 100644 index 0000000..80cd3f0 --- /dev/null +++ b/packages/svelte/src/models/contexts.ts @@ -0,0 +1,85 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import type { + AllOrganizationsApiResponse, + HttpRequestConfig, + HttpResponse, + IdToken, + Organization, + SignInOptions, + StorageManager, + TokenExchangeRequestConfig, + TokenResponse, + User, + UserProfile, +} from '@thunderid/browser'; +import type {ThunderIDSvelteConfig} from './config'; +import type ThunderIDSvelteClient from '../ThunderIDSvelteClient'; + +export interface ThunderIDContext { + afterSignInUrl: string | undefined; + applicationId: string | undefined; + baseUrl: string | undefined; + clearSession: (...args: any[]) => void; + clientId: string | undefined; + scopes: string | string[] | undefined; + + exchangeToken: (config: TokenExchangeRequestConfig) => Promise; + getAccessToken: () => Promise; + getDecodedIdToken: () => Promise; + getIdToken: () => Promise; + getStorageManager: () => StorageManager; + http: { + request: (requestConfig?: HttpRequestConfig) => Promise>; + requestAll: (requestConfigs?: HttpRequestConfig[]) => Promise[]>; + }; + + instanceId: number; + isInitialized: boolean; + isLoading: boolean; + isSignedIn: boolean; + + organization: Organization | null; + organizationHandle: string | undefined; + + reInitialize: (config: Partial) => Promise; + + signIn: (...args: any[]) => Promise; + signInOptions: SignInOptions | undefined; + signInSilently: (options?: SignInOptions) => Promise; + signInUrl: string | undefined; + signOut: (...args: any[]) => Promise; + signUp: (...args: any[]) => Promise; + signUpUrl: string | undefined; + storage: ThunderIDSvelteConfig['storage'] | undefined; + + switchOrganization: ThunderIDSvelteClient['switchOrganization']; + + user: User | null; + userProfile: UserProfile | null; +} + +export interface UserContextValue { + flattenedProfile: User | null; + onUpdateProfile: (payload: User) => void; + profile: UserProfile | null; + revalidateProfile: () => Promise; + schemas: any[] | null; + updateProfile: (requestConfig: any, sessionId?: string) => Promise; +} From 712c17f8f8661ec243f32ee2f010392329302573 Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Sat, 27 Jun 2026 14:24:27 +0530 Subject: [PATCH 03/31] feat(svelte): add reactive auth state with runes --- packages/svelte/src/state.svelte.ts | 32 +++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 packages/svelte/src/state.svelte.ts diff --git a/packages/svelte/src/state.svelte.ts b/packages/svelte/src/state.svelte.ts new file mode 100644 index 0000000..ab61289 --- /dev/null +++ b/packages/svelte/src/state.svelte.ts @@ -0,0 +1,32 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import type {Organization, User, UserProfile} from '@thunderid/browser'; + +class AuthState { + isSignedIn = $state(false); + isLoading = $state(true); + isInitialized = $state(false); + user: User | null = $state(null); + userProfile: UserProfile | null = $state(null); + organization: Organization | null = $state(null); + myOrganizations: Organization[] = $state([]); + resolvedBaseUrl = $state(''); +} + +export const authState = new AuthState(); From b0ed9f040adb52369b5e66cd59cfc25cf706e524 Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Sat, 27 Jun 2026 14:26:10 +0530 Subject: [PATCH 04/31] feat(svelte): add ThunderID provider component --- packages/svelte/src/ThunderID.svelte | 350 +++++++++++++++++++++++++++ packages/svelte/src/index.ts | 3 + 2 files changed, 353 insertions(+) create mode 100644 packages/svelte/src/ThunderID.svelte diff --git a/packages/svelte/src/ThunderID.svelte b/packages/svelte/src/ThunderID.svelte new file mode 100644 index 0000000..30beebf --- /dev/null +++ b/packages/svelte/src/ThunderID.svelte @@ -0,0 +1,350 @@ + + +{@render children?.()} diff --git a/packages/svelte/src/index.ts b/packages/svelte/src/index.ts index 326a64b..5c61fe1 100644 --- a/packages/svelte/src/index.ts +++ b/packages/svelte/src/index.ts @@ -16,6 +16,9 @@ * under the License. */ +// ── Components ── +export {default as ThunderID} from './ThunderID.svelte'; + // ── Client ── export {default as ThunderIDSvelteClient} from './ThunderIDSvelteClient'; From 44abb6fd3c4df3caa9c23677d3724c5518c00a86 Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Sat, 27 Jun 2026 18:07:04 +0530 Subject: [PATCH 05/31] feat(svelte): rewrite SDK with full SvelteKit SSR support BREAKING CHANGE: ThunderIDSvelteClient now extends ThunderIDNodeClient instead of ThunderIDBrowserClient. Provider accepts ssrData prop for SSR hydration. Sign-in/out are server-driven via redirect. Architecture: - ThunderIDSvelteClient: singleton, extends ThunderIDNodeClient - Server: session JWT utils, handle hook, load helper, route handlers - Provider: SSR-first, hydration from .data - Composables: delegate auth actions to server endpoints - Components: SignedIn/SignedOut/Loading control, SignIn/Out/Up buttons Session management uses jose HS256 cookies matching Nuxt/Next.js pattern. --- .github/workflows/release.yml | 63 +++ README.md | 1 + packages/svelte/package.json | 16 +- packages/svelte/src/ThunderID.svelte | 340 ++---------- packages/svelte/src/ThunderIDSvelteClient.ts | 520 ++++++------------ .../components/actions/SignInButton.svelte | 57 ++ .../components/actions/SignOutButton.svelte | 56 ++ .../components/actions/SignUpButton.svelte | 56 ++ .../src/components/control/Loading.svelte | 36 ++ .../src/components/control/SignedIn.svelte | 36 ++ .../src/components/control/SignedOut.svelte | 36 ++ .../svelte/src/composables/useThunderID.ts | 53 ++ packages/svelte/src/composables/useUser.ts | 38 ++ packages/svelte/src/index.ts | 11 + packages/svelte/src/models/config.ts | 39 +- packages/svelte/src/models/contexts.ts | 64 +-- packages/svelte/src/models/session.ts | 50 ++ packages/svelte/src/server/getClient.ts | 37 ++ packages/svelte/src/server/hooks.ts | 90 +++ packages/svelte/src/server/index.ts | 35 ++ packages/svelte/src/server/load.ts | 25 + packages/svelte/src/server/routes/callback.ts | 76 +++ packages/svelte/src/server/routes/index.ts | 21 + packages/svelte/src/server/routes/signin.ts | 45 ++ packages/svelte/src/server/routes/signout.ts | 41 ++ packages/svelte/src/server/session.ts | 208 +++++++ packages/svelte/src/state.svelte.ts | 2 +- pnpm-lock.yaml | 64 +++ pnpm-workspace.yaml | 1 + 29 files changed, 1406 insertions(+), 711 deletions(-) create mode 100644 packages/svelte/src/components/actions/SignInButton.svelte create mode 100644 packages/svelte/src/components/actions/SignOutButton.svelte create mode 100644 packages/svelte/src/components/actions/SignUpButton.svelte create mode 100644 packages/svelte/src/components/control/Loading.svelte create mode 100644 packages/svelte/src/components/control/SignedIn.svelte create mode 100644 packages/svelte/src/components/control/SignedOut.svelte create mode 100644 packages/svelte/src/composables/useThunderID.ts create mode 100644 packages/svelte/src/composables/useUser.ts create mode 100644 packages/svelte/src/models/session.ts create mode 100644 packages/svelte/src/server/getClient.ts create mode 100644 packages/svelte/src/server/hooks.ts create mode 100644 packages/svelte/src/server/index.ts create mode 100644 packages/svelte/src/server/load.ts create mode 100644 packages/svelte/src/server/routes/callback.ts create mode 100644 packages/svelte/src/server/routes/index.ts create mode 100644 packages/svelte/src/server/routes/signin.ts create mode 100644 packages/svelte/src/server/routes/signout.ts create mode 100644 packages/svelte/src/server/session.ts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ed6579b..493c0d4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -84,6 +84,12 @@ on: type: boolean default: false + sdk_svelte: + description: '@thunderid/svelte' + required: false + type: boolean + default: false + permissions: contents: write # IMPORTANT: DO NOT REMOVE. `id-token` is required for OIDC publishing. @@ -450,6 +456,63 @@ jobs: env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + release-svelte: + name: 🧡 Release @thunderid/svelte + needs: [validate, release-browser] + if: ${{ always() && needs.validate.result == 'success' && (needs.release-browser.result == 'success' || needs.release-browser.result == 'skipped') && github.event.inputs.sdk_svelte == 'true' }} + runs-on: ubuntu-latest + steps: + - name: 📥 Checkout Code + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + token: ${{ secrets.THUNDERID_AUTOMATION_BOT }} + + - name: 📦 Set up pnpm + uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0 + with: + version: latest + + - name: ⚙️ Set up Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: ${{ env.NODE_VERSION }} + registry-url: 'https://registry.npmjs.org' + cache: 'pnpm' + + - name: 📦 Install Dependencies + run: pnpm install --frozen-lockfile + + - name: 🏷️ Bump Version + id: bump + run: | + cd packages/svelte + pnpm version ${{ github.event.inputs.bump_type }} --no-git-tag-version + echo "version=$(node -p "require('./package.json').version")" >> $GITHUB_OUTPUT + + - name: 🔨 Build + run: pnpm nx run @thunderid/svelte:build + + - name: 📤 Commit, Tag & Push + run: | + git config user.name "${{ env.RELEASE_GIT_USER_NAME }}" + git config user.email "${{ env.RELEASE_GIT_USER_EMAIL }}" + git add packages/svelte/package.json + git diff --cached --quiet || git commit -m "[Release] @thunderid/svelte@${{ steps.bump.outputs.version }}" + for i in $(seq 1 5); do + git pull --rebase origin ${{ github.ref_name }} + git push origin HEAD:${{ github.ref_name }} && break + [ "$i" -eq 5 ] && exit 1 + sleep $((i * 3)) + done + git tag "sdk/svelte/v${{ steps.bump.outputs.version }}" + git push origin "sdk/svelte/v${{ steps.bump.outputs.version }}" + + - name: 📦 Publish to npm + run: pnpm publish --no-git-checks --access public --provenance + working-directory: packages/svelte + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + # ── Level 3: depends on react / node+react / browser+node+vue ────────────── release-react-router: name: 🔀 Release @thunderid/react-router diff --git a/README.md b/README.md index 122bc4e..a064d93 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ JavaScript SDKs for ThunderID. Provides authentication and user management for b | [`@thunderid/nextjs`](packages/nextjs) | ![npm](https://img.shields.io/npm/v/@thunderid/nextjs) | Next.js SDK | | [`@thunderid/react-router`](packages/react-router) | ![npm](https://img.shields.io/npm/v/@thunderid/react-router) | React Router integration | | [`@thunderid/vue`](packages/vue) | ![npm](https://img.shields.io/npm/v/@thunderid/vue) | Vue SDK | +| [`@thunderid/svelte`](packages/svelte) | ![npm](https://img.shields.io/npm/v/@thunderid/svelte) | Svelte SDK | | [`@thunderid/nuxt`](packages/nuxt) | ![npm](https://img.shields.io/npm/v/@thunderid/nuxt) | Nuxt SDK | | [`@thunderid/express`](packages/express) | ![npm](https://img.shields.io/npm/v/@thunderid/express) | Express.js SDK | | [`@thunderid/tanstack-router`](packages/tanstack-router) | ![npm](https://img.shields.io/npm/v/@thunderid/tanstack-router) | TanStack Router integration | diff --git a/packages/svelte/package.json b/packages/svelte/package.json index dbb355f..2c8fb25 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -1,13 +1,13 @@ { "name": "@thunderid/svelte", - "version": "0.1.0", - "description": "Svelte SDK for ThunderID", + "version": "0.2.0", + "description": "Svelte SDK for ThunderID — full SvelteKit SSR support", "keywords": [ "thunderid", "svelte", - "sveltejs", + "sveltekit", "svelteauth", - "spa" + "ssr" ], "homepage": "https://github.com/thunder-id/javascript-sdks/tree/main/packages/svelte#readme", "bugs": { @@ -23,6 +23,10 @@ "types": "./dist/index.d.ts", "svelte": "./dist/index.js", "default": "./dist/index.js" + }, + "./server": { + "types": "./dist/server/index.d.ts", + "default": "./dist/server/index.js" } }, "files": [ @@ -43,6 +47,7 @@ "typecheck": "svelte-check --tsconfig ./tsconfig.lib.json" }, "devDependencies": { + "@sveltejs/kit": "catalog:", "@sveltejs/package": "catalog:", "@sveltejs/vite-plugin-svelte": "catalog:", "@thunderid/eslint-plugin": "catalog:", @@ -57,10 +62,13 @@ "vitest": "catalog:" }, "peerDependencies": { + "@sveltejs/kit": ">=2.0.0", "svelte": ">=5.0.0" }, "dependencies": { "@thunderid/browser": "workspace:^", + "@thunderid/node": "workspace:^", + "jose": "catalog:", "tslib": "catalog:" }, "publishConfig": { diff --git a/packages/svelte/src/ThunderID.svelte b/packages/svelte/src/ThunderID.svelte index 30beebf..f9b63b6 100644 --- a/packages/svelte/src/ThunderID.svelte +++ b/packages/svelte/src/ThunderID.svelte @@ -17,40 +17,22 @@ * under the License. */ - import {onMount} from 'svelte'; - import { - ThunderIDRuntimeError, - extractUserClaimsFromIdToken, - hasAuthParamsInUrl, - hasCalledForThisInstanceInUrl, - type IdToken, - type Organization, - type SignInOptions, - type User, - type UserProfile, - } from '@thunderid/browser'; - import ThunderIDSvelteClient from './ThunderIDSvelteClient'; - import {setThunderIDContext} from './context'; + import type {ThunderIDSSRData} from './models/session'; + import type {ThunderIDContext, UserContextValue} from './models/contexts'; + import {setThunderIDContext, setUserContext} from './context'; import {authState} from './state.svelte'; - import type {ThunderIDSvelteConfig} from './models/config'; - import type {ThunderIDContext} from './models/contexts'; interface Props { children?: import('svelte').Snippet; afterSignInUrl?: string; afterSignOutUrl?: string; applicationId?: string; - baseUrl: string; - clientId: string; - instanceId?: number; - organizationChain?: object; - organizationHandle?: string; + baseUrl?: string; + clientId?: string; scopes?: string | string[]; - signInOptions?: SignInOptions; signInUrl?: string; signUpUrl?: string; - storage?: string; - syncSession?: boolean; + ssrData?: ThunderIDSSRData; } let { @@ -58,293 +40,77 @@ afterSignInUrl = undefined, afterSignOutUrl = undefined, applicationId = undefined, - baseUrl, - clientId, - instanceId = 0, - organizationChain = undefined, - organizationHandle = undefined, + baseUrl = undefined, + clientId = undefined, scopes = undefined, - signInOptions = undefined, - signInUrl = undefined, + signInUrl = '/api/auth/signin', signUpUrl = undefined, - storage = undefined, - syncSession = undefined, + ssrData = undefined, }: Props = $props(); - const client: ThunderIDSvelteClient = new ThunderIDSvelteClient(instanceId); - - let isUpdatingSession = false; - let signInCheckInterval: ReturnType | undefined; - let loadingCheckInterval: ReturnType | undefined; - - function buildConfig(): ThunderIDSvelteConfig { - const resolvedAfterSignInUrl: string = afterSignInUrl ?? window.location.origin; - const resolvedAfterSignOutUrl: string = afterSignOutUrl ?? window.location.origin; - - return { - afterSignInUrl: resolvedAfterSignInUrl, - afterSignOutUrl: resolvedAfterSignOutUrl, - applicationId, - baseUrl, - clientId, - organizationChain, - organizationHandle, - scopes, - signInOptions, - signInUrl, - signUpUrl, - storage, - syncSession, - } as ThunderIDSvelteConfig; - } - - function hasAuthParams(url: URL, urlAfterSignIn: string | undefined): boolean { - return ( - (hasAuthParamsInUrl() && - !!urlAfterSignIn && - new URL(url.origin + url.pathname).toString() === new URL(urlAfterSignIn).toString()) || - url.searchParams.get('error') !== null - ); - } - - async function updateSession(): Promise { - try { - isUpdatingSession = true; - authState.isLoading = true; - let resolvedBaseUrl: string = authState.resolvedBaseUrl; - - const decodedToken: IdToken = await client.getDecodedIdToken(); - - if ((decodedToken as any)?.['user_org']) { - resolvedBaseUrl = `${(await client.getConfiguration()).baseUrl}/o`; - authState.resolvedBaseUrl = resolvedBaseUrl; - } - - const claims: User = extractUserClaimsFromIdToken(decodedToken); - authState.user = claims; - const profileData: UserProfile = { - flattenedProfile: claims, - profile: claims, - schemas: [], - } as UserProfile; - authState.userProfile = profileData; - - const currentSignInStatus: boolean = await client.isSignedIn(); - authState.isSignedIn = currentSignInStatus; - } catch { - // silent - } finally { - isUpdatingSession = false; - authState.isLoading = client.isLoading(); - } + function hydrateFromSSR(data: ThunderIDSSRData): void { + authState.isSignedIn = data.isSignedIn; + authState.isInitialized = true; + authState.myOrganizations = data.myOrganizations; + authState.organization = data.organization; + authState.resolvedBaseUrl = data.resolvedBaseUrl ?? ''; + authState.user = data.user; + authState.userProfile = data.userProfile; + authState.isLoading = false; } - async function signIn(...args: any[]): Promise { - try { - isUpdatingSession = true; - authState.isLoading = true; - - return await client.signIn(...args); - } catch (error) { - throw new ThunderIDRuntimeError( - `Sign in failed: ${error instanceof Error ? error.message : String(JSON.stringify(error))}`, - 'thunderid-signIn-Error', - 'svelte', - 'An error occurred while trying to sign in.', - ); - } finally { - isUpdatingSession = false; - authState.isLoading = client.isLoading(); + $effect(() => { + if (ssrData) { + hydrateFromSSR(ssrData); } - } - - async function signOut(...args: any[]): Promise { - return client.signOut(...args); - } + }); - async function signUp(...args: any[]): Promise { - return client.signUp(...args); + async function signIn(..._args: any[]): Promise { + window.location.href = signInUrl; } - async function signInSilently(options?: SignInOptions): Promise { - try { - isUpdatingSession = true; - authState.isLoading = true; - return await client.signInSilently(options); - } catch (error) { - throw new ThunderIDRuntimeError( - `Error while signing in silently: ${error instanceof Error ? error.message : String(JSON.stringify(error))}`, - 'thunderid-signInSilently-Error', - 'svelte', - 'An error occurred while trying to sign in silently.', - ); - } finally { - isUpdatingSession = false; - authState.isLoading = client.isLoading(); - } + async function signOut(..._args: any[]): Promise { + window.location.href = '/api/auth/signout'; } - async function switchOrganization(organization: Organization): Promise { - try { - isUpdatingSession = true; - authState.isLoading = true; - const response: any = await client.switchOrganization(organization); - - if (await client.isSignedIn()) { - await updateSession(); - } - - return response; - } catch (error) { - throw new ThunderIDRuntimeError( - `Failed to switch organization: ${error instanceof Error ? error.message : String(JSON.stringify(error))}`, - 'thunderid-switchOrganization-Error', - 'svelte', - 'An error occurred while switching to the specified organization.', - ); - } finally { - isUpdatingSession = false; - authState.isLoading = client.isLoading(); + async function signUp(..._args: any[]): Promise { + if (signUpUrl) { + window.location.href = signUpUrl; } } const context: ThunderIDContext = { - afterSignInUrl, - applicationId, - baseUrl, - clearSession: async (...args: any[]): Promise => { - await client.clearSession(...args); - }, - clientId, - scopes, - exchangeToken: (config: any): Promise => client.exchangeToken(config), - getAccessToken: (): Promise => client.getAccessToken(), - getDecodedIdToken: (): Promise => client.getDecodedIdToken(), - getIdToken: (): Promise => client.getIdToken(), - getStorageManager: () => client.getStorageManager(), - http: { - request: (requestConfig?: any): Promise => client.request(requestConfig), - requestAll: (requestConfigs?: any[]): Promise => client.requestAll(requestConfigs), - }, - instanceId, - isInitialized: authState.isInitialized, - isLoading: authState.isLoading, - isSignedIn: authState.isSignedIn, - organization: authState.organization, - organizationHandle, - reInitialize: async (config: any): Promise => { - const result: boolean = await client.reInitialize(config); - return typeof result === 'boolean' ? result : true; - }, + get afterSignInUrl() { return afterSignInUrl; }, + get afterSignOutUrl() { return afterSignOutUrl; }, + get applicationId() { return applicationId; }, + get baseUrl() { return baseUrl; }, + get clientId() { return clientId; }, + get isInitialized() { return authState.isInitialized; }, + get isLoading() { return authState.isLoading; }, + get isSignedIn() { return authState.isSignedIn; }, + get myOrganizations() { return authState.myOrganizations; }, + get organization() { return authState.organization; }, + get organizationHandle() { return undefined; }, + get resolvedBaseUrl() { return authState.resolvedBaseUrl; }, + get scopes() { return scopes; }, + get signInUrl() { return signInUrl; }, + get signUpUrl() { return signUpUrl; }, + get user() { return authState.user; }, + get userProfile() { return authState.userProfile; }, signIn, - signInOptions, - signInSilently, - signInUrl, signOut, signUp, - signUpUrl, - storage: storage as ThunderIDSvelteConfig['storage'], - switchOrganization, - user: authState.user, - userProfile: authState.userProfile, }; setThunderIDContext(context); - onMount(async () => { - const config: ThunderIDSvelteConfig = buildConfig(); - await client.initialize(config); - - await client.getDiscoveryResponse(); - - const initializedConfig: any = await (client.getConfiguration() as any); - - if (initializedConfig?.baseUrl) { - sessionStorage.setItem('thunderid_base_url', initializedConfig.baseUrl); - } - - try { - const status: boolean = await client.isInitialized(); - authState.isInitialized = status; - } catch { - authState.isInitialized = false; - } - - await client.on('sign-in', async () => { - await updateSession(); - }); - - const alreadySignedIn: boolean = await client.isSignedIn(); - - if (alreadySignedIn) { - await updateSession(); - } else { - const currentUrl: URL = new URL(window.location.href); - const hasParams: boolean = - hasAuthParams(currentUrl, initializedConfig?.afterSignInUrl) && - hasCalledForThisInstanceInUrl(instanceId ?? 0, currentUrl.search); - - if (hasParams) { - try { - const urlParams: URLSearchParams = currentUrl.searchParams; - const code: string | null = urlParams.get('code'); - const executionIdFromUrl: string | null = urlParams.get('executionId'); - const storedExecutionId: string | null = sessionStorage.getItem('thunderid_execution_id'); - - if (code && !executionIdFromUrl && !storedExecutionId) { - await signIn(); - } - } catch (error) { - throw new ThunderIDRuntimeError( - `Sign in failed: ${error instanceof Error ? error.message : String(JSON.stringify(error))}`, - 'thunderid-signIn-Error', - 'svelte', - 'An error occurred while trying to sign in.', - ); - } - } - } - - try { - const status: boolean = await client.isSignedIn(); - authState.isSignedIn = status; - - if (!status) { - signInCheckInterval = setInterval(async () => { - const newStatus: boolean = await client.isSignedIn(); - if (newStatus) { - authState.isSignedIn = true; - if (signInCheckInterval) { - clearInterval(signInCheckInterval); - signInCheckInterval = undefined; - } - } - }, 1000); - } - } catch { - authState.isSignedIn = false; - } - - loadingCheckInterval = setInterval(() => { - if (isUpdatingSession) return; - - const currentUrl: URL = new URL(window.location.href); - if (!authState.isSignedIn && hasAuthParams(currentUrl, initializedConfig?.afterSignInUrl)) return; - - authState.isLoading = client.isLoading(); - }, 100); - }); + const userContext: UserContextValue = { + get profile() { return authState.userProfile; }, + get flattenedProfile() { return authState.user; }, + get schemas() { return authState.userProfile?.schemas ?? null; }, + }; - $effect(() => { - return () => { - if (signInCheckInterval) { - clearInterval(signInCheckInterval); - } - if (loadingCheckInterval) { - clearInterval(loadingCheckInterval); - } - }; - }); + setUserContext(userContext); {@render children?.()} diff --git a/packages/svelte/src/ThunderIDSvelteClient.ts b/packages/svelte/src/ThunderIDSvelteClient.ts index 8d46ff6..bb7ee9f 100644 --- a/packages/svelte/src/ThunderIDSvelteClient.ts +++ b/packages/svelte/src/ThunderIDSvelteClient.ts @@ -17,424 +17,212 @@ */ import { - ThunderIDBrowserClient, - flattenUserSchema, - generateFlattenedUserProfile, - UserProfile, - User, - generateUserProfile, - SignUpOptions, - ThunderIDRuntimeError, - executeEmbeddedSignUpFlow, - executeEmbeddedSignInFlow, - Organization, - IdToken, - AllOrganizationsApiResponse, - extractUserClaimsFromIdToken, - TokenResponse, - HttpRequestConfig, - HttpResponse, - TokenExchangeRequestConfig, - isEmpty, - EmbeddedSignInFlowResponse, - EmbeddedSignInFlowStatus, - EmbeddedSignUpFlowStatus, - deriveOrganizationHandleFromBaseUrl, - StorageManager, -} from '@thunderid/browser'; -import getAllOrganizations from './api/getAllOrganizations'; -import getMeOrganizations from './api/getMeOrganizations'; -import getSchemas from './api/getSchemas'; -import getScim2Me from './api/getScim2Me'; + ThunderIDNodeClient, + type AuthClientConfig, + type IdToken, + type Organization, + type Storage, + type TokenExchangeRequestConfig, + type TokenResponse, + type User, + type UserProfile, + getMeOrganizations, + MemoryCacheStore, +} from '@thunderid/node'; import type {ThunderIDSvelteConfig} from './models/config'; +import type {ThunderIDSessionPayload} from './models/session'; -class ThunderIDSvelteClient extends ThunderIDBrowserClient { - private loadingState = false; +class ThunderIDSvelteClient extends ThunderIDNodeClient> { + private static instance: ThunderIDSvelteClient; - constructor(instanceId = 0) { - super(instanceId); - } + public isInitialized = false; - private setLoading(loading: boolean): void { - this.loadingState = loading; + private constructor() { + super(); } - private async withLoading(operation: () => Promise): Promise { - this.setLoading(true); - try { - return await operation(); - } finally { - this.setLoading(false); + public static getInstance(): ThunderIDSvelteClient { + if (!ThunderIDSvelteClient.instance) { + ThunderIDSvelteClient.instance = new ThunderIDSvelteClient(); } + return ThunderIDSvelteClient.instance; } - override initialize(config: ThunderIDSvelteConfig): Promise { - let resolvedOrganizationHandle: string | undefined = config?.organizationHandle; - - if (!resolvedOrganizationHandle) { - resolvedOrganizationHandle = deriveOrganizationHandleFromBaseUrl(config?.baseUrl); + override async initialize(config: ThunderIDSvelteConfig, storage?: Storage): Promise { + if (this.isInitialized) { + return true; } - return this.withLoading(async () => - super.initialize({...config, organizationHandle: resolvedOrganizationHandle} as unknown as T), + const authConfig: AuthClientConfig = { + afterSignInUrl: config.afterSignInUrl ?? '/', + afterSignOutUrl: config.afterSignOutUrl ?? '/', + baseUrl: config.baseUrl!, + clientId: config.clientId!, + clientSecret: config.clientSecret ?? undefined, + enablePKCE: config.enablePKCE ?? true, + scopes: config.scopes ?? ['openid', 'profile'], + } as AuthClientConfig; + + const resolvedStorage: Storage = storage ?? new MemoryCacheStore(); + + const result: boolean = await super.initialize( + authConfig as unknown as AuthClientConfig, + resolvedStorage, ); + this.isInitialized = true; + return result; } - override reInitialize(config: Partial): Promise { - return this.withLoading(async () => { - try { - await super.reInitialize(config as Partial); - return true; - } catch (error) { - throw new ThunderIDRuntimeError( - `Failed to check if the client is initialized: ${error instanceof Error ? error.message : String(error)}`, - 'ThunderIDSvelteClient-reInitialize-RuntimeError-001', - 'svelte', - 'An error occurred while checking the initialization status of the client.', - ); - } - }); + override async reInitialize(config: Partial): Promise { + await super.reInitialize(config as any); + return true; } - override async updateUserProfile(): Promise { - throw new Error('Not implemented'); + async rehydrateSessionFromPayload(session: ThunderIDSessionPayload): Promise { + if (!this.isInitialized || !session?.sessionId || !session?.accessToken) { + return; + } + + const storageManager: any = this.getStorageManager(); + const iatSeconds: number = typeof session.iat === 'number' ? session.iat : Math.floor(Date.now() / 1000); + const expiresInSeconds: number = + typeof session.accessTokenExpiresAt === 'number' ? Math.max(0, session.accessTokenExpiresAt - iatSeconds) : 3600; + + await storageManager.setSessionData( + { + access_token: session.accessToken, + created_at: iatSeconds * 1000, + expires_in: String(expiresInSeconds || 3600), + id_token: session.idToken ?? '', + refresh_token: session.refreshToken ?? '', + scope: session.scopes ?? '', + session_state: '', + token_type: 'Bearer', + }, + session.sessionId, + ); } - override async getUser(options?: any): Promise { - try { - let baseUrl: string = options?.baseUrl; + override signIn(...args: any[]): Promise { + const arg0: unknown = args[0]; - if (!baseUrl) { - const configData: any = await this.getStorageManager().getConfigData(); - baseUrl = configData?.baseUrl; - } + if (typeof arg0 === 'object' && arg0 !== null && ('code' in arg0 || 'state' in arg0)) { + const payload: {code?: unknown; session_state?: unknown; state?: unknown} = arg0 as { + code?: unknown; + session_state?: unknown; + state?: unknown; + }; + const code: string | undefined = typeof payload.code === 'string' ? payload.code : undefined; + const sessionState: string | undefined = + typeof payload.session_state === 'string' ? payload.session_state : undefined; + const state: string | undefined = typeof payload.state === 'string' ? payload.state : undefined; + const extraParams: Record = {}; - const profile: User = await getScim2Me({baseUrl}); - const schemas: any = await getSchemas({baseUrl}); + if (code) extraParams['code'] = code; + if (sessionState) extraParams['session_state'] = sessionState; + if (state) extraParams['state'] = state; - return generateUserProfile(profile, flattenUserSchema(schemas)); - } catch (error) { - return extractUserClaimsFromIdToken(await this.getDecodedIdToken()); + return super.signIn(args[3], args[2], code, sessionState, state, extraParams); } - } - override async getDecodedIdToken(sessionId?: string): Promise { - return await super.getDecodedIdToken(sessionId); + return super.signIn(args[0], args[1], args[2], args[3], args[4], args[5]); } - override async getIdToken(): Promise { - return this.withLoading(async () => super.getIdToken()); + override async signOut(...args: any[]): Promise { + const configData: any = this.getStorageManager().getConfigData(); + return (configData?.afterSignOutUrl as string) || (configData?.afterSignInUrl as string) || '/'; } - override async getUserProfile(options?: any): Promise { - return this.withLoading(async () => { - try { - let baseUrl: string = options?.baseUrl; - - if (!baseUrl) { - const configData: any = await this.getStorageManager().getConfigData(); - baseUrl = configData?.baseUrl; - } - - const profile: User = await getScim2Me({baseUrl, instanceId: this.getInstanceId()}); - const schemas: any = await getSchemas({baseUrl, instanceId: this.getInstanceId()}); - - const processedSchemas: any = flattenUserSchema(schemas); - - return { - flattenedProfile: generateFlattenedUserProfile(profile, processedSchemas), - profile, - schemas: processedSchemas, - } as UserProfile; - } catch (error) { - return { - flattenedProfile: extractUserClaimsFromIdToken(await this.getDecodedIdToken()), - profile: extractUserClaimsFromIdToken(await this.getDecodedIdToken()), - schemas: [], - } as UserProfile; - } - }); + override async signUp(_options?: any): Promise { + return undefined; } - override async getMyOrganizations(options?: any): Promise { - try { - let baseUrl: string = options?.baseUrl; - - if (!baseUrl) { - const configData: any = await this.getStorageManager().getConfigData(); - baseUrl = configData?.baseUrl; - } - - return await getMeOrganizations({baseUrl, instanceId: this.getInstanceId()}); - } catch (error) { - throw new ThunderIDRuntimeError( - `Failed to fetch the user's associated organizations: ${ - error instanceof Error ? error.message : String(error) - }`, - 'ThunderIDSvelteClient-getMyOrganizations-RuntimeError-001', - 'svelte', - 'An error occurred while fetching associated organizations of the signed-in user.', - ); - } + public async getAuthorizeRequestUrl(customParams: Record, userId?: string): Promise { + return this.getSignInUrl(customParams, userId); } - override async getAllOrganizations(options?: any): Promise { - try { - let baseUrl: string = options?.baseUrl; - - if (!baseUrl) { - const configData: any = await this.getStorageManager().getConfigData(); - baseUrl = configData?.baseUrl; - } - - return await getAllOrganizations({baseUrl, instanceId: this.getInstanceId()}); - } catch (error) { - throw new ThunderIDRuntimeError( - `Failed to fetch all organizations: ${error instanceof Error ? error.message : String(error)}`, - 'ThunderIDSvelteClient-getAllOrganizations-RuntimeError-001', - 'svelte', - 'An error occurred while fetching all the organizations associated with the user.', - ); - } + override getUser(sessionId?: string): Promise { + return super.getUser(sessionId); } - override async getCurrentOrganization(): Promise { - try { - return await this.withLoading(async () => { - const idToken: IdToken = await this.getDecodedIdToken(); - return { - id: idToken?.org_id ?? '', - name: idToken?.org_name ?? '', - orgHandle: idToken?.org_handle ?? '', - }; - }); - } catch (error) { - throw new ThunderIDRuntimeError( - `Failed to fetch the current organization: ${error instanceof Error ? error.message : String(error)}`, - 'ThunderIDSvelteClient-getCurrentOrganization-RuntimeError-001', - 'svelte', - 'An error occurred while fetching the current organization of the signed-in user.', - ); - } - } - - override async switchOrganization(organization: Organization): Promise { - return this.withLoading(async () => { - try { - const configData: any = await this.getStorageManager().getConfigData(); - const sourceInstanceId: number | undefined = configData?.organizationChain?.sourceInstanceId; - - if (!organization.id) { - throw new ThunderIDRuntimeError( - 'Organization ID is required for switching organizations', - 'svelte-ThunderIDSvelteClient-SwitchOrganizationError-001', - 'svelte', - 'The organization object must contain a valid ID to perform the organization switch.', - ); - } - - const exchangeConfig: TokenExchangeRequestConfig = { - attachToken: false, - data: { - client_id: '{{clientId}}', - grant_type: 'organization_switch', - scope: '{{scopes}}', - switching_organization: organization.id, - token: '{{accessToken}}', - }, - id: 'organization-switch', - returnsSession: true, - signInRequired: sourceInstanceId === undefined, - }; - - return (await super.exchangeToken(exchangeConfig as any)) as unknown as TokenResponse | Response; - } catch (error) { - throw new ThunderIDRuntimeError( - `Failed to switch organization: ${error.message || error}`, - 'svelte-ThunderIDSvelteClient-SwitchOrganizationError-003', - 'svelte', - 'An error occurred while switching to the specified organization. Please try again.', - ); - } - }); + override getAccessToken(sessionId?: string): Promise { + return super.getAccessToken(sessionId); } - override isLoading(): boolean { - return this.loadingState || super.isLoading(); + override getDecodedIdToken(sessionId?: string, idToken?: string): Promise { + return super.getDecodedIdToken(sessionId, idToken); } - override async isInitialized(): Promise { - return super.isInitialized(); + override isSignedIn(sessionId?: string): Promise { + return super.isSignedIn(sessionId); } - override async isSignedIn(): Promise { - return await super.isSignedIn(); + override exchangeToken( + config: TokenExchangeRequestConfig, + sessionId?: string, + ): Promise { + return super.exchangeToken(config, sessionId) as unknown as Promise; } - override async exchangeToken(config: TokenExchangeRequestConfig): Promise { - return this.withLoading( - async () => (await super.exchangeToken(config as any)) as unknown as TokenResponse | Response, - ); + override async getUserProfile(sessionId: string): Promise { + const user: User = await this.getUser(sessionId); + return {flattenedProfile: user, profile: user, schemas: []}; } - override async signIn(...args: any[]): Promise { - return this.withLoading(async () => { - const arg1: any = args[0]; - const arg2: any = args[1]; - - if (typeof arg1 === 'object' && arg1 !== null && arg1.callOnlyOnRedirect === true) { - return undefined; - } - - if ( - typeof arg1 === 'object' && - arg1 !== null && - !isEmpty(arg1) && - ('executionId' in arg1 || 'applicationId' in arg1) - ) { - const configData: any = await this.getStorageManager().getConfigData(); - const authIdFromUrl: string | null = new URL(window.location.href).searchParams.get('authId'); - const authIdFromStorage: string | null = (await this.getStorageManager().getHybridDataParameter('authId')) as - | string - | null; - const authId: string = authIdFromUrl || authIdFromStorage || ''; - const baseUrl: string = configData?.baseUrl || ''; - - const response: EmbeddedSignInFlowResponse = await executeEmbeddedSignInFlow({ - authId, - baseUrl, - payload: arg1, - url: arg2?.url, - }); - - if ( - response && - typeof response === 'object' && - response.flowStatus === EmbeddedSignInFlowStatus.Complete && - response.assertion - ) { - const decodedAssertion: { - [key: string]: unknown; - exp?: number; - iat?: number; - scope?: string; - } = await this.decodeJwtToken<{ - [key: string]: unknown; - exp?: number; - iat?: number; - scope?: string; - }>(response.assertion); - - const createdAt: number = decodedAssertion.iat ? decodedAssertion.iat * 1000 : Date.now(); - const expiresIn: number = - decodedAssertion.exp && decodedAssertion.iat ? decodedAssertion.exp - decodedAssertion.iat : 3600; - - await this.setSession({ - access_token: response.assertion, - created_at: createdAt, - expires_in: expiresIn, - id_token: response.assertion, - scope: decodedAssertion.scope, - token_type: 'Bearer', - }); - - this.notifySignIn(extractUserClaimsFromIdToken(decodedAssertion as IdToken) as User); - } - - return response; + override async getCurrentOrganization(sessionId: string): Promise { + try { + const idToken: IdToken = await this.getDecodedIdToken(sessionId); + if (!idToken?.org_id) { + return null; } - - return (await super.signIn(arg1))!; - }); - } - - override async signInSilently(options?: any): Promise { - return (await super.signInSilently(options as Record))!; + return { + id: idToken.org_id, + name: idToken.org_name ?? '', + orgHandle: idToken.org_handle ?? '', + }; + } catch { + return null; + } } - override async signUp(...args: any[]): Promise { - const configData: any = await this.getStorageManager().getConfigData(); - const firstArg: any = args[0]; - const baseUrl: string = configData?.baseUrl || ''; - - const authIdFromUrl: string | null = new URL(window.location.href).searchParams.get('authId'); - const authIdFromStorage: string | null = (await this.getStorageManager().getHybridDataParameter('authId')) as - | string - | null; - const authId: string = authIdFromUrl || authIdFromStorage || ''; - - if (authIdFromUrl && !authIdFromStorage) { - await this.getStorageManager().setHybridDataParameter('authId', authIdFromUrl); - } + override async getMyOrganizations(sessionId: string): Promise { + const accessToken: string = await this.getAccessToken(sessionId); + const configData: any = this.getStorageManager().getConfigData(); + const baseUrl: string = (configData?.baseUrl ?? '') as string; - const response: any = await executeEmbeddedSignUpFlow({ - authId, + return getMeOrganizations({ baseUrl, - payload: typeof firstArg === 'object' && 'flowType' in firstArg ? {...firstArg, verbose: true} : firstArg, + headers: {Authorization: `Bearer ${accessToken}`}, }); - - if ( - response && - typeof response === 'object' && - response.flowStatus === EmbeddedSignUpFlowStatus.Complete && - response.assertion - ) { - const decodedAssertion: { - [key: string]: unknown; - exp?: number; - iat?: number; - scope?: string; - } = await this.decodeJwtToken<{ - [key: string]: unknown; - exp?: number; - iat?: number; - scope?: string; - }>(response.assertion); - - const createdAt: number = decodedAssertion.iat ? decodedAssertion.iat * 1000 : Date.now(); - const expiresIn: number = - decodedAssertion.exp && decodedAssertion.iat ? decodedAssertion.exp - decodedAssertion.iat : 3600; - - await this.setSession({ - access_token: response.assertion, - created_at: createdAt, - expires_in: expiresIn, - id_token: response.assertion, - scope: decodedAssertion.scope, - token_type: 'Bearer', - }); - - this.notifySignIn(extractUserClaimsFromIdToken(decodedAssertion as IdToken) as User); - } - - return response; - } - - async request(requestConfig?: HttpRequestConfig): Promise> { - return (await this.httpRequest(requestConfig!))!; } - async requestAll(requestConfigs?: HttpRequestConfig[]): Promise[]> { - return (await this.httpRequestAll(requestConfigs!))!; - } - - override async getAccessToken(sessionId?: string): Promise { - return super.getAccessToken(sessionId); - } - - override clearSession(sessionId?: string): void { - super.clearSession(sessionId); - } - - override async setSession(sessionData: Record, sessionId?: string): Promise { - return this.getStorageManager().setSessionData(sessionData, sessionId); - } - - override decodeJwtToken>(token: string): Promise { - return super.decodeJwtToken(token); - } + override async switchOrganization( + organization: Organization, + sessionId: string, + ): Promise { + if (!organization.id) { + throw new Error('Organization ID is required for switching organizations.'); + } - public override getStorageManager(): StorageManager { + const exchangeConfig: TokenExchangeRequestConfig = { + attachToken: false, + data: { + client_id: '{{clientId}}', + client_secret: '{{clientSecret}}', + grant_type: 'organization_switch', + scope: '{{scopes}}', + switching_organization: organization.id, + token: '{{accessToken}}', + }, + id: 'organization-switch', + returnsSession: true, + signInRequired: true, + }; + + return this.exchangeToken(exchangeConfig, sessionId); + } + + public override getStorageManager(): any { return super.getStorageManager(); } } diff --git a/packages/svelte/src/components/actions/SignInButton.svelte b/packages/svelte/src/components/actions/SignInButton.svelte new file mode 100644 index 0000000..6e65c7e --- /dev/null +++ b/packages/svelte/src/components/actions/SignInButton.svelte @@ -0,0 +1,57 @@ + + + diff --git a/packages/svelte/src/components/actions/SignOutButton.svelte b/packages/svelte/src/components/actions/SignOutButton.svelte new file mode 100644 index 0000000..97dc91a --- /dev/null +++ b/packages/svelte/src/components/actions/SignOutButton.svelte @@ -0,0 +1,56 @@ + + + diff --git a/packages/svelte/src/components/actions/SignUpButton.svelte b/packages/svelte/src/components/actions/SignUpButton.svelte new file mode 100644 index 0000000..0f17713 --- /dev/null +++ b/packages/svelte/src/components/actions/SignUpButton.svelte @@ -0,0 +1,56 @@ + + + diff --git a/packages/svelte/src/components/control/Loading.svelte b/packages/svelte/src/components/control/Loading.svelte new file mode 100644 index 0000000..7a8c1f1 --- /dev/null +++ b/packages/svelte/src/components/control/Loading.svelte @@ -0,0 +1,36 @@ + + +{#if tid.isLoading} + {@render children?.()} +{:else} + {@render fallback?.()} +{/if} diff --git a/packages/svelte/src/components/control/SignedIn.svelte b/packages/svelte/src/components/control/SignedIn.svelte new file mode 100644 index 0000000..e044cd7 --- /dev/null +++ b/packages/svelte/src/components/control/SignedIn.svelte @@ -0,0 +1,36 @@ + + +{#if tid.isSignedIn} + {@render children?.()} +{:else} + {@render fallback?.()} +{/if} diff --git a/packages/svelte/src/components/control/SignedOut.svelte b/packages/svelte/src/components/control/SignedOut.svelte new file mode 100644 index 0000000..df2dda9 --- /dev/null +++ b/packages/svelte/src/components/control/SignedOut.svelte @@ -0,0 +1,36 @@ + + +{#if !tid.isSignedIn} + {@render children?.()} +{:else} + {@render fallback?.()} +{/if} diff --git a/packages/svelte/src/composables/useThunderID.ts b/packages/svelte/src/composables/useThunderID.ts new file mode 100644 index 0000000..ff35252 --- /dev/null +++ b/packages/svelte/src/composables/useThunderID.ts @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {getThunderIDContext} from '../context'; +import {authState} from '../state.svelte'; +import type {ThunderIDContext} from '../models/contexts'; + +export function useThunderID(): ThunderIDContext { + const context = getThunderIDContext(); + + return { + ...context, + get isSignedIn() { + return authState.isSignedIn; + }, + get isLoading() { + return authState.isLoading; + }, + get isInitialized() { + return authState.isInitialized; + }, + get user() { + return authState.user; + }, + get userProfile() { + return authState.userProfile; + }, + get organization() { + return authState.organization; + }, + get myOrganizations() { + return authState.myOrganizations; + }, + get resolvedBaseUrl() { + return authState.resolvedBaseUrl; + }, + }; +} diff --git a/packages/svelte/src/composables/useUser.ts b/packages/svelte/src/composables/useUser.ts new file mode 100644 index 0000000..1bed2c9 --- /dev/null +++ b/packages/svelte/src/composables/useUser.ts @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {getUserContext} from '../context'; +import {authState} from '../state.svelte'; +import type {UserContextValue} from '../models/contexts'; + +export function useUser(): UserContextValue { + const context = getUserContext(); + + return { + ...context, + get profile() { + return authState.userProfile; + }, + get flattenedProfile() { + return authState.user; + }, + get schemas() { + return authState.userProfile?.schemas ?? null; + }, + }; +} diff --git a/packages/svelte/src/index.ts b/packages/svelte/src/index.ts index 5c61fe1..218e2a1 100644 --- a/packages/svelte/src/index.ts +++ b/packages/svelte/src/index.ts @@ -18,6 +18,12 @@ // ── Components ── export {default as ThunderID} from './ThunderID.svelte'; +export {default as SignedIn} from './components/control/SignedIn.svelte'; +export {default as SignedOut} from './components/control/SignedOut.svelte'; +export {default as Loading} from './components/control/Loading.svelte'; +export {default as SignInButton} from './components/actions/SignInButton.svelte'; +export {default as SignOutButton} from './components/actions/SignOutButton.svelte'; +export {default as SignUpButton} from './components/actions/SignUpButton.svelte'; // ── Client ── export {default as ThunderIDSvelteClient} from './ThunderIDSvelteClient'; @@ -25,6 +31,11 @@ export {default as ThunderIDSvelteClient} from './ThunderIDSvelteClient'; // ── Context ── export {THUNDERID_KEY, USER_KEY} from './context'; +// ── Composables ── +export {useThunderID} from './composables/useThunderID'; +export {useUser} from './composables/useUser'; + // ── Models / Types ── export type {ThunderIDSvelteConfig} from './models/config'; +export type {ThunderIDSSRData, ThunderIDSessionPayload} from './models/session'; export type {ThunderIDContext, UserContextValue} from './models/contexts'; diff --git a/packages/svelte/src/models/config.ts b/packages/svelte/src/models/config.ts index 98bd11e..329546f 100644 --- a/packages/svelte/src/models/config.ts +++ b/packages/svelte/src/models/config.ts @@ -16,6 +16,39 @@ * under the License. */ -import type {ThunderIDBrowserConfig} from '@thunderid/browser'; - -export type ThunderIDSvelteConfig = ThunderIDBrowserConfig; +export interface ThunderIDSvelteConfig { + /** URL to redirect to after sign-in (default: '/') */ + afterSignInUrl?: string; + /** URL to redirect to after sign-out (default: '/') */ + afterSignOutUrl?: string; + /** + * ThunderID application id (`spId`) — appended to the redirect-based sign-up + * URL when present. + */ + applicationId?: string; + /** Base URL of the ThunderID org tenant (e.g. https://localhost:8090) */ + baseUrl?: string; + /** OAuth2 Client ID */ + clientId?: string; + /** OAuth2 Client Secret (server-only, use THUNDERID_CLIENT_SECRET env var) */ + clientSecret?: string; + /** The auth method to use for the token request. */ + enablePKCE?: boolean; + /** OAuth2 scopes to request */ + scopes?: string | string[]; + /** Secret for signing session JWTs (use THUNDERID_SESSION_SECRET env var) */ + sessionSecret?: string; + /** Custom sign-in URL (overrides the default authorize endpoint URL) */ + signInUrl?: string; + /** Custom sign-up URL */ + signUpUrl?: string; + /** Controls which server-side data fetches to perform on every SSR request */ + preferences?: { + user?: { + /** Whether to fetch the user's organizations during SSR (default: true) */ + fetchOrganizations?: boolean; + /** Whether to fetch the SCIM2 user profile during SSR (default: true) */ + fetchUserProfile?: boolean; + }; + }; +} diff --git a/packages/svelte/src/models/contexts.ts b/packages/svelte/src/models/contexts.ts index 80cd3f0..76b9070 100644 --- a/packages/svelte/src/models/contexts.ts +++ b/packages/svelte/src/models/contexts.ts @@ -16,70 +16,34 @@ * under the License. */ -import type { - AllOrganizationsApiResponse, - HttpRequestConfig, - HttpResponse, - IdToken, - Organization, - SignInOptions, - StorageManager, - TokenExchangeRequestConfig, - TokenResponse, - User, - UserProfile, -} from '@thunderid/browser'; -import type {ThunderIDSvelteConfig} from './config'; -import type ThunderIDSvelteClient from '../ThunderIDSvelteClient'; +import type {Organization, User, UserProfile} from '@thunderid/node'; export interface ThunderIDContext { - afterSignInUrl: string | undefined; - applicationId: string | undefined; - baseUrl: string | undefined; - clearSession: (...args: any[]) => void; - clientId: string | undefined; - scopes: string | string[] | undefined; - - exchangeToken: (config: TokenExchangeRequestConfig) => Promise; - getAccessToken: () => Promise; - getDecodedIdToken: () => Promise; - getIdToken: () => Promise; - getStorageManager: () => StorageManager; - http: { - request: (requestConfig?: HttpRequestConfig) => Promise>; - requestAll: (requestConfigs?: HttpRequestConfig[]) => Promise[]>; - }; - - instanceId: number; + afterSignInUrl?: string; + afterSignOutUrl?: string; + applicationId?: string; + baseUrl?: string; + clientId?: string; isInitialized: boolean; isLoading: boolean; isSignedIn: boolean; - + myOrganizations: Organization[]; organization: Organization | null; - organizationHandle: string | undefined; - - reInitialize: (config: Partial) => Promise; + organizationHandle?: string; + resolvedBaseUrl: string; + scopes?: string | string[]; + signInUrl?: string; + signUpUrl?: string; + user: User | null; + userProfile: UserProfile | null; signIn: (...args: any[]) => Promise; - signInOptions: SignInOptions | undefined; - signInSilently: (options?: SignInOptions) => Promise; - signInUrl: string | undefined; signOut: (...args: any[]) => Promise; signUp: (...args: any[]) => Promise; - signUpUrl: string | undefined; - storage: ThunderIDSvelteConfig['storage'] | undefined; - - switchOrganization: ThunderIDSvelteClient['switchOrganization']; - - user: User | null; - userProfile: UserProfile | null; } export interface UserContextValue { flattenedProfile: User | null; - onUpdateProfile: (payload: User) => void; profile: UserProfile | null; - revalidateProfile: () => Promise; schemas: any[] | null; - updateProfile: (requestConfig: any, sessionId?: string) => Promise; } diff --git a/packages/svelte/src/models/session.ts b/packages/svelte/src/models/session.ts new file mode 100644 index 0000000..ac7be8c --- /dev/null +++ b/packages/svelte/src/models/session.ts @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import type {JWTPayload} from 'jose'; +import type {Organization, User, UserProfile} from '@thunderid/node'; + +/** + * Payload stored in the session JWT cookie. + */ +export interface ThunderIDSessionPayload extends JWTPayload { + accessToken: string; + accessTokenExpiresAt?: number; + exp: number; + iat: number; + idToken?: string; + organizationId?: string; + refreshToken?: string; + scopes: string; + sessionId: string; + sub: string; +} + +/** + * Full SSR payload resolved by the handle hook on each page request. + * Written to `event.locals.thunderid` and returned from `+layout.server.ts`. + */ +export interface ThunderIDSSRData { + isSignedIn: boolean; + myOrganizations: Organization[]; + organization: Organization | null; + resolvedBaseUrl: string | null; + session: ThunderIDSessionPayload | null; + user: User | null; + userProfile: UserProfile | null; +} diff --git a/packages/svelte/src/server/getClient.ts b/packages/svelte/src/server/getClient.ts new file mode 100644 index 0000000..e1b41d4 --- /dev/null +++ b/packages/svelte/src/server/getClient.ts @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import ThunderIDSvelteClient from '../ThunderIDSvelteClient'; +import type {ThunderIDSvelteConfig} from '../models/config'; + +let clientInitialized = false; + +export async function getClient(config: ThunderIDSvelteConfig): Promise { + const client: ThunderIDSvelteClient = ThunderIDSvelteClient.getInstance(); + + if (!clientInitialized) { + await client.initialize(config); + clientInitialized = true; + } + + return client; +} + +export function resetClient(): void { + clientInitialized = false; +} diff --git a/packages/svelte/src/server/hooks.ts b/packages/svelte/src/server/hooks.ts new file mode 100644 index 0000000..ea12245 --- /dev/null +++ b/packages/svelte/src/server/hooks.ts @@ -0,0 +1,90 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import type {Handle, RequestEvent} from '@sveltejs/kit'; +import type {ThunderIDSvelteConfig} from '../models/config'; +import type {ThunderIDSSRData, ThunderIDSessionPayload} from '../models/session'; +import ThunderIDSvelteClient from '../ThunderIDSvelteClient'; +import {getClient} from './getClient'; +import {verifySessionToken, getSessionCookieName} from './session'; + +export interface ThunderIDLocals { + thunderid: ThunderIDSSRData; +} + +export function createThunderIDHandle(config: ThunderIDSvelteConfig): Handle { + return async ({event, resolve}) => { + const client: ThunderIDSvelteClient = await getClient(config); + + const sessionCookie: string | undefined = event.cookies.get(getSessionCookieName()); + let session: ThunderIDSessionPayload | null = null; + + if (sessionCookie) { + try { + session = await verifySessionToken(sessionCookie, config.sessionSecret); + await client.rehydrateSessionFromPayload(session); + } catch { + event.cookies.delete(getSessionCookieName(), {path: '/'}); + session = null; + } + } + + const isSignedIn: boolean = session !== null && (await client.isSignedIn(session.sessionId)); + + let ssrData: ThunderIDSSRData; + + if (isSignedIn && session) { + const [user, userProfile, organization, myOrganizations] = await Promise.all([ + client.getUser(session.sessionId), + client.getUserProfile(session.sessionId), + client.getCurrentOrganization(session.sessionId), + config.preferences?.user?.fetchOrganizations !== false + ? client.getMyOrganizations(session.sessionId) + : Promise.resolve([]), + ]); + + ssrData = { + isSignedIn: true, + myOrganizations, + organization, + resolvedBaseUrl: config.baseUrl ?? null, + session, + user, + userProfile, + }; + } else { + ssrData = { + isSignedIn: false, + myOrganizations: [], + organization: null, + resolvedBaseUrl: null, + session: null, + user: null, + userProfile: null, + }; + } + + (event.locals as ThunderIDLocals).thunderid = ssrData; + + return resolve(event); + }; +} + +export function loadThunderID(event: RequestEvent): ThunderIDSSRData { + return (event.locals as ThunderIDLocals).thunderid; +} diff --git a/packages/svelte/src/server/index.ts b/packages/svelte/src/server/index.ts new file mode 100644 index 0000000..2333947 --- /dev/null +++ b/packages/svelte/src/server/index.ts @@ -0,0 +1,35 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export {createThunderIDHandle} from './hooks'; +export {loadThunderID} from './load'; +export {getClient, resetClient} from './getClient'; +export { + createSessionToken, + createTempSessionToken, + verifySessionToken, + verifyTempSessionToken, + issueSessionCookie, + getSessionCookieName, + getTempSessionCookieName, + getSessionCookieOptions, + getTempSessionCookieOptions, +} from './session'; +export {createSignInHandler, createCallbackHandler, createSignOutHandler} from './routes'; + +export type {ThunderIDLocals} from './hooks'; diff --git a/packages/svelte/src/server/load.ts b/packages/svelte/src/server/load.ts new file mode 100644 index 0000000..0a73561 --- /dev/null +++ b/packages/svelte/src/server/load.ts @@ -0,0 +1,25 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import type {RequestEvent} from '@sveltejs/kit'; +import type {ThunderIDSSRData} from '../models/session'; +import type {ThunderIDLocals} from './hooks'; + +export function loadThunderID(event: RequestEvent): ThunderIDSSRData { + return (event.locals as ThunderIDLocals).thunderid; +} diff --git a/packages/svelte/src/server/routes/callback.ts b/packages/svelte/src/server/routes/callback.ts new file mode 100644 index 0000000..7fb6e7f --- /dev/null +++ b/packages/svelte/src/server/routes/callback.ts @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import type {TokenResponse} from '@thunderid/node'; +import type {ThunderIDSvelteConfig} from '../../models/config'; +import ThunderIDSvelteClient from '../../ThunderIDSvelteClient'; +import {issueSessionCookie, verifyTempSessionToken, getTempSessionCookieName, getSessionCookieName} from '../session'; + +export function createCallbackHandler(config: ThunderIDSvelteConfig): (event: {url: URL; cookies: any}) => Promise { + return async (event) => { + const client: ThunderIDSvelteClient = ThunderIDSvelteClient.getInstance(); + + const tempCookie: string | undefined = event.cookies.get(getTempSessionCookieName()); + + if (!tempCookie) { + return new Response(null, {status: 302, headers: {Location: config.afterSignInUrl || '/'}}); + } + + let sessionId: string; + let returnTo: string | undefined; + + try { + const tempPayload = await verifyTempSessionToken(tempCookie, config.sessionSecret); + sessionId = tempPayload.sessionId; + returnTo = tempPayload.returnTo; + } catch { + event.cookies.delete(getTempSessionCookieName(), {path: '/'}); + return new Response(null, {status: 302, headers: {Location: config.afterSignInUrl || '/'}}); + } + + const code: string | null = event.url.searchParams.get('code'); + const state: string | null = event.url.searchParams.get('state'); + const sessionState: string | null = event.url.searchParams.get('session_state'); + + if (code) { + const tokenResponse: TokenResponse = (await client.signIn( + {code, session_state: sessionState ?? undefined, state: state ?? undefined}, + undefined, + sessionId, + )) as unknown as TokenResponse; + + if (tokenResponse.accessToken) { + const sessionPayload = await issueSessionCookie(event, sessionId, tokenResponse, config.sessionSecret); + + event.cookies.delete(getTempSessionCookieName(), {path: '/'}); + + return new Response(null, { + status: 302, + headers: {Location: returnTo || config.afterSignInUrl || '/'}, + }); + } + } + + const error: string | null = event.url.searchParams.get('error'); + if (error) { + return new Response(null, {status: 302, headers: {Location: `${config.afterSignInUrl || '/'}?error=${error}`}}); + } + + return new Response(null, {status: 302, headers: {Location: config.afterSignInUrl || '/'}}); + }; +} diff --git a/packages/svelte/src/server/routes/index.ts b/packages/svelte/src/server/routes/index.ts new file mode 100644 index 0000000..69b1205 --- /dev/null +++ b/packages/svelte/src/server/routes/index.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export {createSignInHandler} from './signin'; +export {createCallbackHandler} from './callback'; +export {createSignOutHandler} from './signout'; diff --git a/packages/svelte/src/server/routes/signin.ts b/packages/svelte/src/server/routes/signin.ts new file mode 100644 index 0000000..f4646eb --- /dev/null +++ b/packages/svelte/src/server/routes/signin.ts @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {generateSessionId} from '@thunderid/node'; +import type {ThunderIDSvelteConfig} from '../../models/config'; +import ThunderIDSvelteClient from '../../ThunderIDSvelteClient'; +import {createTempSessionToken, getTempSessionCookieName, getTempSessionCookieOptions} from '../session'; + +export function createSignInHandler(config: ThunderIDSvelteConfig): (event: {url: URL; cookies: any}) => Promise { + return async (event) => { + const client: ThunderIDSvelteClient = ThunderIDSvelteClient.getInstance(); + const sessionId: string = generateSessionId(); + + const returnTo: string = event.url.searchParams.get('returnTo') || config.afterSignInUrl || '/'; + + const authorizeUrl: string = await client.getAuthorizeRequestUrl( + {redirectUri: `${event.url.origin}/api/auth/callback`, scope: config.scopes}, + sessionId, + ); + + const tempToken: string = await createTempSessionToken(sessionId, config.sessionSecret, returnTo); + + event.cookies.set(getTempSessionCookieName(), tempToken, getTempSessionCookieOptions()); + + return new Response(null, { + status: 302, + headers: {Location: authorizeUrl}, + }); + }; +} diff --git a/packages/svelte/src/server/routes/signout.ts b/packages/svelte/src/server/routes/signout.ts new file mode 100644 index 0000000..3e1a106 --- /dev/null +++ b/packages/svelte/src/server/routes/signout.ts @@ -0,0 +1,41 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import type {ThunderIDSvelteConfig} from '../../models/config'; +import ThunderIDSvelteClient from '../../ThunderIDSvelteClient'; +import {getSessionCookieName} from '../session'; + +export function createSignOutHandler(config: ThunderIDSvelteConfig): (event: {cookies: any}) => Promise { + return async (event) => { + const client: ThunderIDSvelteClient = ThunderIDSvelteClient.getInstance(); + const redirectUrl: string = config.afterSignOutUrl || '/'; + + try { + await client.signOut(); + } catch { + // silent + } + + event.cookies.delete(getSessionCookieName(), {path: '/'}); + + return new Response(null, { + status: 302, + headers: {Location: redirectUrl}, + }); + }; +} diff --git a/packages/svelte/src/server/session.ts b/packages/svelte/src/server/session.ts new file mode 100644 index 0000000..dc82c4d --- /dev/null +++ b/packages/svelte/src/server/session.ts @@ -0,0 +1,208 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {CookieConfig} from '@thunderid/node'; +import type {IdToken, TokenResponse} from '@thunderid/node'; +import {SignJWT, jwtVerify} from 'jose'; +import type {ThunderIDSessionPayload} from '../models/session'; + +const DEFAULT_EXPIRY_SECONDS = 3600; + +function getSecret(sessionSecret?: string): Uint8Array { + const secret: string | undefined = sessionSecret || process.env['THUNDERID_SESSION_SECRET']; + + if (!secret) { + if (process.env['NODE_ENV'] === 'production') { + throw new Error( + '[thunderid] THUNDERID_SESSION_SECRET environment variable is required in production. ' + + 'Set it to a secure random string of at least 32 characters.', + ); + } + console.warn( + '[thunderid] Using default session secret for development. Set THUNDERID_SESSION_SECRET for production.', + ); + return new TextEncoder().encode('thunderid-dev-secret-not-for-production'); + } + + return new TextEncoder().encode(secret); +} + +export async function createSessionToken( + params: { + accessToken: string; + accessTokenExpiresAt?: number; + expirySeconds?: number; + idToken?: string; + organizationId?: string; + refreshToken?: string; + scopes: string; + sessionId: string; + userId: string; + }, + sessionSecret?: string, +): Promise { + const secret: Uint8Array = getSecret(sessionSecret); + + return new SignJWT({ + accessToken: params.accessToken, + accessTokenExpiresAt: params.accessTokenExpiresAt, + idToken: params.idToken, + organizationId: params.organizationId, + refreshToken: params.refreshToken, + scopes: params.scopes, + sessionId: params.sessionId, + type: 'session', + } as Omit) + .setProtectedHeader({alg: 'HS256'}) + .setSubject(params.userId) + .setIssuedAt() + .setExpirationTime(Date.now() / 1000 + (params.expirySeconds ?? DEFAULT_EXPIRY_SECONDS)) + .sign(secret); +} + +export async function createTempSessionToken( + sessionId: string, + sessionSecret?: string, + returnTo?: string, +): Promise { + const secret: Uint8Array = getSecret(sessionSecret); + + const payload: Record = { + sessionId, + type: 'temp', + }; + + if (returnTo) { + payload['returnTo'] = returnTo; + } + + return new SignJWT(payload).setProtectedHeader({alg: 'HS256'}).setIssuedAt().setExpirationTime('15m').sign(secret); +} + +export async function verifySessionToken( + token: string, + sessionSecret?: string, +): Promise { + const secret: Uint8Array = getSecret(sessionSecret); + const {payload} = await jwtVerify(token, secret); + return payload as ThunderIDSessionPayload; +} + +export async function verifyTempSessionToken( + token: string, + sessionSecret?: string, +): Promise<{returnTo?: string; sessionId: string}> { + const secret: Uint8Array = getSecret(sessionSecret); + const {payload} = await jwtVerify(token, secret); + + if (payload['type'] !== 'temp') { + throw new Error('Invalid token type: expected temp session'); + } + + return { + returnTo: payload['returnTo'] as string | undefined, + sessionId: payload['sessionId'] as string, + }; +} + +export function getSessionCookieName(): string { + return CookieConfig.SESSION_COOKIE_NAME; +} + +export function getTempSessionCookieName(): string { + return CookieConfig.TEMP_SESSION_COOKIE_NAME; +} + +export function getSessionCookieOptions(): { + httpOnly: boolean; + maxAge: number; + path: string; + sameSite: 'lax'; + secure: boolean; +} { + return { + httpOnly: true, + maxAge: DEFAULT_EXPIRY_SECONDS, + path: '/', + sameSite: 'lax' as const, + secure: process.env['NODE_ENV'] === 'production', + }; +} + +export function getTempSessionCookieOptions(): { + httpOnly: boolean; + maxAge: number; + path: string; + sameSite: 'lax'; + secure: boolean; +} { + return { + httpOnly: true, + maxAge: 15 * 60, + path: '/', + sameSite: 'lax' as const, + secure: process.env['NODE_ENV'] === 'production', + }; +} + +export async function issueSessionCookie( + event: {cookies: {set: (name: string, value: string, options: any) => void}}, + sessionId: string, + tokenResponse: TokenResponse, + sessionSecret?: string, +): Promise { + const {default: ThunderIDSvelteClient} = await import('../ThunderIDSvelteClient'); + const client = ThunderIDSvelteClient.getInstance(); + + const idToken: IdToken = await client.getDecodedIdToken(sessionId, tokenResponse.idToken); + + const userId: string = idToken.sub || sessionId; + const organizationId: string | undefined = (idToken['user_org'] || idToken['organization_id']) as string | undefined; + const expiresInSeconds: number = parseInt(tokenResponse.expiresIn ?? '3600', 10); + const accessTokenExpiresAt: number = + Math.floor(Date.now() / 1000) + (Number.isFinite(expiresInSeconds) ? expiresInSeconds : 3600); + + const sessionToken: string = await createSessionToken( + { + accessToken: tokenResponse.accessToken, + accessTokenExpiresAt, + idToken: tokenResponse.idToken || undefined, + organizationId, + refreshToken: tokenResponse.refreshToken || undefined, + scopes: tokenResponse.scope || '', + sessionId, + userId, + }, + sessionSecret, + ); + + event.cookies.set(getSessionCookieName(), sessionToken, getSessionCookieOptions()); + + return { + accessToken: tokenResponse.accessToken, + accessTokenExpiresAt, + exp: Math.floor(Date.now() / 1000) + expiresInSeconds, + iat: Math.floor(Date.now() / 1000), + idToken: tokenResponse.idToken || undefined, + organizationId, + refreshToken: tokenResponse.refreshToken || undefined, + scopes: tokenResponse.scope || '', + sessionId, + sub: userId, + } as ThunderIDSessionPayload; +} diff --git a/packages/svelte/src/state.svelte.ts b/packages/svelte/src/state.svelte.ts index ab61289..658e7e4 100644 --- a/packages/svelte/src/state.svelte.ts +++ b/packages/svelte/src/state.svelte.ts @@ -16,7 +16,7 @@ * under the License. */ -import type {Organization, User, UserProfile} from '@thunderid/browser'; +import type {Organization, User, UserProfile} from '@thunderid/node'; class AuthState { isSignedIn = $state(false); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ec9defb..c0f2df6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,6 +15,9 @@ catalogs: '@playwright/test': specifier: 1.60.0 version: 1.60.0 + '@sveltejs/kit': + specifier: 2.68.0 + version: 2.68.0 '@sveltejs/package': specifier: 2.5.8 version: 2.5.8 @@ -592,10 +595,19 @@ importers: '@thunderid/browser': specifier: workspace:^ version: link:../browser + '@thunderid/node': + specifier: workspace:^ + version: link:../node + jose: + specifier: 'catalog:' + version: 5.2.0 tslib: specifier: 'catalog:' version: 2.8.1 devDependencies: + '@sveltejs/kit': + specifier: 'catalog:' + version: 2.68.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@sveltejs/package': specifier: 'catalog:' version: 2.5.8(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3) @@ -2874,6 +2886,22 @@ packages: peerDependencies: acorn: ^8.9.0 + '@sveltejs/kit@2.68.0': + resolution: {integrity: sha512-PdKiWsqinAoubVsSiRgVFkg3MHzGhQPnwQ8VxnGQKpZYijpapZ3UHHBje0GeByt2TvfjHPw+kxV+dNK2RIZg9g==} + engines: {node: '>=18.13'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.0.0 + '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: ^5.3.3 || ^6.0.0 + vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + typescript: + optional: true + '@sveltejs/load-config@0.2.0': resolution: {integrity: sha512-1LgZ/qUqSoq+QorD83lk2hka79Px0wXNW2q5V1nZlxGhQgw1jrsIbVz5YiCeucVLo4XvFLjXukUaQjIiqowkcg==} engines: {node: '>= 18.0.0'} @@ -2981,6 +3009,9 @@ packages: peerDependencies: '@types/express': '*' + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} @@ -3951,6 +3982,10 @@ packages: resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} engines: {node: '>=6.6.0'} + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + cookie@0.7.2: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} @@ -6354,6 +6389,9 @@ packages: set-cookie-parser@2.7.2: resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + set-cookie-parser@3.1.1: + resolution: {integrity: sha512-vM9SUhjsUYs6UeJUmygc5Ofm5eQGe85riob5ju6XCgFGJI5PLV4nrDAQpQjd+LkFBpAkADn5BQQpZ9EUNkyLuA==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -9329,6 +9367,26 @@ snapshots: dependencies: acorn: 8.17.0 + '@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + dependencies: + '@standard-schema/spec': 1.1.0 + '@sveltejs/acorn-typescript': 1.0.10(acorn@8.17.0) + '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@types/cookie': 0.6.0 + acorn: 8.17.0 + cookie: 0.6.0 + devalue: 5.8.1 + esm-env: 1.2.2 + kleur: 4.1.5 + magic-string: 0.30.21 + mrmime: 2.0.1 + set-cookie-parser: 3.1.1 + sirv: 3.0.2 + svelte: 5.56.4(@typescript-eslint/types@8.61.1) + vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + optionalDependencies: + typescript: 5.9.3 + '@sveltejs/load-config@0.2.0': {} '@sveltejs/package@2.5.8(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3)': @@ -9479,6 +9537,8 @@ snapshots: dependencies: '@types/express': 5.0.6 + '@types/cookie@0.6.0': {} + '@types/deep-eql@4.0.2': {} '@types/esrecurse@4.3.1': {} @@ -10651,6 +10711,8 @@ snapshots: cookie-signature@1.2.2: {} + cookie@0.6.0: {} + cookie@0.7.2: {} cookie@1.1.1: {} @@ -13669,6 +13731,8 @@ snapshots: set-cookie-parser@2.7.2: {} + set-cookie-parser@3.1.1: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index a2f80cc..5a27764 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -46,6 +46,7 @@ catalog: rolldown: 1.0.0-beta.45 secure-random-bytes: 5.0.1 svelte: 5.56.4 + '@sveltejs/kit': 2.68.0 '@sveltejs/package': 2.5.8 '@sveltejs/vite-plugin-svelte': 6.2.4 svelte-check: 4.7.1 From 71cd5c12ec58263a2471cfba7d1b8d9937d01104 Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Sat, 27 Jun 2026 18:10:33 +0530 Subject: [PATCH 06/31] feat(svelte): add token refresh, org switch, route guard, type augmentation Gaps filled: - Token refresh: maybeRefreshToken() proactively refreshes expiring access tokens in hooks; getValidAccessToken() for consumer use - Org switch: createOrgSwitchHandler() POST handler for org switching - Route guard: requireServerSession() throws SvelteKit redirect(307) when unauthenticated, for use in +page.server.ts load functions - Type augmentation: app.d.ts augments App.Locals with thunderid: ThunderIDSSRData so event.locals.thunderid is typed without casts --- packages/svelte/src/server/app.d.ts | 27 ++++ packages/svelte/src/server/guard.ts | 51 +++++++ packages/svelte/src/server/hooks.ts | 16 +- packages/svelte/src/server/index.ts | 6 +- packages/svelte/src/server/load.ts | 3 +- packages/svelte/src/server/refresh.ts | 137 ++++++++++++++++++ packages/svelte/src/server/routes/index.ts | 1 + .../svelte/src/server/routes/orgSwitch.ts | 86 +++++++++++ 8 files changed, 315 insertions(+), 12 deletions(-) create mode 100644 packages/svelte/src/server/app.d.ts create mode 100644 packages/svelte/src/server/guard.ts create mode 100644 packages/svelte/src/server/refresh.ts create mode 100644 packages/svelte/src/server/routes/orgSwitch.ts diff --git a/packages/svelte/src/server/app.d.ts b/packages/svelte/src/server/app.d.ts new file mode 100644 index 0000000..393456b --- /dev/null +++ b/packages/svelte/src/server/app.d.ts @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import type {ThunderIDSSRData} from '../models/session'; + +declare global { + namespace App { + interface Locals { + thunderid: ThunderIDSSRData; + } + } +} diff --git a/packages/svelte/src/server/guard.ts b/packages/svelte/src/server/guard.ts new file mode 100644 index 0000000..a74efd5 --- /dev/null +++ b/packages/svelte/src/server/guard.ts @@ -0,0 +1,51 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {redirect} from '@sveltejs/kit'; +import type {RequestEvent} from '@sveltejs/kit'; +import type {ThunderIDSSRData, ThunderIDSessionPayload} from '../models/session'; + +/** + * Read the ThunderID SSR data from `event.locals` (set by `createThunderIDHandle`) + * and throw a SvelteKit redirect to the sign-in page if the user is not authenticated. + * + * Use in `+page.server.ts` or `+layout.server.ts` load functions to protect routes. + * + * @example + * ```ts + * // src/routes/dashboard/+page.server.ts + * import { requireServerSession } from '@thunderid/svelte/server'; + * + * export const load = (event) => { + * const session = requireServerSession(event); + * return { email: session.user?.email }; + * }; + * ``` + */ +export function requireServerSession( + event: RequestEvent, + redirectTo?: string, +): ThunderIDSSRData { + const ssrData: ThunderIDSSRData = event.locals.thunderid; + + if (!ssrData || !ssrData.isSignedIn) { + redirect(307, redirectTo || '/api/auth/signin'); + } + + return ssrData; +} diff --git a/packages/svelte/src/server/hooks.ts b/packages/svelte/src/server/hooks.ts index ea12245..28da3de 100644 --- a/packages/svelte/src/server/hooks.ts +++ b/packages/svelte/src/server/hooks.ts @@ -22,10 +22,7 @@ import type {ThunderIDSSRData, ThunderIDSessionPayload} from '../models/session' import ThunderIDSvelteClient from '../ThunderIDSvelteClient'; import {getClient} from './getClient'; import {verifySessionToken, getSessionCookieName} from './session'; - -export interface ThunderIDLocals { - thunderid: ThunderIDSSRData; -} +import {maybeRefreshToken} from './refresh'; export function createThunderIDHandle(config: ThunderIDSvelteConfig): Handle { return async ({event, resolve}) => { @@ -37,7 +34,12 @@ export function createThunderIDHandle(config: ThunderIDSvelteConfig): Handle { if (sessionCookie) { try { session = await verifySessionToken(sessionCookie, config.sessionSecret); - await client.rehydrateSessionFromPayload(session); + + session = await maybeRefreshToken(session, config, event); + + if (session) { + await client.rehydrateSessionFromPayload(session); + } } catch { event.cookies.delete(getSessionCookieName(), {path: '/'}); session = null; @@ -79,12 +81,12 @@ export function createThunderIDHandle(config: ThunderIDSvelteConfig): Handle { }; } - (event.locals as ThunderIDLocals).thunderid = ssrData; + event.locals.thunderid = ssrData; return resolve(event); }; } export function loadThunderID(event: RequestEvent): ThunderIDSSRData { - return (event.locals as ThunderIDLocals).thunderid; + return event.locals.thunderid; } diff --git a/packages/svelte/src/server/index.ts b/packages/svelte/src/server/index.ts index 2333947..ab02f92 100644 --- a/packages/svelte/src/server/index.ts +++ b/packages/svelte/src/server/index.ts @@ -30,6 +30,6 @@ export { getSessionCookieOptions, getTempSessionCookieOptions, } from './session'; -export {createSignInHandler, createCallbackHandler, createSignOutHandler} from './routes'; - -export type {ThunderIDLocals} from './hooks'; +export {maybeRefreshToken, getValidAccessToken} from './refresh'; +export {requireServerSession} from './guard'; +export {createSignInHandler, createCallbackHandler, createSignOutHandler, createOrgSwitchHandler} from './routes'; diff --git a/packages/svelte/src/server/load.ts b/packages/svelte/src/server/load.ts index 0a73561..29d2514 100644 --- a/packages/svelte/src/server/load.ts +++ b/packages/svelte/src/server/load.ts @@ -18,8 +18,7 @@ import type {RequestEvent} from '@sveltejs/kit'; import type {ThunderIDSSRData} from '../models/session'; -import type {ThunderIDLocals} from './hooks'; export function loadThunderID(event: RequestEvent): ThunderIDSSRData { - return (event.locals as ThunderIDLocals).thunderid; + return event.locals.thunderid; } diff --git a/packages/svelte/src/server/refresh.ts b/packages/svelte/src/server/refresh.ts new file mode 100644 index 0000000..fd78f27 --- /dev/null +++ b/packages/svelte/src/server/refresh.ts @@ -0,0 +1,137 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import type {RequestEvent} from '@sveltejs/kit'; +import type {ThunderIDSvelteConfig} from '../models/config'; +import type {ThunderIDSessionPayload} from '../models/session'; +import {createSessionToken, getSessionCookieName, getSessionCookieOptions} from './session'; + +const REFRESH_SKEW_SECONDS = 60; + +interface OIDCTokenRefreshResponse { + access_token: string; + expires_in?: number; + id_token?: string; + refresh_token?: string; + scope?: string; + token_type?: string; +} + +/** + * Attempt to refresh the access token when it is within the skew window of + * expiry. Returns the (potentially refreshed) session payload, or null when + * the refresh fails. + */ +export async function maybeRefreshToken( + session: ThunderIDSessionPayload, + config: ThunderIDSvelteConfig, + event: Pick, +): Promise { + const now: number = Math.floor(Date.now() / 1000); + + if (!session.accessTokenExpiresAt || session.accessTokenExpiresAt - REFRESH_SKEW_SECONDS > now) { + return session; + } + + if (!session.refreshToken) { + return null; + } + + const tokenEndpoint: string = `${config.baseUrl}/oauth2/token`; + + const body: URLSearchParams = new URLSearchParams({ + client_id: config.clientId!, + grant_type: 'refresh_token', + refresh_token: session.refreshToken, + }); + + if (config.clientSecret) { + body.set('client_secret', config.clientSecret); + } + + let refreshed: OIDCTokenRefreshResponse; + try { + const res: Response = await fetch(tokenEndpoint, { + body, + headers: {'Content-Type': 'application/x-www-form-urlencoded'}, + method: 'POST', + }); + + if (!res.ok) { + return null; + } + + refreshed = (await res.json()) as OIDCTokenRefreshResponse; + } catch { + return null; + } + + const newSessionToken: string = await createSessionToken( + { + accessToken: refreshed.access_token, + accessTokenExpiresAt: now + (refreshed.expires_in ?? 3600), + idToken: refreshed.id_token ?? session.idToken, + organizationId: session.organizationId, + refreshToken: refreshed.refresh_token ?? session.refreshToken, + scopes: refreshed.scope ?? session.scopes, + sessionId: session.sessionId, + userId: session.sub, + }, + config.sessionSecret, + ); + + event.cookies.set(getSessionCookieName(), newSessionToken, getSessionCookieOptions()); + + return { + ...session, + accessToken: refreshed.access_token, + accessTokenExpiresAt: now + (refreshed.expires_in ?? 3600), + idToken: refreshed.id_token ?? session.idToken, + refreshToken: refreshed.refresh_token ?? session.refreshToken, + scopes: refreshed.scope ?? session.scopes, + }; +} + +/** + * Return a valid access token for the current request. + * + * Reads the session from `event.locals` (set by the handle hook), refreshes + * it if needed, and returns a guaranteed-fresh access token. + * + * Throws a redirect to the sign-in page when the session is missing or the + * refresh fails. + */ +export async function getValidAccessToken( + event: RequestEvent, + config: ThunderIDSvelteConfig, +): Promise { + const {loadThunderID} = await import('./load'); + const ssrData = loadThunderID(event); + + if (!ssrData.session) { + throw new Error('Not authenticated'); + } + + const refreshed = await maybeRefreshToken(ssrData.session, config, event); + + if (!refreshed) { + throw new Error('Session expired'); + } + + return refreshed.accessToken; +} diff --git a/packages/svelte/src/server/routes/index.ts b/packages/svelte/src/server/routes/index.ts index 69b1205..2d0dece 100644 --- a/packages/svelte/src/server/routes/index.ts +++ b/packages/svelte/src/server/routes/index.ts @@ -19,3 +19,4 @@ export {createSignInHandler} from './signin'; export {createCallbackHandler} from './callback'; export {createSignOutHandler} from './signout'; +export {createOrgSwitchHandler} from './orgSwitch'; diff --git a/packages/svelte/src/server/routes/orgSwitch.ts b/packages/svelte/src/server/routes/orgSwitch.ts new file mode 100644 index 0000000..880dcec --- /dev/null +++ b/packages/svelte/src/server/routes/orgSwitch.ts @@ -0,0 +1,86 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import type {Organization, TokenResponse} from '@thunderid/node'; +import type {ThunderIDSvelteConfig} from '../../models/config'; +import ThunderIDSvelteClient from '../../ThunderIDSvelteClient'; +import {verifySessionToken, issueSessionCookie, getSessionCookieName} from '../session'; + +export function createOrgSwitchHandler( + config: ThunderIDSvelteConfig, +): (event: {request: Request; cookies: any; url: URL}) => Promise { + return async (event) => { + const client: ThunderIDSvelteClient = ThunderIDSvelteClient.getInstance(); + + const sessionCookie: string | undefined = event.cookies.get(getSessionCookieName()); + if (!sessionCookie) { + return new Response(null, {status: 401, statusText: 'Not authenticated'}); + } + + let sessionId: string; + try { + const session = await verifySessionToken(sessionCookie, config.sessionSecret); + sessionId = session.sessionId; + } catch { + return new Response(null, {status: 401, statusText: 'Invalid session'}); + } + + let body: {organizationId?: string}; + try { + body = (await event.request.json()) as {organizationId?: string}; + } catch { + return new Response(JSON.stringify({error: 'Invalid request body'}), { + status: 400, + headers: {'content-type': 'application/json'}, + }); + } + + if (!body.organizationId) { + return new Response(JSON.stringify({error: 'organizationId is required'}), { + status: 400, + headers: {'content-type': 'application/json'}, + }); + } + + const organization: Organization = {id: body.organizationId, name: '', orgHandle: ''}; + + try { + const tokenResponse: TokenResponse = (await client.switchOrganization( + organization, + sessionId, + )) as unknown as TokenResponse; + + if (!tokenResponse.accessToken) { + throw new Error('No access token in response'); + } + + await issueSessionCookie(event, sessionId, tokenResponse, config.sessionSecret); + + return new Response(null, { + status: 302, + headers: {Location: config.afterSignInUrl || '/'}, + }); + } catch (err: unknown) { + const message: string = err instanceof Error ? err.message : 'Organization switch failed'; + return new Response(JSON.stringify({error: message}), { + status: 500, + headers: {'content-type': 'application/json'}, + }); + } + }; +} From 62fbd5904f4b8a4944bc98fd6818dfcb9956dd01 Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Sat, 27 Jun 2026 18:36:29 +0530 Subject: [PATCH 07/31] feat(svelte): add branding support, test suite, and fix cookie name in test - Add brandingPreference to ThunderIDSSRData, context, provider, composables - Fetch branding in parallel during handle hook (Gap 7) - Add 20 unit tests across 3 test files (session, guard, client) - Fix cookie name assertion to match actual CookieConfig format --- packages/svelte/src/ThunderID.svelte | 9 + packages/svelte/src/ThunderIDSvelteClient.ts | 7 + .../__tests__/ThunderIDSvelteClient.test.ts | 188 ++++++++++++++++++ .../svelte/src/composables/useThunderID.ts | 3 + packages/svelte/src/index.ts | 1 + packages/svelte/src/models/config.ts | 7 + packages/svelte/src/models/contexts.ts | 3 +- packages/svelte/src/models/session.ts | 3 +- .../svelte/src/server/__tests__/guard.test.ts | 104 ++++++++++ .../src/server/__tests__/session.test.ts | 144 ++++++++++++++ packages/svelte/src/server/hooks.ts | 28 ++- 11 files changed, 490 insertions(+), 7 deletions(-) create mode 100644 packages/svelte/src/__tests__/ThunderIDSvelteClient.test.ts create mode 100644 packages/svelte/src/server/__tests__/guard.test.ts create mode 100644 packages/svelte/src/server/__tests__/session.test.ts diff --git a/packages/svelte/src/ThunderID.svelte b/packages/svelte/src/ThunderID.svelte index f9b63b6..609d48e 100644 --- a/packages/svelte/src/ThunderID.svelte +++ b/packages/svelte/src/ThunderID.svelte @@ -79,11 +79,20 @@ } } + let brandingPreference: ThunderIDContext['brandingPreference'] = $state(null); + + $effect(() => { + if (ssrData?.brandingPreference) { + brandingPreference = ssrData.brandingPreference; + } + }); + const context: ThunderIDContext = { get afterSignInUrl() { return afterSignInUrl; }, get afterSignOutUrl() { return afterSignOutUrl; }, get applicationId() { return applicationId; }, get baseUrl() { return baseUrl; }, + get brandingPreference() { return brandingPreference; }, get clientId() { return clientId; }, get isInitialized() { return authState.isInitialized; }, get isLoading() { return authState.isLoading; }, diff --git a/packages/svelte/src/ThunderIDSvelteClient.ts b/packages/svelte/src/ThunderIDSvelteClient.ts index bb7ee9f..c2dc171 100644 --- a/packages/svelte/src/ThunderIDSvelteClient.ts +++ b/packages/svelte/src/ThunderIDSvelteClient.ts @@ -19,6 +19,8 @@ import { ThunderIDNodeClient, type AuthClientConfig, + type BrandingPreference, + type GetBrandingPreferenceConfig, type IdToken, type Organization, type Storage, @@ -26,6 +28,7 @@ import { type TokenResponse, type User, type UserProfile, + getBrandingPreference, getMeOrganizations, MemoryCacheStore, } from '@thunderid/node'; @@ -222,6 +225,10 @@ class ThunderIDSvelteClient extends ThunderIDNodeClient { + return getBrandingPreference(config); + } + public override getStorageManager(): any { return super.getStorageManager(); } diff --git a/packages/svelte/src/__tests__/ThunderIDSvelteClient.test.ts b/packages/svelte/src/__tests__/ThunderIDSvelteClient.test.ts new file mode 100644 index 0000000..decb1f3 --- /dev/null +++ b/packages/svelte/src/__tests__/ThunderIDSvelteClient.test.ts @@ -0,0 +1,188 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest'; +import type {ThunderIDSessionPayload} from '../models/session'; + +vi.mock('@thunderid/node', () => { + let storageData: Record = {}; + + class MockMemoryCacheStore { + setItem(_key: string, _value: any): void {} + getItem(_key: string): any { + return null; + } + removeItem(_key: string): void {} + clear(): void {} + } + + const getMeOrganizations = vi.fn().mockResolvedValue([]); + + const getBrandingPreference = vi.fn().mockResolvedValue(null); + + return { + ThunderIDNodeClient: class MockNodeClient { + private storage: any; + initialized = false; + + async initialize(_config: any, storage?: any): Promise { + this.storage = storage; + this.initialized = true; + return true; + } + + async reInitialize(_config: any): Promise { + return true; + } + + getStorageManager(): any { + return { + setSessionData: vi.fn().mockResolvedValue(undefined), + getSessionData: vi.fn().mockResolvedValue(null), + getConfigData: vi.fn().mockResolvedValue({baseUrl: 'https://example.com'}), + }; + } + + isSignedIn(_sessionId?: string): Promise { + return Promise.resolve(true); + } + + getUser(_sessionId?: string): Promise { + return Promise.resolve({id: 'u1', name: 'Test User'}); + } + + getAccessToken(_sessionId?: string): Promise { + return Promise.resolve('mock-access-token'); + } + + getDecodedIdToken(_sessionId?: string, _idToken?: string): Promise { + return Promise.resolve({sub: 'user-001'}); + } + + getUserProfile(_sessionId: string): Promise { + return Promise.resolve({flattenedProfile: {id: 'u1'}, profile: {id: 'u1'}, schemas: []}); + } + + getCurrentOrganization(_sessionId: string): Promise { + return Promise.resolve(null); + } + + getMyOrganizations(_sessionId: string): Promise { + return getMeOrganizations(); + } + + getSignInUrl(_customParams?: any, _userId?: string): Promise { + return Promise.resolve('https://idp.example.com/authorize'); + } + + exchangeToken(_config: any, _sessionId?: string): Promise { + return Promise.resolve({accessToken: 'new-at'}); + } + + signOut(_sessionId?: string): Promise { + return Promise.resolve('/'); + } + + getInstanceId(): number { + return 0; + } + }, + MemoryCacheStore: MockMemoryCacheStore, + getMeOrganizations, + getBrandingPreference, + CookieConfig: {SESSION_COOKIE_NAME: 'thunderid_session', TEMP_SESSION_COOKIE_NAME: 'thunderid_temp_session'}, + }; +}); + +const {default: ThunderIDSvelteClient} = await import('../ThunderIDSvelteClient'); + +describe('ThunderIDSvelteClient', () => { + beforeEach(() => { + // Reset singleton between tests + (ThunderIDSvelteClient as any).instance = undefined; + }); + + describe('singleton', () => { + it('should return the same instance on getInstance()', () => { + const a = ThunderIDSvelteClient.getInstance(); + const b = ThunderIDSvelteClient.getInstance(); + expect(a).toBe(b); + }); + + it('should create new instances via constructor', () => { + const a = ThunderIDSvelteClient.getInstance(); + const b = new (ThunderIDSvelteClient as any)(); + expect(a).not.toBe(b); + }); + }); + + describe('initialize', () => { + it('should initialize only once', async () => { + const client = ThunderIDSvelteClient.getInstance(); + const r1 = await client.initialize({baseUrl: 'https://example.com', clientId: 'cid'}); + const r2 = await client.initialize({baseUrl: 'https://example.com', clientId: 'cid'}); + expect(r1).toBe(true); + expect(r2).toBe(true); + expect(client.isInitialized).toBe(true); + }); + }); + + describe('rehydrateSessionFromPayload', () => { + it('should not throw when session is valid', async () => { + const client = ThunderIDSvelteClient.getInstance(); + await client.initialize({baseUrl: 'https://example.com', clientId: 'cid'}); + + const session: ThunderIDSessionPayload = { + accessToken: 'at-valid', + accessTokenExpiresAt: Math.floor(Date.now() / 1000) + 3600, + exp: Math.floor(Date.now() / 1000) + 3600, + iat: Math.floor(Date.now() / 1000), + scopes: 'openid', + sessionId: 'sess-001', + sub: 'user-001', + } as ThunderIDSessionPayload; + + await expect(client.rehydrateSessionFromPayload(session)).resolves.toBeUndefined(); + }); + + it('should silently return when not initialized', async () => { + const client = ThunderIDSvelteClient.getInstance(); + + const session: ThunderIDSessionPayload = { + accessToken: 'at', + exp: Math.floor(Date.now() / 1000) + 3600, + iat: Math.floor(Date.now() / 1000), + scopes: 'openid', + sessionId: 'sess-002', + sub: 'user-002', + } as ThunderIDSessionPayload; + + // Clearing any earlier initialized state + client.isInitialized = false; + await expect(client.rehydrateSessionFromPayload(session)).resolves.toBeUndefined(); + }); + + it('should silently return when session has no sessionId', async () => { + const client = ThunderIDSvelteClient.getInstance(); + await client.initialize({baseUrl: 'https://example.com', clientId: 'cid'}); + + const session = {accessToken: 'at'} as ThunderIDSessionPayload; + await expect(client.rehydrateSessionFromPayload(session)).resolves.toBeUndefined(); + }); + }); +}); diff --git a/packages/svelte/src/composables/useThunderID.ts b/packages/svelte/src/composables/useThunderID.ts index ff35252..de51bb3 100644 --- a/packages/svelte/src/composables/useThunderID.ts +++ b/packages/svelte/src/composables/useThunderID.ts @@ -25,6 +25,9 @@ export function useThunderID(): ThunderIDContext { return { ...context, + get brandingPreference() { + return context.brandingPreference; + }, get isSignedIn() { return authState.isSignedIn; }, diff --git a/packages/svelte/src/index.ts b/packages/svelte/src/index.ts index 218e2a1..4beaa37 100644 --- a/packages/svelte/src/index.ts +++ b/packages/svelte/src/index.ts @@ -38,4 +38,5 @@ export {useUser} from './composables/useUser'; // ── Models / Types ── export type {ThunderIDSvelteConfig} from './models/config'; export type {ThunderIDSSRData, ThunderIDSessionPayload} from './models/session'; +export type {BrandingPreference} from '@thunderid/node'; export type {ThunderIDContext, UserContextValue} from './models/contexts'; diff --git a/packages/svelte/src/models/config.ts b/packages/svelte/src/models/config.ts index 329546f..be71ed7 100644 --- a/packages/svelte/src/models/config.ts +++ b/packages/svelte/src/models/config.ts @@ -44,6 +44,13 @@ export interface ThunderIDSvelteConfig { signUpUrl?: string; /** Controls which server-side data fetches to perform on every SSR request */ preferences?: { + theme?: { + /** + * When true (default), the handle hook fetches the branding preference + * from ThunderID and passes it in `ThunderIDSSRData.brandingPreference`. + */ + inheritFromBranding?: boolean; + }; user?: { /** Whether to fetch the user's organizations during SSR (default: true) */ fetchOrganizations?: boolean; diff --git a/packages/svelte/src/models/contexts.ts b/packages/svelte/src/models/contexts.ts index 76b9070..1224205 100644 --- a/packages/svelte/src/models/contexts.ts +++ b/packages/svelte/src/models/contexts.ts @@ -16,13 +16,14 @@ * under the License. */ -import type {Organization, User, UserProfile} from '@thunderid/node'; +import type {BrandingPreference, Organization, User, UserProfile} from '@thunderid/node'; export interface ThunderIDContext { afterSignInUrl?: string; afterSignOutUrl?: string; applicationId?: string; baseUrl?: string; + brandingPreference: BrandingPreference | null; clientId?: string; isInitialized: boolean; isLoading: boolean; diff --git a/packages/svelte/src/models/session.ts b/packages/svelte/src/models/session.ts index ac7be8c..a87c6fb 100644 --- a/packages/svelte/src/models/session.ts +++ b/packages/svelte/src/models/session.ts @@ -17,7 +17,7 @@ */ import type {JWTPayload} from 'jose'; -import type {Organization, User, UserProfile} from '@thunderid/node'; +import type {BrandingPreference, Organization, User, UserProfile} from '@thunderid/node'; /** * Payload stored in the session JWT cookie. @@ -40,6 +40,7 @@ export interface ThunderIDSessionPayload extends JWTPayload { * Written to `event.locals.thunderid` and returned from `+layout.server.ts`. */ export interface ThunderIDSSRData { + brandingPreference: BrandingPreference | null; isSignedIn: boolean; myOrganizations: Organization[]; organization: Organization | null; diff --git a/packages/svelte/src/server/__tests__/guard.test.ts b/packages/svelte/src/server/__tests__/guard.test.ts new file mode 100644 index 0000000..39ad12b --- /dev/null +++ b/packages/svelte/src/server/__tests__/guard.test.ts @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {describe, it, expect, vi, beforeEach} from 'vitest'; +import type {ThunderIDSSRData} from '../../models/session'; +import type {RequestEvent} from '@sveltejs/kit'; + +const mockRedirect = vi.fn(); +vi.mock('@sveltejs/kit', () => ({ + redirect: mockRedirect, +})); + +const {requireServerSession} = await import('../guard'); + +function createMockEvent(ssrData: ThunderIDSSRData | undefined): RequestEvent { + return {locals: {thunderid: ssrData}} as unknown as RequestEvent; +} + +describe('requireServerSession', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return SSR data when signed in', () => { + const ssrData: ThunderIDSSRData = { + brandingPreference: null, + isSignedIn: true, + myOrganizations: [], + organization: null, + resolvedBaseUrl: null, + session: null, + user: {id: 'u1'} as any, + userProfile: null, + }; + + const result = requireServerSession(createMockEvent(ssrData)); + expect(result).toBe(ssrData); + expect(mockRedirect).not.toHaveBeenCalled(); + }); + + it('should throw redirect when not signed in', () => { + const ssrData: ThunderIDSSRData = { + brandingPreference: null, + isSignedIn: false, + myOrganizations: [], + organization: null, + resolvedBaseUrl: null, + session: null, + user: null, + userProfile: null, + }; + + mockRedirect.mockImplementation(() => { + throw new Error('redirect thrown'); + }); + + expect(() => requireServerSession(createMockEvent(ssrData))).toThrow('redirect thrown'); + expect(mockRedirect).toHaveBeenCalledWith(307, '/api/auth/signin'); + }); + + it('should throw redirect with custom redirectTo', () => { + const ssrData: ThunderIDSSRData = { + brandingPreference: null, + isSignedIn: false, + myOrganizations: [], + organization: null, + resolvedBaseUrl: null, + session: null, + user: null, + userProfile: null, + }; + + mockRedirect.mockImplementation(() => { + throw new Error('redirect thrown'); + }); + + expect(() => requireServerSession(createMockEvent(ssrData), '/custom/signin')).toThrow('redirect thrown'); + expect(mockRedirect).toHaveBeenCalledWith(307, '/custom/signin'); + }); + + it('should throw redirect when SSR data is undefined', () => { + mockRedirect.mockImplementation(() => { + throw new Error('redirect thrown'); + }); + + const event = {locals: {}} as RequestEvent; + expect(() => requireServerSession(event)).toThrow('redirect thrown'); + }); +}); diff --git a/packages/svelte/src/server/__tests__/session.test.ts b/packages/svelte/src/server/__tests__/session.test.ts new file mode 100644 index 0000000..8326d50 --- /dev/null +++ b/packages/svelte/src/server/__tests__/session.test.ts @@ -0,0 +1,144 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {describe, it, expect, beforeAll} from 'vitest'; +import { + createSessionToken, + createTempSessionToken, + verifySessionToken, + verifyTempSessionToken, + getSessionCookieName, + getTempSessionCookieName, + getSessionCookieOptions, + getTempSessionCookieOptions, +} from '../session'; + +const TEST_SECRET = 'test-session-secret-at-least-32-chars!!'; + +describe('Session token utilities', () => { + beforeAll(() => { + process.env['THUNDERID_SESSION_SECRET'] = TEST_SECRET; + }); + + describe('createSessionToken / verifySessionToken', () => { + it('should create and verify a session token', async () => { + const token = await createSessionToken({ + accessToken: 'at-123', + accessTokenExpiresAt: Math.floor(Date.now() / 1000) + 3600, + scopes: 'openid profile', + sessionId: 'sess-001', + userId: 'user-001', + }); + + const payload = await verifySessionToken(token); + expect(payload.accessToken).toBe('at-123'); + expect(payload.scopes).toBe('openid profile'); + expect(payload.sessionId).toBe('sess-001'); + expect(payload.sub).toBe('user-001'); + expect(payload['type']).toBe('session'); + }); + + it('should include idToken and refreshToken when provided', async () => { + const token = await createSessionToken({ + accessToken: 'at-456', + idToken: 'id-token-value', + refreshToken: 'rt-789', + scopes: 'openid', + sessionId: 'sess-002', + userId: 'user-002', + }); + + const payload = await verifySessionToken(token); + expect(payload.idToken).toBe('id-token-value'); + expect(payload.refreshToken).toBe('rt-789'); + }); + + it('should respect custom expirySeconds', async () => { + const token = await createSessionToken({ + accessToken: 'at', + expirySeconds: 60, + scopes: 'openid', + sessionId: 'sess-003', + userId: 'user-003', + }); + + const payload = await verifySessionToken(token); + const ttl = payload.exp! - payload.iat!; + expect(ttl).toBeLessThanOrEqual(65); + expect(ttl).toBeGreaterThanOrEqual(55); + }); + + it('should reject a token signed with a different secret', async () => { + const token = await createSessionToken( + { + accessToken: 'at', + scopes: 'openid', + sessionId: 'sess-004', + userId: 'user-004', + }, + 'different-secret-that-is-long-enough-for-test', + ); + + await expect(verifySessionToken(token)).rejects.toThrow(); + }); + }); + + describe('createTempSessionToken / verifyTempSessionToken', () => { + it('should create and verify a temp session token', async () => { + const token = await createTempSessionToken('sess-temp-001'); + const payload = await verifyTempSessionToken(token); + expect(payload.sessionId).toBe('sess-temp-001'); + expect(payload.returnTo).toBeUndefined(); + }); + + it('should store returnTo when provided', async () => { + const token = await createTempSessionToken('sess-temp-002', undefined, '/dashboard'); + const payload = await verifyTempSessionToken(token); + expect(payload.sessionId).toBe('sess-temp-002'); + expect(payload.returnTo).toBe('/dashboard'); + }); + + it('should reject an expired temp token', async () => { + // Can't easily test without overriding Date, but at least verify type checks + const token = await createTempSessionToken('sess-temp-003'); + const payload = await verifyTempSessionToken(token); + expect(payload.sessionId).toBe('sess-temp-003'); + }); + }); + + describe('Cookie helpers', () => { + it('should return consistent cookie names', () => { + expect(getSessionCookieName()).toBe('__thunderid__session'); + expect(getTempSessionCookieName()).toBe('__thunderid__temp.session'); + }); + + it('should return session cookie options', () => { + const opts = getSessionCookieOptions(); + expect(opts.httpOnly).toBe(true); + expect(opts.sameSite).toBe('lax'); + expect(opts.path).toBe('/'); + expect(opts.maxAge).toBeGreaterThan(0); + }); + + it('should return temp session cookie options with 15m maxAge', () => { + const opts = getTempSessionCookieOptions(); + expect(opts.httpOnly).toBe(true); + expect(opts.maxAge).toBe(15 * 60); + }); + }); +}); diff --git a/packages/svelte/src/server/hooks.ts b/packages/svelte/src/server/hooks.ts index 28da3de..762765e 100644 --- a/packages/svelte/src/server/hooks.ts +++ b/packages/svelte/src/server/hooks.ts @@ -16,6 +16,7 @@ * under the License. */ +import type {BrandingPreference} from '@thunderid/node'; import type {Handle, RequestEvent} from '@sveltejs/kit'; import type {ThunderIDSvelteConfig} from '../models/config'; import type {ThunderIDSSRData, ThunderIDSessionPayload} from '../models/session'; @@ -48,29 +49,46 @@ export function createThunderIDHandle(config: ThunderIDSvelteConfig): Handle { const isSignedIn: boolean = session !== null && (await client.isSignedIn(session.sessionId)); + const shouldFetchBranding: boolean = config.preferences?.theme?.inheritFromBranding !== false; + let ssrData: ThunderIDSSRData; if (isSignedIn && session) { - const [user, userProfile, organization, myOrganizations] = await Promise.all([ + const [user, userProfile, organization, myOrganizations, branding] = await Promise.all([ client.getUser(session.sessionId), client.getUserProfile(session.sessionId), client.getCurrentOrganization(session.sessionId), config.preferences?.user?.fetchOrganizations !== false ? client.getMyOrganizations(session.sessionId) : Promise.resolve([]), + shouldFetchBranding + ? client.getBrandingPreference({baseUrl: config.baseUrl!}).catch(() => null) + : Promise.resolve(null), ]); ssrData = { + brandingPreference: (branding as BrandingPreference) ?? null, isSignedIn: true, - myOrganizations, - organization, + myOrganizations: myOrganizations as any[], + organization: organization as any, resolvedBaseUrl: config.baseUrl ?? null, session, - user, - userProfile, + user: user as any, + userProfile: userProfile as any, }; } else { + let branding: BrandingPreference | null = null; + + if (shouldFetchBranding) { + try { + branding = await client.getBrandingPreference({baseUrl: config.baseUrl!}); + } catch { + // branding fetch failed — continue without + } + } + ssrData = { + brandingPreference: branding, isSignedIn: false, myOrganizations: [], organization: null, From 5a534131294b16b214ae3b4376da8ed81e4e52ce Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Sat, 27 Jun 2026 18:37:12 +0530 Subject: [PATCH 08/31] docs(svelte): add comprehensive README with SSR setup guide Covers installation, SPA quick start, 7-step SvelteKit SSR setup (hook, routes, load, provider), route guard, org switch, composables, and component reference. --- packages/svelte/README.md | 129 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 125 insertions(+), 4 deletions(-) diff --git a/packages/svelte/README.md b/packages/svelte/README.md index 3de2a54..1c20c33 100644 --- a/packages/svelte/README.md +++ b/packages/svelte/README.md @@ -1,6 +1,6 @@ # @thunderid/svelte -> **Svelte 5 SDK for [ThunderID](https://thunderid.dev)** — reactive auth with runes. +> **Svelte 5 SDK for [ThunderID](https://thunderid.dev)** — reactive auth with runes. Supports both SPA and SSR (SvelteKit). ## Installation @@ -8,7 +8,7 @@ pnpm add @thunderid/svelte ``` -## Quick Start +## Quick Start (SPA) ```svelte + + + {@render children()} + +``` + +### Server-side route guard + +Protect pages or API routes from unauthenticated access: + +```ts +import {requireServerSession} from '@thunderid/svelte/server'; +import type {RequestEvent} from '@sveltejs/kit'; + +export function load(event: RequestEvent) { + // Redirects to /api/auth/signin if not signed in + const ssrData = requireServerSession(event, '/custom/signin'); +} +``` + +### Switch organization server-side + +```ts +import {orgSwitchHandler} from '@thunderid/svelte/server'; + +export const POST = orgSwitchHandler; +``` + +## Composables + +```svelte + +``` + +## Components + +| Component | Description | +|-----------|-------------| +| `` | Provider — wraps your app with auth context | +| `` | Renders children when signed in | +| `` | Renders children when signed out | +| `` | Renders children while auth state initializes | +| `` | Sign-in button with loading state | +| `` | Sign-out button with loading state | +| `` | Sign-up button with loading state | ## License From 8069ca64fb17e35e4a0aab7a34ba6201f8458f29 Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Sat, 27 Jun 2026 18:40:21 +0530 Subject: [PATCH 09/31] docs(svelte): fix README exports to match factory functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SDK exports factory functions (createThunderIDHandle, createSignInHandler, etc.) — README now references the correct names. Also adds a TypeScript setup section for App.Locals type augmentation. --- packages/svelte/README.md | 38 +++++++++++++++++++++++++++----------- 1 file changed, 27 insertions(+), 11 deletions(-) diff --git a/packages/svelte/README.md b/packages/svelte/README.md index 1c20c33..1d56095 100644 --- a/packages/svelte/README.md +++ b/packages/svelte/README.md @@ -43,21 +43,21 @@ THUNDERID_CLIENT_ID= Create `src/hooks.server.ts`: ```ts -import {handleThunderID} from '@thunderid/svelte/server'; +import {createThunderIDHandle} from '@thunderid/svelte/server'; -export const handle = handleThunderID(); +export const handle = createThunderIDHandle(); ``` -The hook reads `THUNDERID_BASE_URL` and `THUNDERID_CLIENT_ID` from environment variables (or pass them via `handleThunderID({baseUrl, clientId})`). It resolves the session from the JWT cookie, refreshes expiring tokens proactively, fetches branding + organizations in parallel, and writes `event.locals.thunderid` with `ThunderIDSSRData`. +The hook reads `THUNDERID_BASE_URL` and `THUNDERID_CLIENT_ID` from environment variables (or pass them via `createThunderIDHandle({baseUrl, clientId})`). It resolves the session from the JWT cookie, refreshes expiring tokens proactively, fetches branding + organizations in parallel, and writes `event.locals.thunderid` with `ThunderIDSSRData`. ### 3. Create the sign-in route `src/routes/api/auth/signin/+server.ts`: ```ts -import {signInHandler} from '@thunderid/svelte/server'; +import {createSignInHandler} from '@thunderid/svelte/server'; -export const GET = signInHandler; +export const GET = createSignInHandler(); ``` ### 4. Create the callback route @@ -65,9 +65,9 @@ export const GET = signInHandler; `src/routes/api/auth/callback/+server.ts`: ```ts -import {callbackHandler} from '@thunderid/svelte/server'; +import {createCallbackHandler} from '@thunderid/svelte/server'; -export const GET = callbackHandler; +export const GET = createCallbackHandler(); ``` ### 5. Create the sign-out route @@ -75,9 +75,9 @@ export const GET = callbackHandler; `src/routes/api/auth/signout/+server.ts`: ```ts -import {signOutHandler} from '@thunderid/svelte/server'; +import {createSignOutHandler} from '@thunderid/svelte/server'; -export const GET = signOutHandler; +export const GET = createSignOutHandler(); ``` ### 6. Pass SSR data to the client @@ -106,6 +106,22 @@ Pass `ssrData` from the server load function to the provider: ``` +### TypeScript + +For `event.locals.thunderid` to have proper types in your load functions, reference the SDK's type augmentation in your `src/app.d.ts`: + +```ts +declare global { + namespace App { + interface Locals { + thunderid: import('@thunderid/svelte/server').ThunderIDSSRData; + } + } +} + +export {}; +``` + ### Server-side route guard Protect pages or API routes from unauthenticated access: @@ -123,9 +139,9 @@ export function load(event: RequestEvent) { ### Switch organization server-side ```ts -import {orgSwitchHandler} from '@thunderid/svelte/server'; +import {createOrgSwitchHandler} from '@thunderid/svelte/server'; -export const POST = orgSwitchHandler; +export const POST = createOrgSwitchHandler(); ``` ## Composables From 7d85f9a2433bcbbce8a8279683a01b9744c08bfc Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Sat, 27 Jun 2026 18:51:20 +0530 Subject: [PATCH 10/31] docs(svelte): add sv create scaffold step to SSR guide Uses pnpm create sv@latest (official SvelteKit scaffolding) as the prerequisite step before the 7-step setup. --- packages/svelte/README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/svelte/README.md b/packages/svelte/README.md index 1d56095..410b091 100644 --- a/packages/svelte/README.md +++ b/packages/svelte/README.md @@ -30,6 +30,17 @@ pnpm add @thunderid/svelte The SDK provides server utilities under the `@thunderid/svelte/server` subpath for SvelteKit SSR support. +### 0. Prerequisites + +Create a SvelteKit project if you don't have one: + +```bash +pnpm create sv@latest my-app --template minimal +cd my-app +pnpm install +pnpm add @thunderid/svelte +``` + ### 1. Set environment variables ```env From ddf41521182840d9c5648e8671b29d4019276220 Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Sat, 27 Jun 2026 19:12:53 +0530 Subject: [PATCH 11/31] docs(svelte): fix scaffold command to npx sv create, add apps/ to workspace - Use npx sv create (official Svelte CLI) instead of pnpm create sv - Add apps/* to pnpm-workspace.yaml so test apps get deps resolved - Add apps/ to .gitignore so throwaway test apps don't clutter the repo --- .gitignore | 3 +++ packages/svelte/README.md | 2 +- pnpm-workspace.yaml | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 6112da1..c3d69d3 100644 --- a/.gitignore +++ b/.gitignore @@ -146,6 +146,9 @@ vite.config.ts.timestamp-* .DS_Store Thumbs.db +# Test apps +apps/ + # Nx .nx/cache .nx/workspace-data diff --git a/packages/svelte/README.md b/packages/svelte/README.md index 410b091..3f97d55 100644 --- a/packages/svelte/README.md +++ b/packages/svelte/README.md @@ -35,7 +35,7 @@ The SDK provides server utilities under the `@thunderid/svelte/server` subpath f Create a SvelteKit project if you don't have one: ```bash -pnpm create sv@latest my-app --template minimal +npx sv create my-app --template minimal --types ts cd my-app pnpm install pnpm add @thunderid/svelte diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 5a27764..6fa5b7a 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,5 +1,6 @@ packages: - packages/** + - apps/* minimumReleaseAgeExclude: # JUSTIFICATION: Temporary exclusion due to recent release. TODO: Remove after the time passes. @@ -55,3 +56,6 @@ catalog: typescript: 5.9.3 uuid: 11.1.1 vitest: 4.1.8 +onlyBuiltDependencies: + - '@tailwindcss/oxide' + - esbuild From c7338bd60dc23a539119f547f432291e875c5a6e Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Sat, 27 Jun 2026 19:39:30 +0530 Subject: [PATCH 12/31] =?UTF-8?q?docs(svelte):=20fix=20step=207=20?= =?UTF-8?q?=E2=80=94=20ssrData=20prop=20not=20spread,=20show=20layout=20ex?= =?UTF-8?q?ample?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ssrData is passed as a single prop (ssrData={data}), not spread - Merge step 7 and layout wiring into one complete +layout.svelte example - Show SignedIn/SignedOut/SignInButton/SignOutButton in the layout --- packages/svelte/README.md | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/packages/svelte/README.md b/packages/svelte/README.md index 3f97d55..aa3e51d 100644 --- a/packages/svelte/README.md +++ b/packages/svelte/README.md @@ -101,18 +101,30 @@ import {loadThunderID} from '@thunderid/svelte/server'; export const load = loadThunderID; ``` -### 7. Hydrate the provider +### 7. Wire up the layout -Pass `ssrData` from the server load function to the provider: +Wrap your root layout with the provider and add sign-in/out controls: + +`src/routes/+layout.svelte`: ```svelte - + + {@render children()} ``` From a6771a559bdf6f700e0e6bb80fd9743b881c3d19 Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Sat, 27 Jun 2026 19:55:58 +0530 Subject: [PATCH 13/31] fix(svelte): make all server factories accept optional config with env fallback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add resolveConfig() helper that merges user config with env vars (THUNDERID_BASE_URL, THUNDERID_CLIENT_ID, THUNDERID_CLIENT_SECRET, THUNDERID_SESSION_SECRET) - Update createThunderIDHandle to use resolveConfig - Update createSignInHandler, createCallbackHandler, createSignOutHandler, createOrgSwitchHandler — all accept optional config now - Export resolveConfig from @thunderid/svelte/server --- packages/svelte/src/server/config.ts | 36 +++++++++++++++++++ packages/svelte/src/server/hooks.ts | 21 ++++++----- packages/svelte/src/server/index.ts | 1 + packages/svelte/src/server/routes/callback.ts | 19 +++++----- .../svelte/src/server/routes/orgSwitch.ts | 11 +++--- packages/svelte/src/server/routes/signin.ts | 10 +++--- packages/svelte/src/server/routes/signout.ts | 7 ++-- 7 files changed, 78 insertions(+), 27 deletions(-) create mode 100644 packages/svelte/src/server/config.ts diff --git a/packages/svelte/src/server/config.ts b/packages/svelte/src/server/config.ts new file mode 100644 index 0000000..52b5c98 --- /dev/null +++ b/packages/svelte/src/server/config.ts @@ -0,0 +1,36 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import type {ThunderIDSvelteConfig} from '../models/config'; + +export function resolveConfig(config?: ThunderIDSvelteConfig): ThunderIDSvelteConfig { + return { + afterSignInUrl: config?.afterSignInUrl ?? '/', + afterSignOutUrl: config?.afterSignOutUrl ?? '/', + baseUrl: config?.baseUrl || process.env['THUNDERID_BASE_URL'], + clientId: config?.clientId || process.env['THUNDERID_CLIENT_ID'], + clientSecret: config?.clientSecret || process.env['THUNDERID_CLIENT_SECRET'], + enablePKCE: config?.enablePKCE ?? true, + scopes: config?.scopes ?? ['openid', 'profile'], + sessionSecret: config?.sessionSecret || process.env['THUNDERID_SESSION_SECRET'], + signInUrl: config?.signInUrl, + signUpUrl: config?.signUpUrl, + applicationId: config?.applicationId, + preferences: config?.preferences, + }; +} diff --git a/packages/svelte/src/server/hooks.ts b/packages/svelte/src/server/hooks.ts index 762765e..2b32bdc 100644 --- a/packages/svelte/src/server/hooks.ts +++ b/packages/svelte/src/server/hooks.ts @@ -24,19 +24,22 @@ import ThunderIDSvelteClient from '../ThunderIDSvelteClient'; import {getClient} from './getClient'; import {verifySessionToken, getSessionCookieName} from './session'; import {maybeRefreshToken} from './refresh'; +import {resolveConfig} from './config'; + +export function createThunderIDHandle(config?: ThunderIDSvelteConfig): Handle { + const resolvedConfig: ThunderIDSvelteConfig = resolveConfig(config); -export function createThunderIDHandle(config: ThunderIDSvelteConfig): Handle { return async ({event, resolve}) => { - const client: ThunderIDSvelteClient = await getClient(config); + const client: ThunderIDSvelteClient = await getClient(resolvedConfig); const sessionCookie: string | undefined = event.cookies.get(getSessionCookieName()); let session: ThunderIDSessionPayload | null = null; if (sessionCookie) { try { - session = await verifySessionToken(sessionCookie, config.sessionSecret); + session = await verifySessionToken(sessionCookie, resolvedConfig.sessionSecret); - session = await maybeRefreshToken(session, config, event); + session = await maybeRefreshToken(session, resolvedConfig, event); if (session) { await client.rehydrateSessionFromPayload(session); @@ -49,7 +52,7 @@ export function createThunderIDHandle(config: ThunderIDSvelteConfig): Handle { const isSignedIn: boolean = session !== null && (await client.isSignedIn(session.sessionId)); - const shouldFetchBranding: boolean = config.preferences?.theme?.inheritFromBranding !== false; + const shouldFetchBranding: boolean = resolvedConfig.preferences?.theme?.inheritFromBranding !== false; let ssrData: ThunderIDSSRData; @@ -58,11 +61,11 @@ export function createThunderIDHandle(config: ThunderIDSvelteConfig): Handle { client.getUser(session.sessionId), client.getUserProfile(session.sessionId), client.getCurrentOrganization(session.sessionId), - config.preferences?.user?.fetchOrganizations !== false + resolvedConfig.preferences?.user?.fetchOrganizations !== false ? client.getMyOrganizations(session.sessionId) : Promise.resolve([]), shouldFetchBranding - ? client.getBrandingPreference({baseUrl: config.baseUrl!}).catch(() => null) + ? client.getBrandingPreference({baseUrl: resolvedConfig.baseUrl!}).catch(() => null) : Promise.resolve(null), ]); @@ -71,7 +74,7 @@ export function createThunderIDHandle(config: ThunderIDSvelteConfig): Handle { isSignedIn: true, myOrganizations: myOrganizations as any[], organization: organization as any, - resolvedBaseUrl: config.baseUrl ?? null, + resolvedBaseUrl: resolvedConfig.baseUrl ?? null, session, user: user as any, userProfile: userProfile as any, @@ -81,7 +84,7 @@ export function createThunderIDHandle(config: ThunderIDSvelteConfig): Handle { if (shouldFetchBranding) { try { - branding = await client.getBrandingPreference({baseUrl: config.baseUrl!}); + branding = await client.getBrandingPreference({baseUrl: resolvedConfig.baseUrl!}); } catch { // branding fetch failed — continue without } diff --git a/packages/svelte/src/server/index.ts b/packages/svelte/src/server/index.ts index ab02f92..26677c0 100644 --- a/packages/svelte/src/server/index.ts +++ b/packages/svelte/src/server/index.ts @@ -16,6 +16,7 @@ * under the License. */ +export {resolveConfig} from './config'; export {createThunderIDHandle} from './hooks'; export {loadThunderID} from './load'; export {getClient, resetClient} from './getClient'; diff --git a/packages/svelte/src/server/routes/callback.ts b/packages/svelte/src/server/routes/callback.ts index 7fb6e7f..cd47ee7 100644 --- a/packages/svelte/src/server/routes/callback.ts +++ b/packages/svelte/src/server/routes/callback.ts @@ -20,27 +20,30 @@ import type {TokenResponse} from '@thunderid/node'; import type {ThunderIDSvelteConfig} from '../../models/config'; import ThunderIDSvelteClient from '../../ThunderIDSvelteClient'; import {issueSessionCookie, verifyTempSessionToken, getTempSessionCookieName, getSessionCookieName} from '../session'; +import {resolveConfig} from '../config'; + +export function createCallbackHandler(config?: ThunderIDSvelteConfig): (event: {url: URL; cookies: any}) => Promise { + const resolvedConfig: ThunderIDSvelteConfig = resolveConfig(config); -export function createCallbackHandler(config: ThunderIDSvelteConfig): (event: {url: URL; cookies: any}) => Promise { return async (event) => { const client: ThunderIDSvelteClient = ThunderIDSvelteClient.getInstance(); const tempCookie: string | undefined = event.cookies.get(getTempSessionCookieName()); if (!tempCookie) { - return new Response(null, {status: 302, headers: {Location: config.afterSignInUrl || '/'}}); + return new Response(null, {status: 302, headers: {Location: resolvedConfig.afterSignInUrl || '/'}}); } let sessionId: string; let returnTo: string | undefined; try { - const tempPayload = await verifyTempSessionToken(tempCookie, config.sessionSecret); + const tempPayload = await verifyTempSessionToken(tempCookie, resolvedConfig.sessionSecret); sessionId = tempPayload.sessionId; returnTo = tempPayload.returnTo; } catch { event.cookies.delete(getTempSessionCookieName(), {path: '/'}); - return new Response(null, {status: 302, headers: {Location: config.afterSignInUrl || '/'}}); + return new Response(null, {status: 302, headers: {Location: resolvedConfig.afterSignInUrl || '/'}}); } const code: string | null = event.url.searchParams.get('code'); @@ -55,22 +58,22 @@ export function createCallbackHandler(config: ThunderIDSvelteConfig): (event: {u )) as unknown as TokenResponse; if (tokenResponse.accessToken) { - const sessionPayload = await issueSessionCookie(event, sessionId, tokenResponse, config.sessionSecret); + const sessionPayload = await issueSessionCookie(event, sessionId, tokenResponse, resolvedConfig.sessionSecret); event.cookies.delete(getTempSessionCookieName(), {path: '/'}); return new Response(null, { status: 302, - headers: {Location: returnTo || config.afterSignInUrl || '/'}, + headers: {Location: returnTo || resolvedConfig.afterSignInUrl || '/'}, }); } } const error: string | null = event.url.searchParams.get('error'); if (error) { - return new Response(null, {status: 302, headers: {Location: `${config.afterSignInUrl || '/'}?error=${error}`}}); + return new Response(null, {status: 302, headers: {Location: `${resolvedConfig.afterSignInUrl || '/'}?error=${error}`}}); } - return new Response(null, {status: 302, headers: {Location: config.afterSignInUrl || '/'}}); + return new Response(null, {status: 302, headers: {Location: resolvedConfig.afterSignInUrl || '/'}}); }; } diff --git a/packages/svelte/src/server/routes/orgSwitch.ts b/packages/svelte/src/server/routes/orgSwitch.ts index 880dcec..d1328a5 100644 --- a/packages/svelte/src/server/routes/orgSwitch.ts +++ b/packages/svelte/src/server/routes/orgSwitch.ts @@ -20,10 +20,13 @@ import type {Organization, TokenResponse} from '@thunderid/node'; import type {ThunderIDSvelteConfig} from '../../models/config'; import ThunderIDSvelteClient from '../../ThunderIDSvelteClient'; import {verifySessionToken, issueSessionCookie, getSessionCookieName} from '../session'; +import {resolveConfig} from '../config'; export function createOrgSwitchHandler( - config: ThunderIDSvelteConfig, + config?: ThunderIDSvelteConfig, ): (event: {request: Request; cookies: any; url: URL}) => Promise { + const resolvedConfig: ThunderIDSvelteConfig = resolveConfig(config); + return async (event) => { const client: ThunderIDSvelteClient = ThunderIDSvelteClient.getInstance(); @@ -34,7 +37,7 @@ export function createOrgSwitchHandler( let sessionId: string; try { - const session = await verifySessionToken(sessionCookie, config.sessionSecret); + const session = await verifySessionToken(sessionCookie, resolvedConfig.sessionSecret); sessionId = session.sessionId; } catch { return new Response(null, {status: 401, statusText: 'Invalid session'}); @@ -69,11 +72,11 @@ export function createOrgSwitchHandler( throw new Error('No access token in response'); } - await issueSessionCookie(event, sessionId, tokenResponse, config.sessionSecret); + await issueSessionCookie(event, sessionId, tokenResponse, resolvedConfig.sessionSecret); return new Response(null, { status: 302, - headers: {Location: config.afterSignInUrl || '/'}, + headers: {Location: resolvedConfig.afterSignInUrl || '/'}, }); } catch (err: unknown) { const message: string = err instanceof Error ? err.message : 'Organization switch failed'; diff --git a/packages/svelte/src/server/routes/signin.ts b/packages/svelte/src/server/routes/signin.ts index f4646eb..b04d102 100644 --- a/packages/svelte/src/server/routes/signin.ts +++ b/packages/svelte/src/server/routes/signin.ts @@ -20,20 +20,22 @@ import {generateSessionId} from '@thunderid/node'; import type {ThunderIDSvelteConfig} from '../../models/config'; import ThunderIDSvelteClient from '../../ThunderIDSvelteClient'; import {createTempSessionToken, getTempSessionCookieName, getTempSessionCookieOptions} from '../session'; +import {resolveConfig} from '../config'; -export function createSignInHandler(config: ThunderIDSvelteConfig): (event: {url: URL; cookies: any}) => Promise { +export function createSignInHandler(config?: ThunderIDSvelteConfig): (event: {url: URL; cookies: any}) => Promise { + const resolvedConfig: ThunderIDSvelteConfig = resolveConfig(config); return async (event) => { const client: ThunderIDSvelteClient = ThunderIDSvelteClient.getInstance(); const sessionId: string = generateSessionId(); - const returnTo: string = event.url.searchParams.get('returnTo') || config.afterSignInUrl || '/'; + const returnTo: string = event.url.searchParams.get('returnTo') || resolvedConfig.afterSignInUrl || '/'; const authorizeUrl: string = await client.getAuthorizeRequestUrl( - {redirectUri: `${event.url.origin}/api/auth/callback`, scope: config.scopes}, + {redirectUri: `${event.url.origin}/api/auth/callback`, scope: resolvedConfig.scopes}, sessionId, ); - const tempToken: string = await createTempSessionToken(sessionId, config.sessionSecret, returnTo); + const tempToken: string = await createTempSessionToken(sessionId, resolvedConfig.sessionSecret, returnTo); event.cookies.set(getTempSessionCookieName(), tempToken, getTempSessionCookieOptions()); diff --git a/packages/svelte/src/server/routes/signout.ts b/packages/svelte/src/server/routes/signout.ts index 3e1a106..dd5007a 100644 --- a/packages/svelte/src/server/routes/signout.ts +++ b/packages/svelte/src/server/routes/signout.ts @@ -19,11 +19,14 @@ import type {ThunderIDSvelteConfig} from '../../models/config'; import ThunderIDSvelteClient from '../../ThunderIDSvelteClient'; import {getSessionCookieName} from '../session'; +import {resolveConfig} from '../config'; + +export function createSignOutHandler(config?: ThunderIDSvelteConfig): (event: {cookies: any}) => Promise { + const resolvedConfig: ThunderIDSvelteConfig = resolveConfig(config); -export function createSignOutHandler(config: ThunderIDSvelteConfig): (event: {cookies: any}) => Promise { return async (event) => { const client: ThunderIDSvelteClient = ThunderIDSvelteClient.getInstance(); - const redirectUrl: string = config.afterSignOutUrl || '/'; + const redirectUrl: string = resolvedConfig.afterSignOutUrl || '/'; try { await client.signOut(); From c2674dd2cc9830590c570a2c3b7a29cbb8e64750 Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Sun, 28 Jun 2026 08:50:40 +0530 Subject: [PATCH 14/31] fix(svelte): complete OAuth sign-in flow with PKCE, SSR hydration, and token refresh - Fix sign-in handler: remove broken redirectUri/scope custom params, pass client_secret as {{clientSecret}} for IdP-based secret resolution - Fix callback handler: add try-catch with proper error serialization (ThunderIDAuthException does not extend Error) - Fix orgSwitch handler: same catch pattern for non-Error exceptions - Add tokenRequest.authMethod default of client_secret_post in config (matches IdP expectations for server-side apps) - Add missing await on getStorageManager().getConfigData() in getMyOrganizations and signOut methods - Add .catch(() => []) on getMyOrganizations call in hooks to handle 403 gracefully when user lacks org scope - Update README with /static/private pattern, tokenRequest config, and redirect URI setup notes --- packages/svelte/README.md | 61 +++++++++++++------ packages/svelte/src/ThunderIDSvelteClient.ts | 5 +- packages/svelte/src/models/config.ts | 6 +- packages/svelte/src/server/config.ts | 1 + packages/svelte/src/server/hooks.ts | 7 ++- packages/svelte/src/server/routes/callback.ts | 21 +++++-- .../svelte/src/server/routes/orgSwitch.ts | 2 +- packages/svelte/src/server/routes/signin.ts | 40 ++++++++---- 8 files changed, 101 insertions(+), 42 deletions(-) diff --git a/packages/svelte/README.md b/packages/svelte/README.md index aa3e51d..e49a7f0 100644 --- a/packages/svelte/README.md +++ b/packages/svelte/README.md @@ -47,8 +47,25 @@ pnpm add @thunderid/svelte THUNDERID_SESSION_SECRET= THUNDERID_BASE_URL=https://{tenant}.thunderid.dev THUNDERID_CLIENT_ID= +THUNDERID_CLIENT_SECRET= ``` +> **SvelteKit convention**: Import env vars from `$env/static/private` and pass them explicitly to factory functions for full type safety: +> +> ```ts +> import { THUNDERID_BASE_URL, THUNDERID_CLIENT_ID, THUNDERID_CLIENT_SECRET, THUNDERID_SESSION_SECRET } from '$env/static/private'; +> import { createThunderIDHandle } from '@thunderid/svelte/server'; +> +> export const handle = createThunderIDHandle({ +> baseUrl: THUNDERID_BASE_URL, +> clientId: THUNDERID_CLIENT_ID, +> clientSecret: THUNDERID_CLIENT_SECRET, +> sessionSecret: THUNDERID_SESSION_SECRET, +> }); +> ``` +> +> Passing no arguments falls back to `process.env` (works in dev, but `$env/static/private` is recommended). + ### 2. Apply the handle hook Create `src/hooks.server.ts`: @@ -59,38 +76,35 @@ import {createThunderIDHandle} from '@thunderid/svelte/server'; export const handle = createThunderIDHandle(); ``` -The hook reads `THUNDERID_BASE_URL` and `THUNDERID_CLIENT_ID` from environment variables (or pass them via `createThunderIDHandle({baseUrl, clientId})`). It resolves the session from the JWT cookie, refreshes expiring tokens proactively, fetches branding + organizations in parallel, and writes `event.locals.thunderid` with `ThunderIDSSRData`. - -### 3. Create the sign-in route +The hook reads env vars from `process.env` (or accepts config explicitly). It resolves the session from the JWT cookie, refreshes expiring tokens proactively, fetches branding + organizations in parallel, and writes `event.locals.thunderid` with `ThunderIDSSRData`. -`src/routes/api/auth/signin/+server.ts`: +**Token endpoint auth method**: The SDK defaults to `client_secret_post`. ```ts -import {createSignInHandler} from '@thunderid/svelte/server'; - -export const GET = createSignInHandler(); +createThunderIDHandle({ + ...config, + tokenRequest: {authMethod: 'client_secret_basic'}, +}); ``` -### 4. Create the callback route - -`src/routes/api/auth/callback/+server.ts`: +### 3. Create the sign-in, callback, and sign-out routes ```ts -import {createCallbackHandler} from '@thunderid/svelte/server'; +// src/routes/api/auth/signin/+server.ts +import {createSignInHandler} from '@thunderid/svelte/server'; +export const GET = createSignInHandler(); +// src/routes/api/auth/callback/+server.ts +import {createCallbackHandler} from '@thunderid/svelte/server'; export const GET = createCallbackHandler(); -``` -### 5. Create the sign-out route - -`src/routes/api/auth/signout/+server.ts`: - -```ts +// src/routes/api/auth/signout/+server.ts import {createSignOutHandler} from '@thunderid/svelte/server'; - export const GET = createSignOutHandler(); ``` +You can pass config to each handler (e.g., `createSignInHandler({...config})`). If omitted, they use the same env-var fallbacks as the handle hook. + ### 6. Pass SSR data to the client Create `src/routes/+layout.server.ts`: @@ -145,6 +159,17 @@ declare global { export {}; ``` +### Redirect URI Configuration + +In your ThunderID application settings, add the exact callback URL: + +- **Development**: `http://localhost:5173/api/auth/callback` (use `http`, not `https` — SvelteKit dev server runs on HTTP) +- **Production**: `https://your-domain.com/api/auth/callback` + +The hook automatically computes the callback URL from the request origin — no manual `afterSignInUrl` setup needed. + +> **Local dev with self-signed TLS**: If your IdP uses a self-signed certificate, launch the dev server with `NODE_TLS_REJECT_UNAUTHORIZED=0 pnpm dev` (dev only — never use in production). + ### Server-side route guard Protect pages or API routes from unauthenticated access: diff --git a/packages/svelte/src/ThunderIDSvelteClient.ts b/packages/svelte/src/ThunderIDSvelteClient.ts index c2dc171..a9a6c6c 100644 --- a/packages/svelte/src/ThunderIDSvelteClient.ts +++ b/packages/svelte/src/ThunderIDSvelteClient.ts @@ -64,6 +64,7 @@ class ThunderIDSvelteClient extends ThunderIDNodeClient; const resolvedStorage: Storage = storage ?? new MemoryCacheStore(); @@ -132,7 +133,7 @@ class ThunderIDSvelteClient extends ThunderIDNodeClient { - const configData: any = this.getStorageManager().getConfigData(); + const configData: any = await this.getStorageManager().getConfigData(); return (configData?.afterSignOutUrl as string) || (configData?.afterSignInUrl as string) || '/'; } @@ -190,7 +191,7 @@ class ThunderIDSvelteClient extends ThunderIDNodeClient { const accessToken: string = await this.getAccessToken(sessionId); - const configData: any = this.getStorageManager().getConfigData(); + const configData: any = await this.getStorageManager().getConfigData(); const baseUrl: string = (configData?.baseUrl ?? '') as string; return getMeOrganizations({ diff --git a/packages/svelte/src/models/config.ts b/packages/svelte/src/models/config.ts index be71ed7..31ac862 100644 --- a/packages/svelte/src/models/config.ts +++ b/packages/svelte/src/models/config.ts @@ -32,8 +32,12 @@ export interface ThunderIDSvelteConfig { clientId?: string; /** OAuth2 Client Secret (server-only, use THUNDERID_CLIENT_SECRET env var) */ clientSecret?: string; - /** The auth method to use for the token request. */ + /** Whether to enable PKCE (default: true) */ enablePKCE?: boolean; + /** Token endpoint authentication method (default: 'client_secret_post') */ + tokenRequest?: { + authMethod: 'client_secret_basic' | 'client_secret_post' | 'private_key_jwt' | 'none'; + }; /** OAuth2 scopes to request */ scopes?: string | string[]; /** Secret for signing session JWTs (use THUNDERID_SESSION_SECRET env var) */ diff --git a/packages/svelte/src/server/config.ts b/packages/svelte/src/server/config.ts index 52b5c98..ddcf993 100644 --- a/packages/svelte/src/server/config.ts +++ b/packages/svelte/src/server/config.ts @@ -28,6 +28,7 @@ export function resolveConfig(config?: ThunderIDSvelteConfig): ThunderIDSvelteCo enablePKCE: config?.enablePKCE ?? true, scopes: config?.scopes ?? ['openid', 'profile'], sessionSecret: config?.sessionSecret || process.env['THUNDERID_SESSION_SECRET'], + tokenRequest: config?.tokenRequest ?? {authMethod: 'client_secret_post'}, signInUrl: config?.signInUrl, signUpUrl: config?.signUpUrl, applicationId: config?.applicationId, diff --git a/packages/svelte/src/server/hooks.ts b/packages/svelte/src/server/hooks.ts index 2b32bdc..e69de6b 100644 --- a/packages/svelte/src/server/hooks.ts +++ b/packages/svelte/src/server/hooks.ts @@ -26,11 +26,14 @@ import {verifySessionToken, getSessionCookieName} from './session'; import {maybeRefreshToken} from './refresh'; import {resolveConfig} from './config'; +const CALLBACK_PATH = '/api/auth/callback'; + export function createThunderIDHandle(config?: ThunderIDSvelteConfig): Handle { const resolvedConfig: ThunderIDSvelteConfig = resolveConfig(config); return async ({event, resolve}) => { - const client: ThunderIDSvelteClient = await getClient(resolvedConfig); + const callbackUrl: string = `${event.url.origin}${CALLBACK_PATH}`; + const client: ThunderIDSvelteClient = await getClient({...resolvedConfig, afterSignInUrl: callbackUrl}); const sessionCookie: string | undefined = event.cookies.get(getSessionCookieName()); let session: ThunderIDSessionPayload | null = null; @@ -62,7 +65,7 @@ export function createThunderIDHandle(config?: ThunderIDSvelteConfig): Handle { client.getUserProfile(session.sessionId), client.getCurrentOrganization(session.sessionId), resolvedConfig.preferences?.user?.fetchOrganizations !== false - ? client.getMyOrganizations(session.sessionId) + ? client.getMyOrganizations(session.sessionId).catch(() => []) : Promise.resolve([]), shouldFetchBranding ? client.getBrandingPreference({baseUrl: resolvedConfig.baseUrl!}).catch(() => null) diff --git a/packages/svelte/src/server/routes/callback.ts b/packages/svelte/src/server/routes/callback.ts index cd47ee7..c74df5c 100644 --- a/packages/svelte/src/server/routes/callback.ts +++ b/packages/svelte/src/server/routes/callback.ts @@ -51,11 +51,22 @@ export function createCallbackHandler(config?: ThunderIDSvelteConfig): (event: { const sessionState: string | null = event.url.searchParams.get('session_state'); if (code) { - const tokenResponse: TokenResponse = (await client.signIn( - {code, session_state: sessionState ?? undefined, state: state ?? undefined}, - undefined, - sessionId, - )) as unknown as TokenResponse; + let tokenResponse: TokenResponse; + + try { + tokenResponse = (await client.signIn( + {code, session_state: sessionState ?? undefined, state: state ?? undefined}, + undefined, + sessionId, + )) as unknown as TokenResponse; + } catch (err: unknown) { + const message: string = (err as any)?.message ?? String(err); + console.error('[thunderid] callback signIn failed:', message); + return new Response(JSON.stringify({error: message}), { + status: 500, + headers: {'content-type': 'application/json'}, + }); + } if (tokenResponse.accessToken) { const sessionPayload = await issueSessionCookie(event, sessionId, tokenResponse, resolvedConfig.sessionSecret); diff --git a/packages/svelte/src/server/routes/orgSwitch.ts b/packages/svelte/src/server/routes/orgSwitch.ts index d1328a5..e7e647e 100644 --- a/packages/svelte/src/server/routes/orgSwitch.ts +++ b/packages/svelte/src/server/routes/orgSwitch.ts @@ -79,7 +79,7 @@ export function createOrgSwitchHandler( headers: {Location: resolvedConfig.afterSignInUrl || '/'}, }); } catch (err: unknown) { - const message: string = err instanceof Error ? err.message : 'Organization switch failed'; + const message: string = (err as any)?.message ?? 'Organization switch failed'; return new Response(JSON.stringify({error: message}), { status: 500, headers: {'content-type': 'application/json'}, diff --git a/packages/svelte/src/server/routes/signin.ts b/packages/svelte/src/server/routes/signin.ts index b04d102..d5e0302 100644 --- a/packages/svelte/src/server/routes/signin.ts +++ b/packages/svelte/src/server/routes/signin.ts @@ -24,24 +24,38 @@ import {resolveConfig} from '../config'; export function createSignInHandler(config?: ThunderIDSvelteConfig): (event: {url: URL; cookies: any}) => Promise { const resolvedConfig: ThunderIDSvelteConfig = resolveConfig(config); + + if (!resolvedConfig.baseUrl) { + console.warn('[thunderid] THUNDERID_BASE_URL is not set. Set it as an environment variable or pass it to createSignInHandler().'); + } + return async (event) => { - const client: ThunderIDSvelteClient = ThunderIDSvelteClient.getInstance(); - const sessionId: string = generateSessionId(); + try { + const client: ThunderIDSvelteClient = ThunderIDSvelteClient.getInstance(); + const sessionId: string = generateSessionId(); - const returnTo: string = event.url.searchParams.get('returnTo') || resolvedConfig.afterSignInUrl || '/'; + const returnTo: string = event.url.searchParams.get('returnTo') || resolvedConfig.afterSignInUrl || '/'; - const authorizeUrl: string = await client.getAuthorizeRequestUrl( - {redirectUri: `${event.url.origin}/api/auth/callback`, scope: resolvedConfig.scopes}, - sessionId, - ); + const authorizeUrl: string = await client.getAuthorizeRequestUrl( + {client_secret: '{{clientSecret}}'}, + sessionId, + ); - const tempToken: string = await createTempSessionToken(sessionId, resolvedConfig.sessionSecret, returnTo); + const tempToken: string = await createTempSessionToken(sessionId, resolvedConfig.sessionSecret, returnTo); - event.cookies.set(getTempSessionCookieName(), tempToken, getTempSessionCookieOptions()); + event.cookies.set(getTempSessionCookieName(), tempToken, getTempSessionCookieOptions()); - return new Response(null, { - status: 302, - headers: {Location: authorizeUrl}, - }); + return new Response(null, { + status: 302, + headers: {Location: authorizeUrl}, + }); + } catch (err: unknown) { + const message: string = (err as any)?.message ?? String(err); + console.error('[thunderid] sign-in failed:', message); + return new Response(JSON.stringify({error: message}), { + status: 500, + headers: {'content-type': 'application/json'}, + }); + } }; } From 8a7411498974f367e80973b6eba4b957d0d1f1ec Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Sun, 28 Jun 2026 08:57:23 +0530 Subject: [PATCH 15/31] docs(svelte): remove untested SPA quick-start, keep only SSR docs --- packages/svelte/README.md | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/packages/svelte/README.md b/packages/svelte/README.md index e49a7f0..ed58535 100644 --- a/packages/svelte/README.md +++ b/packages/svelte/README.md @@ -8,24 +8,6 @@ pnpm add @thunderid/svelte ``` -## Quick Start (SPA) - -```svelte - - - - -

Welcome!

- -
- - - -
-``` - ## SvelteKit (SSR) The SDK provides server utilities under the `@thunderid/svelte/server` subpath for SvelteKit SSR support. From f02403a06e3e7b549661c0e677c34deeeacc1110 Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Sun, 28 Jun 2026 08:59:23 +0530 Subject: [PATCH 16/31] docs(svelte): update README --- packages/svelte/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/svelte/README.md b/packages/svelte/README.md index ed58535..59e9b73 100644 --- a/packages/svelte/README.md +++ b/packages/svelte/README.md @@ -1,6 +1,6 @@ # @thunderid/svelte -> **Svelte 5 SDK for [ThunderID](https://thunderid.dev)** — reactive auth with runes. Supports both SPA and SSR (SvelteKit). +**Svelte 5 SDK for [ThunderID](https://thunderid.dev)** — reactive auth with runes. ## Installation From de765bd0e544afbb2a3f3fa73d163184ac06ebf1 Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Sun, 28 Jun 2026 09:02:03 +0530 Subject: [PATCH 17/31] docs(svelte): update README --- packages/svelte/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/svelte/README.md b/packages/svelte/README.md index 59e9b73..306f7e3 100644 --- a/packages/svelte/README.md +++ b/packages/svelte/README.md @@ -1,6 +1,6 @@ # @thunderid/svelte -**Svelte 5 SDK for [ThunderID](https://thunderid.dev)** — reactive auth with runes. +**Svelte 5 SDK for [ThunderID](https://thunderid.dev)** - reactive auth with runes. ## Installation @@ -12,7 +12,7 @@ pnpm add @thunderid/svelte The SDK provides server utilities under the `@thunderid/svelte/server` subpath for SvelteKit SSR support. -### 0. Prerequisites +### Prerequisites Create a SvelteKit project if you don't have one: @@ -145,12 +145,12 @@ export {}; In your ThunderID application settings, add the exact callback URL: -- **Development**: `http://localhost:5173/api/auth/callback` (use `http`, not `https` — SvelteKit dev server runs on HTTP) +- **Development**: `http://localhost:5173/api/auth/callback` (use `http`, not `https` - SvelteKit dev server runs on HTTP) - **Production**: `https://your-domain.com/api/auth/callback` -The hook automatically computes the callback URL from the request origin — no manual `afterSignInUrl` setup needed. +The hook automatically computes the callback URL from the request origin - no manual `afterSignInUrl` setup needed. -> **Local dev with self-signed TLS**: If your IdP uses a self-signed certificate, launch the dev server with `NODE_TLS_REJECT_UNAUTHORIZED=0 pnpm dev` (dev only — never use in production). +> **Local dev with self-signed TLS**: If your IdP uses a self-signed certificate, launch the dev server with `NODE_TLS_REJECT_UNAUTHORIZED=0 pnpm dev` (dev only - never use in production). ### Server-side route guard @@ -189,7 +189,7 @@ export const POST = createOrgSwitchHandler(); | Component | Description | |-----------|-------------| -| `` | Provider — wraps your app with auth context | +| `` | Provider - wraps your app with auth context | | `` | Renders children when signed in | | `` | Renders children when signed out | | `` | Renders children while auth state initializes | From a969f2cad0cf7e423833ffb1bce3557948b606c0 Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Tue, 30 Jun 2026 10:07:23 +0530 Subject: [PATCH 18/31] fix(svelte): implement critical-tier spec compliance gaps - IAMError class + ErrorCode enum with 25+ typed error codes - LoggerAdapter interface + DefaultLogger with PII sanitization - HTTPS enforcement in initialize() and resolveConfig() - PKCE flag removed (forced to true) - RFC 7009 token revocation on sign-out - signUp() redirect-based implementation - signInSilently() session-check implementation - changePassword() skeleton with NOT_IMPLEMENTED error - Callback.svelte component with loading/error/success states - reInitialize(), double-init guard, missing config validation - isLoading() tracking during initialize() - Logger replaces all bare console.* calls - All lint errors on new files fixed --- packages/svelte/src/ThunderIDSvelteClient.ts | 151 +++- .../__tests__/ThunderIDSvelteClient.test.ts | 31 +- .../src/components/auth/Callback.svelte | 103 +++ .../svelte/src/composables/useThunderID.ts | 2 +- packages/svelte/src/composables/useUser.ts | 2 +- packages/svelte/src/errors/IAMError.ts | 67 ++ packages/svelte/src/errors/index.ts | 20 + packages/svelte/src/index.ts | 9 + packages/svelte/src/logger/LoggerAdapter.ts | 75 ++ packages/svelte/src/logger/index.ts | 21 + packages/svelte/src/logger/sanitizer.ts | 52 ++ packages/svelte/src/models/config.ts | 4 +- packages/svelte/src/models/session.ts | 2 +- .../svelte/src/server/__tests__/guard.test.ts | 2 +- .../src/server/__tests__/session.test.ts | 2 +- packages/svelte/src/server/config.ts | 28 +- packages/svelte/src/server/getClient.ts | 2 +- packages/svelte/src/server/guard.ts | 2 +- packages/svelte/src/server/hooks.ts | 14 +- packages/svelte/src/server/refresh.ts | 4 +- packages/svelte/src/server/routes/callback.ts | 6 +- .../svelte/src/server/routes/orgSwitch.ts | 5 +- packages/svelte/src/server/routes/signin.ts | 10 +- packages/svelte/src/server/routes/signout.ts | 9 +- packages/svelte/src/server/session.ts | 6 +- pnpm-lock.yaml | 825 ++++++++++++++---- 26 files changed, 1214 insertions(+), 240 deletions(-) create mode 100644 packages/svelte/src/components/auth/Callback.svelte create mode 100644 packages/svelte/src/errors/IAMError.ts create mode 100644 packages/svelte/src/errors/index.ts create mode 100644 packages/svelte/src/logger/LoggerAdapter.ts create mode 100644 packages/svelte/src/logger/index.ts create mode 100644 packages/svelte/src/logger/sanitizer.ts diff --git a/packages/svelte/src/ThunderIDSvelteClient.ts b/packages/svelte/src/ThunderIDSvelteClient.ts index a9a6c6c..6644402 100644 --- a/packages/svelte/src/ThunderIDSvelteClient.ts +++ b/packages/svelte/src/ThunderIDSvelteClient.ts @@ -32,6 +32,8 @@ import { getMeOrganizations, MemoryCacheStore, } from '@thunderid/node'; +import {IAMError, ErrorCode} from './errors/IAMError'; +import {getLogger, type LoggerAdapter} from './logger/LoggerAdapter'; import type {ThunderIDSvelteConfig} from './models/config'; import type {ThunderIDSessionPayload} from './models/session'; @@ -39,6 +41,8 @@ class ThunderIDSvelteClient extends ThunderIDNodeClient { if (this.isInitialized) { - return true; + throw new IAMError({ + code: ErrorCode.ALREADY_INITIALIZED, + message: 'ThunderID SDK is already initialized. Call reset() first if you need to re-initialize.', + }); + } + + if (!config.baseUrl) { + throw new IAMError({ + code: ErrorCode.INVALID_CONFIGURATION, + message: 'baseUrl is required. Set THUNDERID_BASE_URL environment variable or pass it in the config.', + }); + } + + if (!config.baseUrl.startsWith('https://')) { + throw new IAMError({ + code: ErrorCode.INVALID_CONFIGURATION, + message: 'baseUrl must use HTTPS.', + }); + } + + if (!config.clientId) { + throw new IAMError({ + code: ErrorCode.INVALID_CONFIGURATION, + message: 'clientId is required. Set THUNDERID_CLIENT_ID environment variable or pass it in the config.', + }); } const authConfig: AuthClientConfig = { afterSignInUrl: config.afterSignInUrl ?? '/', afterSignOutUrl: config.afterSignOutUrl ?? '/', - baseUrl: config.baseUrl!, - clientId: config.clientId!, + baseUrl: config.baseUrl, + clientId: config.clientId, clientSecret: config.clientSecret ?? undefined, - enablePKCE: config.enablePKCE ?? true, + enablePKCE: true, scopes: config.scopes ?? ['openid', 'profile'], tokenRequest: config.tokenRequest ?? {authMethod: 'client_secret_post'}, } as AuthClientConfig; const resolvedStorage: Storage = storage ?? new MemoryCacheStore(); - const result: boolean = await super.initialize( - authConfig as unknown as AuthClientConfig, - resolvedStorage, - ); - this.isInitialized = true; - return result; + this._isLoading = true; + try { + const result: boolean = await super.initialize( + authConfig as unknown as AuthClientConfig, + resolvedStorage, + ); + this.isInitialized = true; + return result; + } finally { + this._isLoading = false; + } } override async reInitialize(config: Partial): Promise { + if (!this.isInitialized) { + throw new IAMError({ + code: ErrorCode.SDK_NOT_INITIALIZED, + message: 'SDK is not initialized. Call initialize() first.', + }); + } await super.reInitialize(config as any); return true; } + override isLoading(): boolean { + return this._isLoading; + } + + override getConfiguration(): AuthClientConfig { + const storageManager: any = this.getStorageManager(); + return storageManager?.getConfigData?.() as AuthClientConfig; + } + async rehydrateSessionFromPayload(session: ThunderIDSessionPayload): Promise { if (!this.isInitialized || !session?.sessionId || !session?.accessToken) { return; @@ -137,8 +185,74 @@ class ThunderIDSvelteClient extends ThunderIDNodeClient { - return undefined; + override async signUp(options?: Record): Promise { + const configData: any = await this.getStorageManager().getConfigData(); + const signUpUrl: string | undefined = configData?.signUpUrl as string | undefined; + const baseUrl: string | undefined = configData?.baseUrl as string | undefined; + const applicationId: string | undefined = configData?.applicationId as string | undefined; + + if (signUpUrl) { + window.location.href = signUpUrl; + return; + } + + if (baseUrl) { + let url = `${baseUrl}/accountrecoveryendpoint/register.do`; + if (applicationId) { + url += `?spId=${applicationId}`; + } + window.location.href = url; + return; + } + + throw new IAMError({ + code: ErrorCode.INVALID_CONFIGURATION, + message: 'Cannot sign up: no signUpUrl or baseUrl configured.', + }); + } + + override async signInSilently(_options?: Record): Promise { + if (!this.isInitialized) { + throw new IAMError({ + code: ErrorCode.SDK_NOT_INITIALIZED, + message: 'SDK is not initialized. Call initialize() first.', + }); + } + + try { + const signedIn: boolean = await this.isSignedIn(); + if (signedIn) { + const user: User = await this.getUser(); + return user; + } + return false; + } catch { + return false; + } + } + + async changePassword(_currentPassword: string, _newPassword: string): Promise { + throw new IAMError({ + code: ErrorCode.NOT_IMPLEMENTED, + message: 'changePassword() is not yet implemented. This will be available in a future release.', + }); + } + + async setSessionData(sessionData: Record, sessionId?: string): Promise { + const storageManager: any = this.getStorageManager(); + await storageManager.setSessionData(sessionData, sessionId); + } + + clearSessionData(sessionId?: string): void { + const storageManager: any = this.getStorageManager(); + storageManager.clearSession(sessionId); + } + + override getAllOrganizations(_options?: Record, _sessionId?: string): Promise { + throw new IAMError({ + code: ErrorCode.NOT_IMPLEMENTED, + message: 'getAllOrganizations() is not yet implemented. Use getMyOrganizations() instead.', + }); } public async getAuthorizeRequestUrl(customParams: Record, userId?: string): Promise { @@ -168,12 +282,12 @@ class ThunderIDSvelteClient extends ThunderIDNodeClient; } - override async getUserProfile(sessionId: string): Promise { + override async getUserProfile(sessionId?: string): Promise { const user: User = await this.getUser(sessionId); return {flattenedProfile: user, profile: user, schemas: []}; } - override async getCurrentOrganization(sessionId: string): Promise { + override async getCurrentOrganization(sessionId?: string): Promise { try { const idToken: IdToken = await this.getDecodedIdToken(sessionId); if (!idToken?.org_id) { @@ -189,7 +303,7 @@ class ThunderIDSvelteClient extends ThunderIDNodeClient { + override async getMyOrganizations(sessionId?: string): Promise { const accessToken: string = await this.getAccessToken(sessionId); const configData: any = await this.getStorageManager().getConfigData(); const baseUrl: string = (configData?.baseUrl ?? '') as string; @@ -202,10 +316,13 @@ class ThunderIDSvelteClient extends ThunderIDNodeClient { if (!organization.id) { - throw new Error('Organization ID is required for switching organizations.'); + throw new IAMError({ + code: ErrorCode.INVALID_INPUT, + message: 'Organization ID is required for switching organizations.', + }); } const exchangeConfig: TokenExchangeRequestConfig = { diff --git a/packages/svelte/src/__tests__/ThunderIDSvelteClient.test.ts b/packages/svelte/src/__tests__/ThunderIDSvelteClient.test.ts index decb1f3..480e9f9 100644 --- a/packages/svelte/src/__tests__/ThunderIDSvelteClient.test.ts +++ b/packages/svelte/src/__tests__/ThunderIDSvelteClient.test.ts @@ -20,7 +20,7 @@ import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest'; import type {ThunderIDSessionPayload} from '../models/session'; vi.mock('@thunderid/node', () => { - let storageData: Record = {}; + const storageData: Record = {}; class MockMemoryCacheStore { setItem(_key: string, _value: any): void {} @@ -98,6 +98,10 @@ vi.mock('@thunderid/node', () => { return Promise.resolve('/'); } + revokeAccessToken(_userId?: string): Promise { + return Promise.resolve(true); + } + getInstanceId(): number { return 0; } @@ -132,14 +136,33 @@ describe('ThunderIDSvelteClient', () => { }); describe('initialize', () => { - it('should initialize only once', async () => { + it('should initialize successfully with valid config', async () => { const client = ThunderIDSvelteClient.getInstance(); const r1 = await client.initialize({baseUrl: 'https://example.com', clientId: 'cid'}); - const r2 = await client.initialize({baseUrl: 'https://example.com', clientId: 'cid'}); expect(r1).toBe(true); - expect(r2).toBe(true); expect(client.isInitialized).toBe(true); }); + + it('should throw AlreadyInitialized on second call', async () => { + const client = ThunderIDSvelteClient.getInstance(); + await client.initialize({baseUrl: 'https://example.com', clientId: 'cid'}); + await expect(client.initialize({baseUrl: 'https://example.com', clientId: 'cid'})).rejects.toThrow(); + }); + + it('should throw InvalidConfiguration when baseUrl is missing', async () => { + const client = ThunderIDSvelteClient.getInstance(); + await expect(client.initialize({} as any)).rejects.toThrow(); + }); + + it('should throw InvalidConfiguration when baseUrl uses HTTP', async () => { + const client = ThunderIDSvelteClient.getInstance(); + await expect(client.initialize({baseUrl: 'http://example.com', clientId: 'cid'})).rejects.toThrow(); + }); + + it('should throw InvalidConfiguration when clientId is missing', async () => { + const client = ThunderIDSvelteClient.getInstance(); + await expect(client.initialize({baseUrl: 'https://example.com'} as any)).rejects.toThrow(); + }); }); describe('rehydrateSessionFromPayload', () => { diff --git a/packages/svelte/src/components/auth/Callback.svelte b/packages/svelte/src/components/auth/Callback.svelte new file mode 100644 index 0000000..383153c --- /dev/null +++ b/packages/svelte/src/components/auth/Callback.svelte @@ -0,0 +1,103 @@ + + +{#if status === 'loading'} + {#if loadingComponent} + {@render loadingComponent()} + {:else if children} + {@render children()} + {:else} +
Signing you in...
+ {/if} +{:else if status === 'error'} + {#if errorComponent} + {@render errorComponent({error: error ?? ''})} + {:else} +
Sign-in failed: {error}
+ {/if} +{:else if status === 'success'} + {#if children} + {@render children()} + {:else} +
Signed in successfully! Redirecting...
+ {/if} +{/if} diff --git a/packages/svelte/src/composables/useThunderID.ts b/packages/svelte/src/composables/useThunderID.ts index de51bb3..bf1fdf1 100644 --- a/packages/svelte/src/composables/useThunderID.ts +++ b/packages/svelte/src/composables/useThunderID.ts @@ -17,8 +17,8 @@ */ import {getThunderIDContext} from '../context'; -import {authState} from '../state.svelte'; import type {ThunderIDContext} from '../models/contexts'; +import {authState} from '../state.svelte'; export function useThunderID(): ThunderIDContext { const context = getThunderIDContext(); diff --git a/packages/svelte/src/composables/useUser.ts b/packages/svelte/src/composables/useUser.ts index 1bed2c9..fe168d6 100644 --- a/packages/svelte/src/composables/useUser.ts +++ b/packages/svelte/src/composables/useUser.ts @@ -17,8 +17,8 @@ */ import {getUserContext} from '../context'; -import {authState} from '../state.svelte'; import type {UserContextValue} from '../models/contexts'; +import {authState} from '../state.svelte'; export function useUser(): UserContextValue { const context = getUserContext(); diff --git a/packages/svelte/src/errors/IAMError.ts b/packages/svelte/src/errors/IAMError.ts new file mode 100644 index 0000000..c30c393 --- /dev/null +++ b/packages/svelte/src/errors/IAMError.ts @@ -0,0 +1,67 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export enum ErrorCode { + SDK_NOT_INITIALIZED = 'SDK_NOT_INITIALIZED', + ALREADY_INITIALIZED = 'ALREADY_INITIALIZED', + INVALID_CONFIGURATION = 'INVALID_CONFIGURATION', + INVALID_REDIRECT_URI = 'INVALID_REDIRECT_URI', + AUTHENTICATION_FAILED = 'AUTHENTICATION_FAILED', + USER_ACCOUNT_LOCKED = 'USER_ACCOUNT_LOCKED', + USER_ACCOUNT_DISABLED = 'USER_ACCOUNT_DISABLED', + SESSION_EXPIRED = 'SESSION_EXPIRED', + MFA_REQUIRED = 'MFA_REQUIRED', + MFA_FAILED = 'MFA_FAILED', + INVALID_GRANT = 'INVALID_GRANT', + CONSENT_REQUIRED = 'CONSENT_REQUIRED', + USER_ALREADY_EXISTS = 'USER_ALREADY_EXISTS', + INVALID_INPUT = 'INVALID_INPUT', + INVITATION_CODE_INVALID = 'INVITATION_CODE_INVALID', + INVITATION_CODE_EXPIRED = 'INVITATION_CODE_EXPIRED', + REGISTRATION_DISABLED = 'REGISTRATION_DISABLED', + RECOVERY_FAILED = 'RECOVERY_FAILED', + CONFIRMATION_CODE_INVALID = 'CONFIRMATION_CODE_INVALID', + CONFIRMATION_CODE_EXPIRED = 'CONFIRMATION_CODE_EXPIRED', + NETWORK_ERROR = 'NETWORK_ERROR', + REQUEST_TIMEOUT = 'REQUEST_TIMEOUT', + SERVER_ERROR = 'SERVER_ERROR', + UNKNOWN_ERROR = 'UNKNOWN_ERROR', + NOT_IMPLEMENTED = 'NOT_IMPLEMENTED', +} + +export class IAMError extends Error { + public readonly code: ErrorCode; + public readonly requestId?: string; + public override readonly cause?: Error; + public readonly statusCode?: number; + + constructor(opts: { + code: ErrorCode; + message: string; + cause?: Error; + requestId?: string; + statusCode?: number; + }) { + super(opts.message); + this.name = 'IAMError'; + this.code = opts.code; + this.cause = opts.cause; + this.requestId = opts.requestId; + this.statusCode = opts.statusCode; + } +} diff --git a/packages/svelte/src/errors/index.ts b/packages/svelte/src/errors/index.ts new file mode 100644 index 0000000..04fb294 --- /dev/null +++ b/packages/svelte/src/errors/index.ts @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export {IAMError, ErrorCode} from './IAMError'; +export type {ErrorCode as ErrorCodeType} from './IAMError'; diff --git a/packages/svelte/src/index.ts b/packages/svelte/src/index.ts index 4beaa37..933ff84 100644 --- a/packages/svelte/src/index.ts +++ b/packages/svelte/src/index.ts @@ -24,6 +24,7 @@ export {default as Loading} from './components/control/Loading.svelte'; export {default as SignInButton} from './components/actions/SignInButton.svelte'; export {default as SignOutButton} from './components/actions/SignOutButton.svelte'; export {default as SignUpButton} from './components/actions/SignUpButton.svelte'; +export {default as Callback} from './components/auth/Callback.svelte'; // ── Client ── export {default as ThunderIDSvelteClient} from './ThunderIDSvelteClient'; @@ -40,3 +41,11 @@ export type {ThunderIDSvelteConfig} from './models/config'; export type {ThunderIDSSRData, ThunderIDSessionPayload} from './models/session'; export type {BrandingPreference} from '@thunderid/node'; export type {ThunderIDContext, UserContextValue} from './models/contexts'; + +// ── Errors ── +export {IAMError, ErrorCode} from './errors/IAMError'; + +// ── Logger ── +export {DefaultLogger, setLogger, getLogger} from './logger/LoggerAdapter'; +export type {LoggerAdapter} from './logger/LoggerAdapter'; +export {sanitizeForLog, sanitizeTokenForLog} from './logger/sanitizer'; diff --git a/packages/svelte/src/logger/LoggerAdapter.ts b/packages/svelte/src/logger/LoggerAdapter.ts new file mode 100644 index 0000000..43b979e --- /dev/null +++ b/packages/svelte/src/logger/LoggerAdapter.ts @@ -0,0 +1,75 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {sanitizeForLog} from './sanitizer'; + +/* eslint-disable no-console -- DefaultLogger intentionally wraps console as the default log transport */ + +export interface LoggerAdapter { + debug(message: string, context?: Record): void; + info(message: string, context?: Record): void; + warn(message: string, context?: Record): void; + error(message: string, error?: Error, context?: Record): void; +} + +export class DefaultLogger implements LoggerAdapter { + private prefix: string; + + constructor(prefix = '[thunderid]') { + this.prefix = prefix; + } + + debug(message: string, context?: Record): void { + console.debug(this.prefix, sanitizeForLog(message), this.sanitizeContext(context)); + } + + info(message: string, context?: Record): void { + console.info(this.prefix, sanitizeForLog(message), this.sanitizeContext(context)); + } + + warn(message: string, context?: Record): void { + console.warn(this.prefix, sanitizeForLog(message), this.sanitizeContext(context)); + } + + error(message: string, error?: Error, context?: Record): void { + console.error(this.prefix, sanitizeForLog(message), error ?? '', this.sanitizeContext(context)); + } + + private sanitizeContext(context?: Record): Record | undefined { + if (!context) return undefined; + const sanitized: Record = {}; + for (const [key, value] of Object.entries(context)) { + if (typeof value === 'string') { + sanitized[key] = sanitizeForLog(value); + } else { + sanitized[key] = value; + } + } + return sanitized; + } +} + +let _logger: LoggerAdapter = new DefaultLogger(); + +export function setLogger(logger: LoggerAdapter): void { + _logger = logger; +} + +export function getLogger(): LoggerAdapter { + return _logger; +} diff --git a/packages/svelte/src/logger/index.ts b/packages/svelte/src/logger/index.ts new file mode 100644 index 0000000..39ff4f6 --- /dev/null +++ b/packages/svelte/src/logger/index.ts @@ -0,0 +1,21 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +export {DefaultLogger, setLogger, getLogger} from './LoggerAdapter'; +export type {LoggerAdapter} from './LoggerAdapter'; +export {sanitizeForLog, sanitizeTokenForLog} from './sanitizer'; diff --git a/packages/svelte/src/logger/sanitizer.ts b/packages/svelte/src/logger/sanitizer.ts new file mode 100644 index 0000000..7a53a90 --- /dev/null +++ b/packages/svelte/src/logger/sanitizer.ts @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +const TOKEN_PATTERN = /^[A-Za-z0-9\-_.]+\.[A-Za-z0-9\-_.]+\.[A-Za-z0-9\-_.]+$/; +const EMAIL_PATTERN = /([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g; +const PHONE_PATTERN = /(\+?\d[\d\s\-().]{6,}\d)/g; + +export function sanitizeForLog(input: string): string { + let result: string = input; + + result = result.replace(EMAIL_PATTERN, (_match: string, local: string, domain: string) => { + const maskedLocal: string = local.length > 1 ? local[0] + '*'.repeat(local.length - 1) : local[0]; + const domainParts: string[] = domain.split('.'); + const maskedDomain: string = domainParts.map((part: string, i: number) => + i === domainParts.length - 1 ? part : '*'.repeat(part.length), + ).join('.'); + return `${maskedLocal}@${maskedDomain}`; + }); + + result = result.replace(PHONE_PATTERN, (match: string) => { + const digits: string = match.replace(/\D/g, ''); + if (digits.length <= 4) return match; + const last4: string = digits.slice(-4); + const prefix: string = digits.length > 4 ? digits[0] : ''; + return prefix + '*'.repeat(digits.length - 4 - (prefix ? 1 : 0)) + last4; + }); + + return result; +} + +export function sanitizeTokenForLog(token: string): string { + if (TOKEN_PATTERN.test(token)) { + const parts: string[] = token.split('.'); + return `${parts[0]}.${parts[1].substring(0, 8)}...`; + } + return ''; +} diff --git a/packages/svelte/src/models/config.ts b/packages/svelte/src/models/config.ts index 31ac862..444072a 100644 --- a/packages/svelte/src/models/config.ts +++ b/packages/svelte/src/models/config.ts @@ -32,11 +32,11 @@ export interface ThunderIDSvelteConfig { clientId?: string; /** OAuth2 Client Secret (server-only, use THUNDERID_CLIENT_SECRET env var) */ clientSecret?: string; - /** Whether to enable PKCE (default: true) */ + /** @deprecated PKCE is always enabled and cannot be disabled. */ enablePKCE?: boolean; /** Token endpoint authentication method (default: 'client_secret_post') */ tokenRequest?: { - authMethod: 'client_secret_basic' | 'client_secret_post' | 'private_key_jwt' | 'none'; + authMethod?: 'client_secret_basic' | 'client_secret_post' | 'private_key_jwt' | 'none'; }; /** OAuth2 scopes to request */ scopes?: string | string[]; diff --git a/packages/svelte/src/models/session.ts b/packages/svelte/src/models/session.ts index a87c6fb..7835bbb 100644 --- a/packages/svelte/src/models/session.ts +++ b/packages/svelte/src/models/session.ts @@ -16,8 +16,8 @@ * under the License. */ -import type {JWTPayload} from 'jose'; import type {BrandingPreference, Organization, User, UserProfile} from '@thunderid/node'; +import type {JWTPayload} from 'jose'; /** * Payload stored in the session JWT cookie. diff --git a/packages/svelte/src/server/__tests__/guard.test.ts b/packages/svelte/src/server/__tests__/guard.test.ts index 39ad12b..bdd82db 100644 --- a/packages/svelte/src/server/__tests__/guard.test.ts +++ b/packages/svelte/src/server/__tests__/guard.test.ts @@ -16,9 +16,9 @@ * under the License. */ +import type {RequestEvent} from '@sveltejs/kit'; import {describe, it, expect, vi, beforeEach} from 'vitest'; import type {ThunderIDSSRData} from '../../models/session'; -import type {RequestEvent} from '@sveltejs/kit'; const mockRedirect = vi.fn(); vi.mock('@sveltejs/kit', () => ({ diff --git a/packages/svelte/src/server/__tests__/session.test.ts b/packages/svelte/src/server/__tests__/session.test.ts index 8326d50..f1766af 100644 --- a/packages/svelte/src/server/__tests__/session.test.ts +++ b/packages/svelte/src/server/__tests__/session.test.ts @@ -78,7 +78,7 @@ describe('Session token utilities', () => { }); const payload = await verifySessionToken(token); - const ttl = payload.exp! - payload.iat!; + const ttl = payload.exp - payload.iat; expect(ttl).toBeLessThanOrEqual(65); expect(ttl).toBeGreaterThanOrEqual(55); }); diff --git a/packages/svelte/src/server/config.ts b/packages/svelte/src/server/config.ts index ddcf993..ec7eea4 100644 --- a/packages/svelte/src/server/config.ts +++ b/packages/svelte/src/server/config.ts @@ -16,16 +16,17 @@ * under the License. */ +import {IAMError, ErrorCode} from '../errors/IAMError'; import type {ThunderIDSvelteConfig} from '../models/config'; export function resolveConfig(config?: ThunderIDSvelteConfig): ThunderIDSvelteConfig { - return { + const resolved: ThunderIDSvelteConfig = { afterSignInUrl: config?.afterSignInUrl ?? '/', afterSignOutUrl: config?.afterSignOutUrl ?? '/', baseUrl: config?.baseUrl || process.env['THUNDERID_BASE_URL'], clientId: config?.clientId || process.env['THUNDERID_CLIENT_ID'], clientSecret: config?.clientSecret || process.env['THUNDERID_CLIENT_SECRET'], - enablePKCE: config?.enablePKCE ?? true, + enablePKCE: true, scopes: config?.scopes ?? ['openid', 'profile'], sessionSecret: config?.sessionSecret || process.env['THUNDERID_SESSION_SECRET'], tokenRequest: config?.tokenRequest ?? {authMethod: 'client_secret_post'}, @@ -34,4 +35,27 @@ export function resolveConfig(config?: ThunderIDSvelteConfig): ThunderIDSvelteCo applicationId: config?.applicationId, preferences: config?.preferences, }; + + if (!resolved.baseUrl) { + throw new IAMError({ + code: ErrorCode.INVALID_CONFIGURATION, + message: 'baseUrl is required. Set THUNDERID_BASE_URL environment variable or pass it in config.', + }); + } + + if (!resolved.baseUrl.startsWith('https://')) { + throw new IAMError({ + code: ErrorCode.INVALID_CONFIGURATION, + message: 'baseUrl must use HTTPS.', + }); + } + + if (!resolved.clientId) { + throw new IAMError({ + code: ErrorCode.INVALID_CONFIGURATION, + message: 'clientId is required. Set THUNDERID_CLIENT_ID environment variable or pass it in config.', + }); + } + + return resolved; } diff --git a/packages/svelte/src/server/getClient.ts b/packages/svelte/src/server/getClient.ts index e1b41d4..42ee5f0 100644 --- a/packages/svelte/src/server/getClient.ts +++ b/packages/svelte/src/server/getClient.ts @@ -16,8 +16,8 @@ * under the License. */ -import ThunderIDSvelteClient from '../ThunderIDSvelteClient'; import type {ThunderIDSvelteConfig} from '../models/config'; +import ThunderIDSvelteClient from '../ThunderIDSvelteClient'; let clientInitialized = false; diff --git a/packages/svelte/src/server/guard.ts b/packages/svelte/src/server/guard.ts index a74efd5..8ff4f48 100644 --- a/packages/svelte/src/server/guard.ts +++ b/packages/svelte/src/server/guard.ts @@ -43,7 +43,7 @@ export function requireServerSession( ): ThunderIDSSRData { const ssrData: ThunderIDSSRData = event.locals.thunderid; - if (!ssrData || !ssrData.isSignedIn) { + if (!ssrData?.isSignedIn) { redirect(307, redirectTo || '/api/auth/signin'); } diff --git a/packages/svelte/src/server/hooks.ts b/packages/svelte/src/server/hooks.ts index e69de6b..7f6a71c 100644 --- a/packages/svelte/src/server/hooks.ts +++ b/packages/svelte/src/server/hooks.ts @@ -16,15 +16,15 @@ * under the License. */ -import type {BrandingPreference} from '@thunderid/node'; import type {Handle, RequestEvent} from '@sveltejs/kit'; +import type {BrandingPreference} from '@thunderid/node'; +import {resolveConfig} from './config'; +import {getClient} from './getClient'; +import {maybeRefreshToken} from './refresh'; +import {verifySessionToken, getSessionCookieName} from './session'; import type {ThunderIDSvelteConfig} from '../models/config'; import type {ThunderIDSSRData, ThunderIDSessionPayload} from '../models/session'; import ThunderIDSvelteClient from '../ThunderIDSvelteClient'; -import {getClient} from './getClient'; -import {verifySessionToken, getSessionCookieName} from './session'; -import {maybeRefreshToken} from './refresh'; -import {resolveConfig} from './config'; const CALLBACK_PATH = '/api/auth/callback'; @@ -32,7 +32,7 @@ export function createThunderIDHandle(config?: ThunderIDSvelteConfig): Handle { const resolvedConfig: ThunderIDSvelteConfig = resolveConfig(config); return async ({event, resolve}) => { - const callbackUrl: string = `${event.url.origin}${CALLBACK_PATH}`; + const callbackUrl = `${event.url.origin}${CALLBACK_PATH}`; const client: ThunderIDSvelteClient = await getClient({...resolvedConfig, afterSignInUrl: callbackUrl}); const sessionCookie: string | undefined = event.cookies.get(getSessionCookieName()); @@ -73,7 +73,7 @@ export function createThunderIDHandle(config?: ThunderIDSvelteConfig): Handle { ]); ssrData = { - brandingPreference: (branding as BrandingPreference) ?? null, + brandingPreference: (branding!) ?? null, isSignedIn: true, myOrganizations: myOrganizations as any[], organization: organization as any, diff --git a/packages/svelte/src/server/refresh.ts b/packages/svelte/src/server/refresh.ts index fd78f27..a6f4274 100644 --- a/packages/svelte/src/server/refresh.ts +++ b/packages/svelte/src/server/refresh.ts @@ -17,9 +17,9 @@ */ import type {RequestEvent} from '@sveltejs/kit'; +import {createSessionToken, getSessionCookieName, getSessionCookieOptions} from './session'; import type {ThunderIDSvelteConfig} from '../models/config'; import type {ThunderIDSessionPayload} from '../models/session'; -import {createSessionToken, getSessionCookieName, getSessionCookieOptions} from './session'; const REFRESH_SKEW_SECONDS = 60; @@ -52,7 +52,7 @@ export async function maybeRefreshToken( return null; } - const tokenEndpoint: string = `${config.baseUrl}/oauth2/token`; + const tokenEndpoint = `${config.baseUrl}/oauth2/token`; const body: URLSearchParams = new URLSearchParams({ client_id: config.clientId!, diff --git a/packages/svelte/src/server/routes/callback.ts b/packages/svelte/src/server/routes/callback.ts index c74df5c..1df6e42 100644 --- a/packages/svelte/src/server/routes/callback.ts +++ b/packages/svelte/src/server/routes/callback.ts @@ -17,13 +17,15 @@ */ import type {TokenResponse} from '@thunderid/node'; +import {getLogger} from '../../logger/LoggerAdapter'; import type {ThunderIDSvelteConfig} from '../../models/config'; import ThunderIDSvelteClient from '../../ThunderIDSvelteClient'; -import {issueSessionCookie, verifyTempSessionToken, getTempSessionCookieName, getSessionCookieName} from '../session'; import {resolveConfig} from '../config'; +import {issueSessionCookie, verifyTempSessionToken, getTempSessionCookieName, getSessionCookieName} from '../session'; export function createCallbackHandler(config?: ThunderIDSvelteConfig): (event: {url: URL; cookies: any}) => Promise { const resolvedConfig: ThunderIDSvelteConfig = resolveConfig(config); + const logger = getLogger(); return async (event) => { const client: ThunderIDSvelteClient = ThunderIDSvelteClient.getInstance(); @@ -61,7 +63,7 @@ export function createCallbackHandler(config?: ThunderIDSvelteConfig): (event: { )) as unknown as TokenResponse; } catch (err: unknown) { const message: string = (err as any)?.message ?? String(err); - console.error('[thunderid] callback signIn failed:', message); + logger.error('callback signIn failed', err instanceof Error ? err : new Error(message)); return new Response(JSON.stringify({error: message}), { status: 500, headers: {'content-type': 'application/json'}, diff --git a/packages/svelte/src/server/routes/orgSwitch.ts b/packages/svelte/src/server/routes/orgSwitch.ts index e7e647e..2de97b1 100644 --- a/packages/svelte/src/server/routes/orgSwitch.ts +++ b/packages/svelte/src/server/routes/orgSwitch.ts @@ -17,15 +17,17 @@ */ import type {Organization, TokenResponse} from '@thunderid/node'; +import {getLogger} from '../../logger/LoggerAdapter'; import type {ThunderIDSvelteConfig} from '../../models/config'; import ThunderIDSvelteClient from '../../ThunderIDSvelteClient'; -import {verifySessionToken, issueSessionCookie, getSessionCookieName} from '../session'; import {resolveConfig} from '../config'; +import {verifySessionToken, issueSessionCookie, getSessionCookieName} from '../session'; export function createOrgSwitchHandler( config?: ThunderIDSvelteConfig, ): (event: {request: Request; cookies: any; url: URL}) => Promise { const resolvedConfig: ThunderIDSvelteConfig = resolveConfig(config); + const logger = getLogger(); return async (event) => { const client: ThunderIDSvelteClient = ThunderIDSvelteClient.getInstance(); @@ -80,6 +82,7 @@ export function createOrgSwitchHandler( }); } catch (err: unknown) { const message: string = (err as any)?.message ?? 'Organization switch failed'; + logger.error('organization switch failed', err instanceof Error ? err : new Error(message)); return new Response(JSON.stringify({error: message}), { status: 500, headers: {'content-type': 'application/json'}, diff --git a/packages/svelte/src/server/routes/signin.ts b/packages/svelte/src/server/routes/signin.ts index d5e0302..949cd2e 100644 --- a/packages/svelte/src/server/routes/signin.ts +++ b/packages/svelte/src/server/routes/signin.ts @@ -17,17 +17,15 @@ */ import {generateSessionId} from '@thunderid/node'; +import {getLogger} from '../../logger/LoggerAdapter'; import type {ThunderIDSvelteConfig} from '../../models/config'; import ThunderIDSvelteClient from '../../ThunderIDSvelteClient'; -import {createTempSessionToken, getTempSessionCookieName, getTempSessionCookieOptions} from '../session'; import {resolveConfig} from '../config'; +import {createTempSessionToken, getTempSessionCookieName, getTempSessionCookieOptions} from '../session'; export function createSignInHandler(config?: ThunderIDSvelteConfig): (event: {url: URL; cookies: any}) => Promise { const resolvedConfig: ThunderIDSvelteConfig = resolveConfig(config); - - if (!resolvedConfig.baseUrl) { - console.warn('[thunderid] THUNDERID_BASE_URL is not set. Set it as an environment variable or pass it to createSignInHandler().'); - } + const logger = getLogger(); return async (event) => { try { @@ -51,7 +49,7 @@ export function createSignInHandler(config?: ThunderIDSvelteConfig): (event: {ur }); } catch (err: unknown) { const message: string = (err as any)?.message ?? String(err); - console.error('[thunderid] sign-in failed:', message); + logger.error('sign-in failed', err instanceof Error ? err : new Error(message)); return new Response(JSON.stringify({error: message}), { status: 500, headers: {'content-type': 'application/json'}, diff --git a/packages/svelte/src/server/routes/signout.ts b/packages/svelte/src/server/routes/signout.ts index dd5007a..1f803b0 100644 --- a/packages/svelte/src/server/routes/signout.ts +++ b/packages/svelte/src/server/routes/signout.ts @@ -16,22 +16,25 @@ * under the License. */ +import {getLogger} from '../../logger/LoggerAdapter'; import type {ThunderIDSvelteConfig} from '../../models/config'; import ThunderIDSvelteClient from '../../ThunderIDSvelteClient'; -import {getSessionCookieName} from '../session'; import {resolveConfig} from '../config'; +import {getSessionCookieName} from '../session'; export function createSignOutHandler(config?: ThunderIDSvelteConfig): (event: {cookies: any}) => Promise { const resolvedConfig: ThunderIDSvelteConfig = resolveConfig(config); + const logger = getLogger(); return async (event) => { const client: ThunderIDSvelteClient = ThunderIDSvelteClient.getInstance(); const redirectUrl: string = resolvedConfig.afterSignOutUrl || '/'; try { + await client.revokeAccessToken(); await client.signOut(); - } catch { - // silent + } catch (err: unknown) { + logger.warn('sign-out encountered an issue', {error: (err as any)?.message ?? String(err)}); } event.cookies.delete(getSessionCookieName(), {path: '/'}); diff --git a/packages/svelte/src/server/session.ts b/packages/svelte/src/server/session.ts index dc82c4d..23e730a 100644 --- a/packages/svelte/src/server/session.ts +++ b/packages/svelte/src/server/session.ts @@ -19,12 +19,14 @@ import {CookieConfig} from '@thunderid/node'; import type {IdToken, TokenResponse} from '@thunderid/node'; import {SignJWT, jwtVerify} from 'jose'; +import {getLogger} from '../logger/LoggerAdapter'; import type {ThunderIDSessionPayload} from '../models/session'; const DEFAULT_EXPIRY_SECONDS = 3600; function getSecret(sessionSecret?: string): Uint8Array { const secret: string | undefined = sessionSecret || process.env['THUNDERID_SESSION_SECRET']; + const logger = getLogger(); if (!secret) { if (process.env['NODE_ENV'] === 'production') { @@ -33,9 +35,7 @@ function getSecret(sessionSecret?: string): Uint8Array { 'Set it to a secure random string of at least 32 characters.', ); } - console.warn( - '[thunderid] Using default session secret for development. Set THUNDERID_SESSION_SECRET for production.', - ); + logger.warn('Using default session secret for development. Set THUNDERID_SESSION_SECRET for production.'); return new TextEncoder().encode('thunderid-dev-secret-not-for-production'); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c0f2df6..11e57b6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -150,6 +150,34 @@ importers: specifier: 'catalog:' version: 5.9.3 + apps/svelte-playground: + dependencies: + '@thunderid/svelte': + specifier: workspace:^ + version: link:../../packages/svelte + devDependencies: + '@sveltejs/adapter-auto': + specifier: ^7.0.1 + version: 7.0.1(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))) + '@sveltejs/kit': + specifier: ^2.63.0 + version: 2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@sveltejs/vite-plugin-svelte': + specifier: ^7.1.2 + version: 7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + svelte: + specifier: ^5.56.1 + version: 5.56.4(@typescript-eslint/types@8.61.1) + svelte-check: + specifier: ^4.6.0 + version: 4.7.1(picomatch@4.0.4)(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@6.0.3) + typescript: + specifier: ^6.0.3 + version: 6.0.3 + vite: + specifier: ^8.0.16 + version: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + packages/browser: dependencies: '@thunderid/javascript': @@ -194,7 +222,7 @@ importers: version: 24.7.2 '@vitest/browser-playwright': specifier: 'catalog:' - version: 4.1.8(playwright@1.60.0)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) + version: 4.1.8(playwright@1.60.0)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) eslint: specifier: 'catalog:' version: 9.39.4(jiti@2.7.0) @@ -212,13 +240,13 @@ importers: version: 6.1.3 rolldown: specifier: 'catalog:' - version: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) + version: 1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) packages/express: dependencies: @@ -258,13 +286,13 @@ importers: version: 6.1.3 rolldown: specifier: 'catalog:' - version: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) + version: 1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@22.15.3)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@22.15.3)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) packages/javascript: dependencies: @@ -295,13 +323,13 @@ importers: version: 6.1.3 rolldown: specifier: 'catalog:' - version: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) + version: 1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) packages/nextjs: dependencies: @@ -347,13 +375,13 @@ importers: version: 6.1.3 rolldown: specifier: 'catalog:' - version: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) + version: 1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) packages/node: dependencies: @@ -405,13 +433,13 @@ importers: version: 6.1.3 rolldown: specifier: 'catalog:' - version: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) + version: 1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) packages/nuxt: dependencies: @@ -439,7 +467,7 @@ importers: devDependencies: '@nuxt/devtools': specifier: 2.6.4 - version: 2.6.4(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)) + version: 2.6.4(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)) '@nuxt/module-builder': specifier: 1.0.1 version: 1.0.1(@nuxt/cli@3.36.0(@nuxt/schema@3.21.7)(cac@6.7.14)(magicast@0.5.3))(@vue/compiler-core@3.5.38)(esbuild@0.28.1)(typescript@5.9.3)(vue@3.5.30(typescript@5.9.3)) @@ -448,7 +476,7 @@ importers: version: 3.21.7 '@nuxt/test-utils': specifier: 3.17.2 - version: 3.17.2(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0) + version: 3.17.2(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(lightningcss@1.32.0)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0) '@thunderid/eslint-plugin': specifier: 'catalog:' version: 0.0.2(@typescript-eslint/utils@8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)(vitest@4.1.8)(vue-eslint-parser@10.4.1(eslint@9.39.4(jiti@2.7.0))) @@ -466,13 +494,13 @@ importers: version: 1.15.11 nuxt: specifier: 3.21.7 - version: 3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) + version: 3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) packages/react: dependencies: @@ -527,13 +555,13 @@ importers: version: 6.1.3 rolldown: specifier: 'catalog:' - version: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) + version: 1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) packages/react-router: dependencies: @@ -561,7 +589,7 @@ importers: version: 19.2.14 '@vitest/browser-playwright': specifier: 'catalog:' - version: 4.1.8(playwright@1.60.0)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) + version: 4.1.8(playwright@1.60.0)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) eslint: specifier: 'catalog:' version: 9.39.4(jiti@2.7.0) @@ -582,13 +610,13 @@ importers: version: 6.1.3 rolldown: specifier: 'catalog:' - version: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) + version: 1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) packages/svelte: dependencies: @@ -607,13 +635,13 @@ importers: devDependencies: '@sveltejs/kit': specifier: 'catalog:' - version: 2.68.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 2.68.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@sveltejs/package': specifier: 'catalog:' version: 2.5.8(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3) '@sveltejs/vite-plugin-svelte': specifier: 'catalog:' - version: 6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@thunderid/eslint-plugin': specifier: 'catalog:' version: 0.0.2(@typescript-eslint/utils@8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)(vitest@4.1.8)(vue-eslint-parser@10.4.1(eslint@9.39.4(jiti@2.7.0))) @@ -643,7 +671,7 @@ importers: version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) packages/tanstack-router: dependencies: @@ -686,13 +714,13 @@ importers: version: 6.1.3 rolldown: specifier: 'catalog:' - version: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) + version: 1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) packages/vue: dependencies: @@ -707,7 +735,7 @@ importers: version: 2.8.1 vue-router: specifier: '>=4.0.0' - version: 5.1.0(@vue/compiler-sfc@3.5.38)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)) + version: 5.1.0(@vue/compiler-sfc@3.5.38)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)) devDependencies: '@thunderid/eslint-plugin': specifier: 'catalog:' @@ -732,13 +760,13 @@ importers: version: 6.1.3 rolldown: specifier: 'catalog:' - version: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) + version: 1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) vue: specifier: 3.5.30 version: 3.5.30(typescript@5.9.3) @@ -979,6 +1007,9 @@ packages: '@emnapi/core@1.10.0': resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + '@emnapi/core@1.11.1': + resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + '@emnapi/core@1.4.5': resolution: {integrity: sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==} @@ -997,6 +1028,9 @@ packages: '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + '@emotion/babel-plugin@11.13.5': resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} @@ -1781,6 +1815,12 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 + '@napi-rs/wasm-runtime@1.1.6': + resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + '@next/env@15.5.18': resolution: {integrity: sha512-hAV85Ckd9QR6RvH04MEKwsfLTksvFpO47j9xwtoIuvuPnlwecpSi+uZTtm8HirVbtlI2Fnz//xpcSTjFdyJk+g==} @@ -2292,6 +2332,9 @@ packages: '@oxc-project/types@0.132.0': resolution: {integrity: sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==} + '@oxc-project/types@0.137.0': + resolution: {integrity: sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==} + '@oxc-project/types@0.95.0': resolution: {integrity: sha512-vACy7vhpMPhjEJhULNxrdR0D943TkA/MigMpJCHmBHvMXxRStRi/dPtTlfQ3uDwWSzRpT8z+7ImjZVf8JWBocQ==} @@ -2546,30 +2589,60 @@ packages: cpu: [arm64] os: [android] + '@rolldown/binding-android-arm64@1.1.3': + resolution: {integrity: sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + '@rolldown/binding-darwin-arm64@1.0.0-beta.45': resolution: {integrity: sha512-xjCv4CRVsSnnIxTuyH1RDJl5OEQ1c9JYOwfDAHddjJDxCw46ZX9q80+xq7Eok7KC4bRSZudMJllkvOKv0T9SeA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] + '@rolldown/binding-darwin-arm64@1.1.3': + resolution: {integrity: sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + '@rolldown/binding-darwin-x64@1.0.0-beta.45': resolution: {integrity: sha512-ddcO9TD3D/CLUa/l8GO8LHzBOaZqWg5ClMy3jICoxwCuoz47h9dtqPsIeTiB6yR501LQTeDsjA4lIFd7u3Ljfw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] + '@rolldown/binding-darwin-x64@1.1.3': + resolution: {integrity: sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + '@rolldown/binding-freebsd-x64@1.0.0-beta.45': resolution: {integrity: sha512-MBTWdrzW9w+UMYDUvnEuh0pQvLENkl2Sis15fHTfHVW7ClbGuez+RWopZudIDEGkpZXdeI4CkRXk+vdIIebrmg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] + '@rolldown/binding-freebsd-x64@1.1.3': + resolution: {integrity: sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.45': resolution: {integrity: sha512-4YgoCFiki1HR6oSg+GxxfzfnVCesQxLF1LEnw9uXS/MpBmuog0EOO2rYfy69rWP4tFZL9IWp6KEfGZLrZ7aUog==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] + '@rolldown/binding-linux-arm-gnueabihf@1.1.3': + resolution: {integrity: sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.45': resolution: {integrity: sha512-LE1gjAwQRrbCOorJJ7LFr10s5vqYf5a00V5Ea9wXcT2+56n5YosJkcp8eQ12FxRBv2YX8dsdQJb+ZTtYJwb6XQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2577,6 +2650,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-arm64-gnu@1.1.3': + resolution: {integrity: sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.45': resolution: {integrity: sha512-tdy8ThO/fPp40B81v0YK3QC+KODOmzJzSUOO37DinQxzlTJ026gqUSOM8tzlVixRbQJltgVDCTYF8HNPRErQTA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2584,6 +2664,27 @@ packages: os: [linux] libc: [musl] + '@rolldown/binding-linux-arm64-musl@1.1.3': + resolution: {integrity: sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.1.3': + resolution: {integrity: sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.1.3': + resolution: {integrity: sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.45': resolution: {integrity: sha512-lS082ROBWdmOyVY/0YB3JmsiClaWoxvC+dA8/rbhyB9VLkvVEaihLEOr4CYmrMse151C4+S6hCw6oa1iewox7g==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2591,6 +2692,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-x64-gnu@1.1.3': + resolution: {integrity: sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-x64-musl@1.0.0-beta.45': resolution: {integrity: sha512-Hi73aYY0cBkr1/SvNQqH8Cd+rSV6S9RB5izCv0ySBcRnd/Wfn5plguUoGYwBnhHgFbh6cPw9m2dUVBR6BG1gxA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2598,23 +2706,47 @@ packages: os: [linux] libc: [musl] + '@rolldown/binding-linux-x64-musl@1.1.3': + resolution: {integrity: sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + '@rolldown/binding-openharmony-arm64@1.0.0-beta.45': resolution: {integrity: sha512-fljEqbO7RHHogNDxYtTzr+GNjlfOx21RUyGmF+NrkebZ8emYYiIqzPxsaMZuRx0rgZmVmliOzEp86/CQFDKhJQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] + '@rolldown/binding-openharmony-arm64@1.1.3': + resolution: {integrity: sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + '@rolldown/binding-wasm32-wasi@1.0.0-beta.45': resolution: {integrity: sha512-ZJDB7lkuZE9XUnWQSYrBObZxczut+8FZ5pdanm8nNS1DAo8zsrPuvGwn+U3fwU98WaiFsNrA4XHngesCGr8tEQ==} engines: {node: '>=14.0.0'} cpu: [wasm32] + '@rolldown/binding-wasm32-wasi@1.1.3': + resolution: {integrity: sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.45': resolution: {integrity: sha512-zyzAjItHPUmxg6Z8SyRhLdXlJn3/D9KL5b9mObUrBHhWS/GwRH4665xCiFqeuktAhhWutqfc+rOV2LjK4VYQGQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] + '@rolldown/binding-win32-arm64-msvc@1.1.3': + resolution: {integrity: sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.45': resolution: {integrity: sha512-wODcGzlfxqS6D7BR0srkJk3drPwXYLu7jPHN27ce2c4PUnVVmJnp9mJzUQGT4LpmHmmVdMZ+P6hKvyTGBzc1CA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2627,6 +2759,12 @@ packages: cpu: [x64] os: [win32] + '@rolldown/binding-win32-x64-msvc@1.1.3': + resolution: {integrity: sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@rolldown/pluginutils@1.0.0-beta.45': resolution: {integrity: sha512-Le9ulGCrD8ggInzWw/k2J8QcbPz7eGIOWqfJ2L+1R0Opm7n6J37s2hiDWlh6LJN0Lk9L5sUzMvRHKW7UxBZsQA==} @@ -2886,6 +3024,11 @@ packages: peerDependencies: acorn: ^8.9.0 + '@sveltejs/adapter-auto@7.0.1': + resolution: {integrity: sha512-dvuPm1E7M9NI/+canIQ6KKQDU2AkEefEZ2Dp7cY6uKoPq9Z/PhOXABe526UdW2mN986gjVkuSLkOYIBnS/M2LQ==} + peerDependencies: + '@sveltejs/kit': ^2.0.0 + '@sveltejs/kit@2.68.0': resolution: {integrity: sha512-PdKiWsqinAoubVsSiRgVFkg3MHzGhQPnwQ8VxnGQKpZYijpapZ3UHHBje0GeByt2TvfjHPw+kxV+dNK2RIZg9g==} engines: {node: '>=18.13'} @@ -2928,6 +3071,13 @@ packages: svelte: ^5.0.0 vite: ^6.3.0 || ^7.0.0 + '@sveltejs/vite-plugin-svelte@7.1.2': + resolution: {integrity: sha512-DrUBA2UXRfDmUX/ZTiEopd3X40yavsJF1FX2RygcuIScHL7o5YX1fMvoYnDhjeJQC4weCOklirpNWlcb2NiSeA==} + engines: {node: ^20.19 || ^22.12 || >=24} + peerDependencies: + svelte: ^5.46.4 + vite: ^8.0.0-beta.7 || ^8.0.0 + '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} @@ -2989,6 +3139,9 @@ packages: '@tybys/wasm-util@0.10.2': resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + '@tybys/wasm-util@0.9.0': resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} @@ -5299,6 +5452,80 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + lilconfig@3.1.3: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} @@ -6258,6 +6485,11 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rolldown@1.1.3: + resolution: {integrity: sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + rollup-plugin-dts@6.4.1: resolution: {integrity: sha512-l//F3Zf7ID5GoOfLfD8kroBjQKEKpy1qfhtAdnpibFZMffPaylrg1CoDC2vGkPeTeyxUe4bVFCln2EFuL7IGGg==} engines: {node: '>=20'} @@ -6829,6 +7061,11 @@ packages: engines: {node: '>=14.17'} hasBin: true + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + ufo@1.6.4: resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} @@ -7168,6 +7405,49 @@ packages: yaml: optional: true + vite@8.1.0: + resolution: {integrity: sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.3.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + vitefu@1.1.3: resolution: {integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==} peerDependencies: @@ -7744,6 +8024,12 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/core@1.11.1': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + '@emnapi/core@1.4.5': dependencies: '@emnapi/wasi-threads': 1.0.4 @@ -7772,6 +8058,11 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + '@emotion/babel-plugin@11.13.5': dependencies: '@babel/helper-module-imports': 7.29.7 @@ -8319,13 +8610,20 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true - '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1)': + '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: - '@emnapi/core': 1.10.0 + '@emnapi/core': 1.11.1 '@emnapi/runtime': 1.11.1 '@tybys/wasm-util': 0.10.2 optional: true + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.3 + optional: true + '@next/env@15.5.18': {} '@next/swc-darwin-arm64@15.5.18': @@ -8404,19 +8702,19 @@ snapshots: '@nuxt/devalue@2.0.2': {} - '@nuxt/devtools-kit@2.6.4(magicast@0.3.5)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + '@nuxt/devtools-kit@2.6.4(magicast@0.3.5)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': dependencies: '@nuxt/kit': 3.21.7(magicast@0.3.5) execa: 8.0.1 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) transitivePeerDependencies: - magicast - '@nuxt/devtools-kit@3.2.4(magicast@0.5.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + '@nuxt/devtools-kit@3.2.4(magicast@0.5.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': dependencies: '@nuxt/kit': 4.4.8(magicast@0.5.3) execa: 8.0.1 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) transitivePeerDependencies: - magicast @@ -8442,12 +8740,12 @@ snapshots: pkg-types: 2.3.1 semver: 7.8.5 - '@nuxt/devtools@2.6.4(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3))': + '@nuxt/devtools@2.6.4(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3))': dependencies: - '@nuxt/devtools-kit': 2.6.4(magicast@0.3.5)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@nuxt/devtools-kit': 2.6.4(magicast@0.3.5)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@nuxt/devtools-wizard': 2.6.4 '@nuxt/kit': 3.21.7(magicast@0.3.5) - '@vue/devtools-core': 7.7.9(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)) + '@vue/devtools-core': 7.7.9(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)) '@vue/devtools-kit': 7.7.9 birpc: 2.9.0 consola: 3.4.2 @@ -8472,9 +8770,9 @@ snapshots: sirv: 3.0.2 structured-clone-es: 1.0.0 tinyglobby: 0.2.17 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vite-plugin-inspect: 11.4.1(@nuxt/kit@3.21.7(magicast@0.5.3))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) - vite-plugin-vue-tracer: 1.4.0(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite-plugin-inspect: 11.4.1(@nuxt/kit@3.21.7(magicast@0.5.3))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vite-plugin-vue-tracer: 1.4.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)) which: 5.0.0 ws: 8.21.0 transitivePeerDependencies: @@ -8483,9 +8781,9 @@ snapshots: - utf-8-validate - vue - '@nuxt/devtools@3.2.4(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3))': + '@nuxt/devtools@3.2.4(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3))': dependencies: - '@nuxt/devtools-kit': 3.2.4(magicast@0.5.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@nuxt/devtools-kit': 3.2.4(magicast@0.5.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@nuxt/devtools-wizard': 3.2.4 '@nuxt/kit': 4.4.8(magicast@0.5.3) '@vue/devtools-core': 8.1.3(vue@3.5.38(typescript@5.9.3)) @@ -8513,9 +8811,9 @@ snapshots: sirv: 3.0.2 structured-clone-es: 2.0.0 tinyglobby: 0.2.17 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vite-plugin-inspect: 11.4.1(@nuxt/kit@4.4.8(magicast@0.5.3))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) - vite-plugin-vue-tracer: 1.4.0(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite-plugin-inspect: 11.4.1(@nuxt/kit@4.4.8(magicast@0.5.3))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vite-plugin-vue-tracer: 1.4.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) which: 6.0.1 ws: 8.21.0 transitivePeerDependencies: @@ -8624,7 +8922,7 @@ snapshots: - vue - vue-tsc - '@nuxt/nitro-server@3.21.7(db0@0.3.4)(ioredis@5.11.1)(magicast@0.5.3)(nuxt@3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(oxc-parser@0.132.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(srvx@0.11.17)(typescript@5.9.3)': + '@nuxt/nitro-server@3.21.7(db0@0.3.4)(ioredis@5.11.1)(magicast@0.5.3)(nuxt@3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(oxc-parser@0.132.0)(rolldown@1.1.3)(srvx@0.11.17)(typescript@5.9.3)': dependencies: '@nuxt/devalue': 2.0.2 '@nuxt/kit': 3.21.7(magicast@0.5.3) @@ -8641,8 +8939,8 @@ snapshots: impound: 1.1.5 klona: 2.0.6 mocked-exports: 0.1.1 - nitropack: 2.13.4(oxc-parser@0.132.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(srvx@0.11.17) - nuxt: 3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) + nitropack: 2.13.4(oxc-parser@0.132.0)(rolldown@1.1.3)(srvx@0.11.17) + nuxt: 3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) ohash: 2.0.11 pathe: 2.0.3 pkg-types: 2.3.1 @@ -8709,7 +9007,7 @@ snapshots: rc9: 3.0.1 std-env: 4.1.0 - '@nuxt/test-utils@3.17.2(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0)': + '@nuxt/test-utils@3.17.2(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(lightningcss@1.32.0)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0)': dependencies: '@nuxt/kit': 3.21.7(magicast@0.5.3) '@nuxt/schema': 3.21.7 @@ -8734,14 +9032,14 @@ snapshots: tinyexec: 0.3.2 ufo: 1.6.4 unplugin: 2.3.11 - vite: 6.4.3(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vitest-environment-nuxt: 1.0.1(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0) + vite: 6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) + vitest-environment-nuxt: 1.0.1(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(lightningcss@1.32.0)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0) vue: 3.5.30(typescript@5.9.3) optionalDependencies: '@playwright/test': 1.60.0 '@vue/test-utils': 2.4.6 playwright-core: 1.60.0 - vitest: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vitest: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) transitivePeerDependencies: - '@types/node' - jiti @@ -8757,12 +9055,12 @@ snapshots: - typescript - yaml - '@nuxt/vite-builder@3.21.7(@types/node@24.7.2)(eslint@9.39.4(jiti@2.7.0))(magicast@0.5.3)(nuxt@3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3))(yaml@2.9.0)': + '@nuxt/vite-builder@3.21.7(@types/node@24.7.2)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.32.0)(magicast@0.5.3)(nuxt@3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3))(yaml@2.9.0)': dependencies: '@nuxt/kit': 3.21.7(magicast@0.5.3) '@rollup/plugin-replace': 6.0.3(rollup@4.62.2) - '@vitejs/plugin-vue': 6.0.7(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) - '@vitejs/plugin-vue-jsx': 5.1.5(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) + '@vitejs/plugin-vue': 6.0.7(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) + '@vitejs/plugin-vue-jsx': 5.1.5(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) autoprefixer: 10.5.0(postcss@8.5.15) consola: 3.4.2 cssnano: 7.1.9(postcss@8.5.15) @@ -8776,7 +9074,7 @@ snapshots: magic-string: 0.30.21 mlly: 1.8.2 mocked-exports: 0.1.1 - nuxt: 3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) + nuxt: 3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) nypm: 0.6.7 ohash: 2.0.11 pathe: 2.0.3 @@ -8787,14 +9085,14 @@ snapshots: std-env: 4.1.0 ufo: 1.6.4 unenv: 2.0.0-rc.24 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vite-node: 5.3.0(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vite-plugin-checker: 0.13.0(eslint@9.39.4(jiti@2.7.0))(optionator@0.9.4)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) + vite-node: 5.3.0(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) + vite-plugin-checker: 0.13.0(eslint@9.39.4(jiti@2.7.0))(optionator@0.9.4)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0)) vue: 3.5.38(typescript@5.9.3) vue-bundle-renderer: 2.2.0 optionalDependencies: - rolldown: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - rollup-plugin-visualizer: 7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2) + rolldown: 1.1.3 + rollup-plugin-visualizer: 7.0.1(rolldown@1.1.3)(rollup@4.62.2) transitivePeerDependencies: - '@biomejs/biome' - '@types/node' @@ -8982,6 +9280,8 @@ snapshots: '@oxc-project/types@0.132.0': {} + '@oxc-project/types@0.137.0': {} + '@oxc-project/types@0.95.0': {} '@oxc-transform/binding-android-arm-eabi@0.132.0': @@ -9139,58 +9439,99 @@ snapshots: '@rolldown/binding-android-arm64@1.0.0-beta.45': optional: true + '@rolldown/binding-android-arm64@1.1.3': + optional: true + '@rolldown/binding-darwin-arm64@1.0.0-beta.45': optional: true + '@rolldown/binding-darwin-arm64@1.1.3': + optional: true + '@rolldown/binding-darwin-x64@1.0.0-beta.45': optional: true + '@rolldown/binding-darwin-x64@1.1.3': + optional: true + '@rolldown/binding-freebsd-x64@1.0.0-beta.45': optional: true + '@rolldown/binding-freebsd-x64@1.1.3': + optional: true + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.45': optional: true + '@rolldown/binding-linux-arm-gnueabihf@1.1.3': + optional: true + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.45': optional: true + '@rolldown/binding-linux-arm64-gnu@1.1.3': + optional: true + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.45': optional: true + '@rolldown/binding-linux-arm64-musl@1.1.3': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.1.3': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.1.3': + optional: true + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.45': optional: true + '@rolldown/binding-linux-x64-gnu@1.1.3': + optional: true + '@rolldown/binding-linux-x64-musl@1.0.0-beta.45': optional: true + '@rolldown/binding-linux-x64-musl@1.1.3': + optional: true + '@rolldown/binding-openharmony-arm64@1.0.0-beta.45': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@rolldown/binding-openharmony-arm64@1.1.3': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: - '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1)': + '@rolldown/binding-wasm32-wasi@1.1.3': dependencies: - '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) optional: true '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.45': optional: true + '@rolldown/binding-win32-arm64-msvc@1.1.3': + optional: true + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.45': optional: true '@rolldown/binding-win32-x64-msvc@1.0.0-beta.45': optional: true + '@rolldown/binding-win32-x64-msvc@1.1.3': + optional: true + '@rolldown/pluginutils@1.0.0-beta.45': {} '@rolldown/pluginutils@1.0.1': {} @@ -9367,11 +9708,15 @@ snapshots: dependencies: acorn: 8.17.0 - '@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + '@sveltejs/adapter-auto@7.0.1(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))': + dependencies: + '@sveltejs/kit': 2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + + '@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': dependencies: '@standard-schema/spec': 1.1.0 '@sveltejs/acorn-typescript': 1.0.10(acorn@8.17.0) - '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@types/cookie': 0.6.0 acorn: 8.17.0 cookie: 0.6.0 @@ -9383,10 +9728,30 @@ snapshots: set-cookie-parser: 3.1.1 sirv: 3.0.2 svelte: 5.56.4(@typescript-eslint/types@8.61.1) - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) optionalDependencies: typescript: 5.9.3 + '@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + dependencies: + '@standard-schema/spec': 1.1.0 + '@sveltejs/acorn-typescript': 1.0.10(acorn@8.17.0) + '@sveltejs/vite-plugin-svelte': 7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@types/cookie': 0.6.0 + acorn: 8.17.0 + cookie: 0.6.0 + devalue: 5.8.1 + esm-env: 1.2.2 + kleur: 4.1.5 + magic-string: 0.30.21 + mrmime: 2.0.1 + set-cookie-parser: 3.1.1 + sirv: 3.0.2 + svelte: 5.56.4(@typescript-eslint/types@8.61.1) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + optionalDependencies: + typescript: 6.0.3 + '@sveltejs/load-config@0.2.0': {} '@sveltejs/package@2.5.8(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3)': @@ -9400,22 +9765,31 @@ snapshots: transitivePeerDependencies: - typescript - '@sveltejs/vite-plugin-svelte-inspector@5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + '@sveltejs/vite-plugin-svelte-inspector@5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + dependencies: + '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + obug: 2.1.3 + svelte: 5.56.4(@typescript-eslint/types@8.61.1) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + + '@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@sveltejs/vite-plugin-svelte-inspector': 5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + deepmerge: 4.3.1 + magic-string: 0.30.21 obug: 2.1.3 svelte: 5.56.4(@typescript-eslint/types@8.61.1) - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vitefu: 1.1.3(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) - '@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + '@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) deepmerge: 4.3.1 magic-string: 0.30.21 obug: 2.1.3 svelte: 5.56.4(@typescript-eslint/types@8.61.1) - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vitefu: 1.1.3(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vitefu: 1.1.3(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@swc/helpers@0.5.15': dependencies: @@ -9513,6 +9887,11 @@ snapshots: tslib: 2.8.1 optional: true + '@tybys/wasm-util@0.10.3': + dependencies: + tslib: 2.8.1 + optional: true + '@tybys/wasm-util@0.9.0': dependencies: tslib: 2.8.1 @@ -9836,31 +10215,31 @@ snapshots: - rollup - supports-color - '@vitejs/plugin-vue-jsx@5.1.5(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3))': + '@vitejs/plugin-vue-jsx@5.1.5(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3))': dependencies: '@babel/core': 7.29.7 '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) '@rolldown/pluginutils': 1.0.1 '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.7) - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) vue: 3.5.38(typescript@5.9.3) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@6.0.7(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.7(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) vue: 3.5.38(typescript@5.9.3) - '@vitest/browser-playwright@4.1.8(playwright@1.60.0)(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8)': + '@vitest/browser-playwright@4.1.8(playwright@1.60.0)(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8)': dependencies: - '@vitest/browser': 4.1.8(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) - '@vitest/mocker': 4.1.8(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@vitest/browser': 4.1.8(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) + '@vitest/mocker': 4.1.8(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) playwright: 1.60.0 tinyrainbow: 3.1.0 - vitest: 4.1.8(@types/node@22.15.3)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vitest: 4.1.8(@types/node@22.15.3)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw @@ -9868,29 +10247,29 @@ snapshots: - vite optional: true - '@vitest/browser-playwright@4.1.8(playwright@1.60.0)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8)': + '@vitest/browser-playwright@4.1.8(playwright@1.60.0)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8)': dependencies: - '@vitest/browser': 4.1.8(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) - '@vitest/mocker': 4.1.8(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@vitest/browser': 4.1.8(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) + '@vitest/mocker': 4.1.8(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) playwright: 1.60.0 tinyrainbow: 3.1.0 - vitest: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vitest: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser@4.1.8(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8)': + '@vitest/browser@4.1.8(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.8(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@vitest/mocker': 4.1.8(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@vitest/utils': 4.1.8 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.8(@types/node@22.15.3)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vitest: 4.1.8(@types/node@22.15.3)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) ws: 8.21.0 transitivePeerDependencies: - bufferutil @@ -9899,16 +10278,16 @@ snapshots: - vite optional: true - '@vitest/browser@4.1.8(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8)': + '@vitest/browser@4.1.8(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.8(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@vitest/mocker': 4.1.8(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@vitest/utils': 4.1.8 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vitest: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) ws: 8.21.0 transitivePeerDependencies: - bufferutil @@ -9924,7 +10303,7 @@ snapshots: optionalDependencies: '@typescript-eslint/eslint-plugin': 8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) typescript: 5.9.3 - vitest: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vitest: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) transitivePeerDependencies: - supports-color @@ -9937,21 +10316,21 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.8(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + '@vitest/mocker@4.1.8(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.8 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - '@vitest/mocker@4.1.8(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + '@vitest/mocker@4.1.8(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.8 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) '@vitest/pretty-format@4.1.8': dependencies: @@ -10098,14 +10477,14 @@ snapshots: dependencies: '@vue/devtools-kit': 8.1.3 - '@vue/devtools-core@7.7.9(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3))': + '@vue/devtools-core@7.7.9(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3))': dependencies: '@vue/devtools-kit': 7.7.9 '@vue/devtools-shared': 7.7.9 mitt: 3.0.1 nanoid: 5.1.15 pathe: 2.0.3 - vite-hot-client: 2.2.0(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vite-hot-client: 2.2.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) vue: 3.5.30(typescript@5.9.3) transitivePeerDependencies: - vite @@ -12214,6 +12593,55 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} @@ -12461,7 +12889,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - nitropack@2.13.4(oxc-parser@0.132.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(srvx@0.11.17): + nitropack@2.13.4(oxc-parser@0.132.0)(rolldown@1.1.3)(srvx@0.11.17): dependencies: '@cloudflare/kv-asset-handler': 0.4.2 '@rollup/plugin-alias': 6.0.0(rollup@4.62.2) @@ -12514,7 +12942,7 @@ snapshots: pretty-bytes: 7.1.0 radix3: 1.1.2 rollup: 4.62.2 - rollup-plugin-visualizer: 7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2) + rollup-plugin-visualizer: 7.0.1(rolldown@1.1.3)(rollup@4.62.2) scule: 1.3.0 semver: 7.8.5 serve-placeholder: 2.0.2 @@ -12526,7 +12954,7 @@ snapshots: uncrypto: 0.1.3 unctx: 2.5.0 unenv: 2.0.0-rc.24 - unimport: 6.3.0(oxc-parser@0.132.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)) + unimport: 6.3.0(oxc-parser@0.132.0)(rolldown@1.1.3) unplugin-utils: 0.3.1 unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1) untyped: 2.0.0 @@ -12616,16 +13044,16 @@ snapshots: dependencies: boolbase: 1.0.0 - nuxt@3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0): + nuxt@3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0): dependencies: '@dxup/nuxt': 0.4.1(magicast@0.5.3)(typescript@5.9.3) '@nuxt/cli': 3.36.0(@nuxt/schema@3.21.7)(cac@6.7.14)(magicast@0.5.3) - '@nuxt/devtools': 3.2.4(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) + '@nuxt/devtools': 3.2.4(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) '@nuxt/kit': 3.21.7(magicast@0.5.3) - '@nuxt/nitro-server': 3.21.7(db0@0.3.4)(ioredis@5.11.1)(magicast@0.5.3)(nuxt@3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(oxc-parser@0.132.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(srvx@0.11.17)(typescript@5.9.3) + '@nuxt/nitro-server': 3.21.7(db0@0.3.4)(ioredis@5.11.1)(magicast@0.5.3)(nuxt@3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(oxc-parser@0.132.0)(rolldown@1.1.3)(srvx@0.11.17)(typescript@5.9.3) '@nuxt/schema': 3.21.7 '@nuxt/telemetry': 2.8.0(@nuxt/kit@3.21.7(magicast@0.5.3)) - '@nuxt/vite-builder': 3.21.7(@types/node@24.7.2)(eslint@9.39.4(jiti@2.7.0))(magicast@0.5.3)(nuxt@3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3))(yaml@2.9.0) + '@nuxt/vite-builder': 3.21.7(@types/node@24.7.2)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.32.0)(magicast@0.5.3)(nuxt@3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3))(yaml@2.9.0) '@unhead/vue': 2.1.15(vue@3.5.38(typescript@5.9.3)) '@vue/shared': 3.5.38 c12: 3.3.4(magicast@0.5.3) @@ -12656,7 +13084,7 @@ snapshots: oxc-minify: 0.132.0 oxc-parser: 0.132.0 oxc-transform: 0.132.0 - oxc-walker: 1.0.0(oxc-parser@0.132.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)) + oxc-walker: 1.0.0(oxc-parser@0.132.0)(rolldown@1.1.3) pathe: 2.0.3 perfect-debounce: 2.1.0 pkg-types: 2.3.1 @@ -12669,7 +13097,7 @@ snapshots: ultrahtml: 1.6.0 uncrypto: 0.1.3 unctx: 2.5.0 - unimport: 6.3.0(oxc-parser@0.132.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)) + unimport: 6.3.0(oxc-parser@0.132.0)(rolldown@1.1.3) unplugin: 3.0.0 unplugin-vue-router: 0.19.2(@vue/compiler-sfc@3.5.38)(vue-router@4.6.4(vue@3.5.38(typescript@5.9.3)))(vue@3.5.38(typescript@5.9.3)) untyped: 2.0.0 @@ -13052,12 +13480,12 @@ snapshots: '@oxc-transform/binding-win32-ia32-msvc': 0.132.0 '@oxc-transform/binding-win32-x64-msvc': 0.132.0 - oxc-walker@1.0.0(oxc-parser@0.132.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)): + oxc-walker@1.0.0(oxc-parser@0.132.0)(rolldown@1.1.3): dependencies: magic-regexp: 0.11.0 optionalDependencies: oxc-parser: 0.132.0 - rolldown: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + rolldown: 1.1.3 p-limit@3.1.0: dependencies: @@ -13520,7 +13948,7 @@ snapshots: glob: 13.0.6 package-json-from-dist: 1.0.1 - rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): + rolldown@1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1): dependencies: '@oxc-project/types': 0.95.0 '@rolldown/pluginutils': 1.0.0-beta.45 @@ -13535,37 +13963,34 @@ snapshots: '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.45 '@rolldown/binding-linux-x64-musl': 1.0.0-beta.45 '@rolldown/binding-openharmony-arm64': 1.0.0-beta.45 - '@rolldown/binding-wasm32-wasi': 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.45 '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.45 '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.45 transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' - optional: true - rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1): + rolldown@1.1.3: dependencies: - '@oxc-project/types': 0.95.0 - '@rolldown/pluginutils': 1.0.0-beta.45 + '@oxc-project/types': 0.137.0 + '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-beta.45 - '@rolldown/binding-darwin-arm64': 1.0.0-beta.45 - '@rolldown/binding-darwin-x64': 1.0.0-beta.45 - '@rolldown/binding-freebsd-x64': 1.0.0-beta.45 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.45 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.45 - '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.45 - '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.45 - '@rolldown/binding-linux-x64-musl': 1.0.0-beta.45 - '@rolldown/binding-openharmony-arm64': 1.0.0-beta.45 - '@rolldown/binding-wasm32-wasi': 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) - '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.45 - '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.45 - '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.45 - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' + '@rolldown/binding-android-arm64': 1.1.3 + '@rolldown/binding-darwin-arm64': 1.1.3 + '@rolldown/binding-darwin-x64': 1.1.3 + '@rolldown/binding-freebsd-x64': 1.1.3 + '@rolldown/binding-linux-arm-gnueabihf': 1.1.3 + '@rolldown/binding-linux-arm64-gnu': 1.1.3 + '@rolldown/binding-linux-arm64-musl': 1.1.3 + '@rolldown/binding-linux-ppc64-gnu': 1.1.3 + '@rolldown/binding-linux-s390x-gnu': 1.1.3 + '@rolldown/binding-linux-x64-gnu': 1.1.3 + '@rolldown/binding-linux-x64-musl': 1.1.3 + '@rolldown/binding-openharmony-arm64': 1.1.3 + '@rolldown/binding-wasm32-wasi': 1.1.3 + '@rolldown/binding-win32-arm64-msvc': 1.1.3 + '@rolldown/binding-win32-x64-msvc': 1.1.3 rollup-plugin-dts@6.4.1(rollup@4.62.2)(typescript@5.9.3): dependencies: @@ -13578,14 +14003,14 @@ snapshots: optionalDependencies: '@babel/code-frame': 7.29.7 - rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2): + rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2): dependencies: open: 11.0.0 picomatch: 4.0.4 source-map: 0.7.6 yargs: 18.0.0 optionalDependencies: - rolldown: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + rolldown: 1.1.3 rollup: 4.62.2 rollup@4.62.2: @@ -14040,6 +14465,19 @@ snapshots: transitivePeerDependencies: - picomatch + svelte-check@4.7.1(picomatch@4.0.4)(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@6.0.3): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@sveltejs/load-config': 0.2.0 + chokidar: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picocolors: 1.1.1 + sade: 1.8.1 + svelte: 5.56.4(@typescript-eslint/types@8.61.1) + typescript: 6.0.3 + transitivePeerDependencies: + - picomatch + svelte2tsx@0.7.57(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3): dependencies: dedent-js: 1.0.1 @@ -14258,6 +14696,8 @@ snapshots: typescript@5.9.3: {} + typescript@6.0.3: {} + ufo@1.6.4: {} ultrahtml@1.6.0: {} @@ -14328,7 +14768,7 @@ snapshots: unicorn-magic@0.4.0: {} - unimport@6.3.0(oxc-parser@0.132.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)): + unimport@6.3.0(oxc-parser@0.132.0)(rolldown@1.1.3): dependencies: acorn: 8.17.0 escape-string-regexp: 5.0.0 @@ -14346,7 +14786,7 @@ snapshots: unplugin-utils: 0.3.1 optionalDependencies: oxc-parser: 0.132.0 - rolldown: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + rolldown: 1.1.3 unpipe@1.0.0: {} @@ -14484,23 +14924,23 @@ snapshots: vary@1.1.2: {} - vite-dev-rpc@2.0.0(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): + vite-dev-rpc@2.0.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): dependencies: birpc: 4.0.0 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vite-hot-client: 2.2.0(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite-hot-client: 2.2.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) - vite-hot-client@2.2.0(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): + vite-hot-client@2.2.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): dependencies: - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vite-node@5.3.0(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0): + vite-node@5.3.0(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0): dependencies: cac: 6.7.14 es-module-lexer: 2.1.0 obug: 2.1.3 pathe: 2.0.3 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) transitivePeerDependencies: - '@types/node' - jiti @@ -14514,7 +14954,7 @@ snapshots: - tsx - yaml - vite-plugin-checker@0.13.0(eslint@9.39.4(jiti@2.7.0))(optionator@0.9.4)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): + vite-plugin-checker@0.13.0(eslint@9.39.4(jiti@2.7.0))(optionator@0.9.4)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0)): dependencies: '@babel/code-frame': 7.29.7 chokidar: 4.0.3 @@ -14524,14 +14964,14 @@ snapshots: proper-lockfile: 4.1.2 tiny-invariant: 1.3.3 tinyglobby: 0.2.17 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) vscode-uri: 3.1.0 optionalDependencies: eslint: 9.39.4(jiti@2.7.0) optionator: 0.9.4 typescript: 5.9.3 - vite-plugin-inspect@11.4.1(@nuxt/kit@3.21.7(magicast@0.5.3))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): + vite-plugin-inspect@11.4.1(@nuxt/kit@3.21.7(magicast@0.5.3))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): dependencies: ansis: 4.3.1 error-stack-parser-es: 1.0.5 @@ -14541,12 +14981,12 @@ snapshots: perfect-debounce: 2.1.0 sirv: 3.0.2 unplugin-utils: 0.3.1 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vite-dev-rpc: 2.0.0(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite-dev-rpc: 2.0.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) optionalDependencies: '@nuxt/kit': 3.21.7(magicast@0.5.3) - vite-plugin-inspect@11.4.1(@nuxt/kit@4.4.8(magicast@0.5.3))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): + vite-plugin-inspect@11.4.1(@nuxt/kit@4.4.8(magicast@0.5.3))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): dependencies: ansis: 4.3.1 error-stack-parser-es: 1.0.5 @@ -14556,32 +14996,32 @@ snapshots: perfect-debounce: 2.1.0 sirv: 3.0.2 unplugin-utils: 0.3.1 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vite-dev-rpc: 2.0.0(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite-dev-rpc: 2.0.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) optionalDependencies: '@nuxt/kit': 4.4.8(magicast@0.5.3) - vite-plugin-vue-tracer@1.4.0(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)): + vite-plugin-vue-tracer@1.4.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)): dependencies: estree-walker: 3.0.3 exsolve: 1.0.8 magic-string: 0.30.21 pathe: 2.0.3 source-map-js: 1.2.1 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) vue: 3.5.30(typescript@5.9.3) - vite-plugin-vue-tracer@1.4.0(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)): + vite-plugin-vue-tracer@1.4.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)): dependencies: estree-walker: 3.0.3 exsolve: 1.0.8 magic-string: 0.30.21 pathe: 2.0.3 source-map-js: 1.2.1 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) vue: 3.5.38(typescript@5.9.3) - vite@6.4.3(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0): + vite@6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.4) @@ -14593,10 +15033,11 @@ snapshots: '@types/node': 24.7.2 fsevents: 2.3.3 jiti: 2.7.0 + lightningcss: 1.32.0 terser: 5.48.0 yaml: 2.9.0 - vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0): + vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) @@ -14604,35 +15045,51 @@ snapshots: postcss: 8.5.15 rollup: 4.62.2 tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 24.7.2 + fsevents: 2.3.3 + jiti: 2.7.0 + lightningcss: 1.32.0 + terser: 5.48.0 + yaml: 2.9.0 + + vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0): + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.5.15 + rolldown: 1.1.3 + tinyglobby: 0.2.17 optionalDependencies: '@types/node': 22.15.3 + esbuild: 0.28.1 fsevents: 2.3.3 jiti: 2.7.0 terser: 5.48.0 yaml: 2.9.0 - vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0): + vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0): dependencies: - esbuild: 0.27.7 - fdir: 6.5.0(picomatch@4.0.4) + lightningcss: 1.32.0 picomatch: 4.0.4 postcss: 8.5.15 - rollup: 4.62.2 + rolldown: 1.1.3 tinyglobby: 0.2.17 optionalDependencies: '@types/node': 24.7.2 + esbuild: 0.28.1 fsevents: 2.3.3 jiti: 2.7.0 terser: 5.48.0 yaml: 2.9.0 - vitefu@1.1.3(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): + vitefu@1.1.3(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): optionalDependencies: - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vitest-environment-nuxt@1.0.1(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0): + vitest-environment-nuxt@1.0.1(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(lightningcss@1.32.0)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0): dependencies: - '@nuxt/test-utils': 3.17.2(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0) + '@nuxt/test-utils': 3.17.2(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(lightningcss@1.32.0)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0) transitivePeerDependencies: - '@cucumber/cucumber' - '@jest/globals' @@ -14658,10 +15115,10 @@ snapshots: - vitest - yaml - vitest@4.1.8(@types/node@22.15.3)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): + vitest@4.1.8(@types/node@22.15.3)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.8 - '@vitest/mocker': 4.1.8(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@vitest/mocker': 4.1.8(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.8 '@vitest/runner': 4.1.8 '@vitest/snapshot': 4.1.8 @@ -14678,19 +15135,19 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.15.3 - '@vitest/browser-playwright': 4.1.8(playwright@1.60.0)(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) + '@vitest/browser-playwright': 4.1.8(playwright@1.60.0)(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) jsdom: 27.0.1 transitivePeerDependencies: - msw - vitest@4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): + vitest@4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.8 - '@vitest/mocker': 4.1.8(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@vitest/mocker': 4.1.8(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.8 '@vitest/runner': 4.1.8 '@vitest/snapshot': 4.1.8 @@ -14707,19 +15164,19 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.7.2 - '@vitest/browser-playwright': 4.1.8(playwright@1.60.0)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) + '@vitest/browser-playwright': 4.1.8(playwright@1.60.0)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) jsdom: 27.0.1 transitivePeerDependencies: - msw - vitest@4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): + vitest@4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.8 - '@vitest/mocker': 4.1.8(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@vitest/mocker': 4.1.8(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.8 '@vitest/runner': 4.1.8 '@vitest/snapshot': 4.1.8 @@ -14736,11 +15193,11 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.7.2 - '@vitest/browser-playwright': 4.1.8(playwright@1.60.0)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) + '@vitest/browser-playwright': 4.1.8(playwright@1.60.0)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) transitivePeerDependencies: - msw optional: true @@ -14772,7 +15229,7 @@ snapshots: '@vue/devtools-api': 6.6.4 vue: 3.5.30(typescript@5.9.3) - vue-router@5.1.0(@vue/compiler-sfc@3.5.38)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)): + vue-router@5.1.0(@vue/compiler-sfc@3.5.38)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)): dependencies: '@babel/generator': 8.0.0 '@vue-macros/common': 3.1.2(vue@3.5.30(typescript@5.9.3)) @@ -14794,7 +15251,7 @@ snapshots: yaml: 2.9.0 optionalDependencies: '@vue/compiler-sfc': 3.5.38 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) vue-sfc-transformer@0.1.17(@vue/compiler-core@3.5.38)(esbuild@0.28.1)(vue@3.5.30(typescript@5.9.3)): dependencies: From 58d55e6c41cbb3ff2e1deee2b621f569c7e06a3b Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Thu, 16 Jul 2026 11:22:08 +0530 Subject: [PATCH 19/31] refactor: move SvelteKit test app to samples/apps/sveltekit-b2c per SDK spec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Move apps/test-app/ to samples/apps/sveltekit-b2c/ (spec §17) - Rename package from test-app to sveltekit-b2c - Rewrite README with prerequisites, env setup, run instructions - Add packages/sveltekit/.gitignore (node_modules, .svelte-kit, dist) - Anchor root .gitignore apps/ rule to /apps/ so samples/apps/ is tracked - Add samples/** to pnpm-workspace.yaml --- .gitignore | 2 +- packages/sveltekit/.gitignore | 4 + pnpm-lock.yaml | 96 ++++++++++++------- pnpm-workspace.yaml | 1 + samples/apps/sveltekit-b2c/.env.example | 13 +++ samples/apps/sveltekit-b2c/.gitignore | 23 +++++ samples/apps/sveltekit-b2c/.npmrc | 1 + samples/apps/sveltekit-b2c/.prettierignore | 9 ++ samples/apps/sveltekit-b2c/.prettierrc | 15 +++ .../sveltekit-b2c/.vscode/extensions.json | 3 + samples/apps/sveltekit-b2c/README.md | 55 +++++++++++ samples/apps/sveltekit-b2c/package.json | 30 ++++++ samples/apps/sveltekit-b2c/src/app.d.ts | 11 +++ samples/apps/sveltekit-b2c/src/app.html | 12 +++ .../apps/sveltekit-b2c/src/hooks.server.ts | 14 +++ .../sveltekit-b2c/src/lib/AppShell.svelte | 65 +++++++++++++ .../sveltekit-b2c/src/lib/assets/favicon.svg | 1 + samples/apps/sveltekit-b2c/src/lib/index.ts | 1 + .../src/routes/+layout.server.ts | 6 ++ .../sveltekit-b2c/src/routes/+layout.svelte | 13 +++ .../sveltekit-b2c/src/routes/+page.svelte | 92 ++++++++++++++++++ .../src/routes/api/auth/callback/+server.ts | 10 ++ .../src/routes/api/auth/signin/+server.ts | 10 ++ .../src/routes/api/auth/signout/+server.ts | 10 ++ .../src/routes/callback/+page.svelte | 6 ++ .../src/routes/protected/+page.server.ts | 19 ++++ .../src/routes/protected/+page.svelte | 29 ++++++ samples/apps/sveltekit-b2c/static/robots.txt | 3 + samples/apps/sveltekit-b2c/tsconfig.json | 20 ++++ samples/apps/sveltekit-b2c/vite.config.ts | 20 ++++ 30 files changed, 561 insertions(+), 33 deletions(-) create mode 100644 packages/sveltekit/.gitignore create mode 100644 samples/apps/sveltekit-b2c/.env.example create mode 100644 samples/apps/sveltekit-b2c/.gitignore create mode 100644 samples/apps/sveltekit-b2c/.npmrc create mode 100644 samples/apps/sveltekit-b2c/.prettierignore create mode 100644 samples/apps/sveltekit-b2c/.prettierrc create mode 100644 samples/apps/sveltekit-b2c/.vscode/extensions.json create mode 100644 samples/apps/sveltekit-b2c/README.md create mode 100644 samples/apps/sveltekit-b2c/package.json create mode 100644 samples/apps/sveltekit-b2c/src/app.d.ts create mode 100644 samples/apps/sveltekit-b2c/src/app.html create mode 100644 samples/apps/sveltekit-b2c/src/hooks.server.ts create mode 100644 samples/apps/sveltekit-b2c/src/lib/AppShell.svelte create mode 100644 samples/apps/sveltekit-b2c/src/lib/assets/favicon.svg create mode 100644 samples/apps/sveltekit-b2c/src/lib/index.ts create mode 100644 samples/apps/sveltekit-b2c/src/routes/+layout.server.ts create mode 100644 samples/apps/sveltekit-b2c/src/routes/+layout.svelte create mode 100644 samples/apps/sveltekit-b2c/src/routes/+page.svelte create mode 100644 samples/apps/sveltekit-b2c/src/routes/api/auth/callback/+server.ts create mode 100644 samples/apps/sveltekit-b2c/src/routes/api/auth/signin/+server.ts create mode 100644 samples/apps/sveltekit-b2c/src/routes/api/auth/signout/+server.ts create mode 100644 samples/apps/sveltekit-b2c/src/routes/callback/+page.svelte create mode 100644 samples/apps/sveltekit-b2c/src/routes/protected/+page.server.ts create mode 100644 samples/apps/sveltekit-b2c/src/routes/protected/+page.svelte create mode 100644 samples/apps/sveltekit-b2c/static/robots.txt create mode 100644 samples/apps/sveltekit-b2c/tsconfig.json create mode 100644 samples/apps/sveltekit-b2c/vite.config.ts diff --git a/.gitignore b/.gitignore index c3d69d3..ad80035 100644 --- a/.gitignore +++ b/.gitignore @@ -147,7 +147,7 @@ vite.config.ts.timestamp-* Thumbs.db # Test apps -apps/ +/apps/ # Nx .nx/cache diff --git a/packages/sveltekit/.gitignore b/packages/sveltekit/.gitignore new file mode 100644 index 0000000..5ee898e --- /dev/null +++ b/packages/sveltekit/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +.svelte-kit/ +dist/ +*.log diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 11e57b6..45383bf 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -150,34 +150,6 @@ importers: specifier: 'catalog:' version: 5.9.3 - apps/svelte-playground: - dependencies: - '@thunderid/svelte': - specifier: workspace:^ - version: link:../../packages/svelte - devDependencies: - '@sveltejs/adapter-auto': - specifier: ^7.0.1 - version: 7.0.1(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))) - '@sveltejs/kit': - specifier: ^2.63.0 - version: 2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) - '@sveltejs/vite-plugin-svelte': - specifier: ^7.1.2 - version: 7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) - svelte: - specifier: ^5.56.1 - version: 5.56.4(@typescript-eslint/types@8.61.1) - svelte-check: - specifier: ^4.6.0 - version: 4.7.1(picomatch@4.0.4)(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@6.0.3) - typescript: - specifier: ^6.0.3 - version: 6.0.3 - vite: - specifier: ^8.0.16 - version: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - packages/browser: dependencies: '@thunderid/javascript': @@ -618,7 +590,7 @@ importers: specifier: 'catalog:' version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) - packages/svelte: + packages/sveltekit: dependencies: '@thunderid/browser': specifier: workspace:^ @@ -771,6 +743,40 @@ importers: specifier: 3.5.30 version: 3.5.30(typescript@5.9.3) + samples/apps/sveltekit-b2c: + dependencies: + '@thunderid/sveltekit': + specifier: workspace:^ + version: link:../../../packages/sveltekit + devDependencies: + '@sveltejs/adapter-auto': + specifier: ^7.0.1 + version: 7.0.1(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))) + '@sveltejs/kit': + specifier: ^2.63.0 + version: 2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@sveltejs/vite-plugin-svelte': + specifier: ^7.1.2 + version: 7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + prettier: + specifier: ^3.8.3 + version: 3.9.4 + prettier-plugin-svelte: + specifier: ^4.1.0 + version: 4.1.1(prettier@3.9.4)(svelte@5.56.4(@typescript-eslint/types@8.61.1)) + svelte: + specifier: ^5.56.1 + version: 5.56.4(@typescript-eslint/types@8.61.1) + svelte-check: + specifier: ^4.6.0 + version: 4.7.1(picomatch@4.0.4)(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@6.0.3) + typescript: + specifier: ^6.0.3 + version: 6.0.3 + vite: + specifier: ^8.0.16 + version: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + packages: '@asamuzakjp/css-color@4.1.2': @@ -6288,11 +6294,23 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prettier-plugin-svelte@4.1.1: + resolution: {integrity: sha512-wXvbXMjSvb4C9ENWTHXyd+ihakKCsJ6rJhLP6/8HFNj4GkZr48jqL9PoKsl2sk7SyCZRTnJ7O2TTowUpOxP/KA==} + engines: {node: '>=20'} + peerDependencies: + prettier: ^3.0.0 + svelte: ^5.0.0 + prettier@3.6.2: resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} engines: {node: '>=14'} hasBin: true + prettier@3.9.4: + resolution: {integrity: sha512-yWG/o/4oJfo036EKAfK6ACAoDOfHeRHx4tuxkfBZiauURiaSmYwlpOr5LQqKtIkRD2z1PLteme2WoxEnj4tHTg==} + engines: {node: '>=14'} + hasBin: true + pretty-bytes@7.1.0: resolution: {integrity: sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw==} engines: {node: '>=20'} @@ -8617,6 +8635,13 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.3 + optional: true + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: '@emnapi/core': 1.11.1 @@ -10008,8 +10033,8 @@ snapshots: '@typescript-eslint/project-service@8.57.2(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.57.2(typescript@5.9.3) - '@typescript-eslint/types': 8.57.2 + '@typescript-eslint/tsconfig-utils': 8.61.1(typescript@5.9.3) + '@typescript-eslint/types': 8.61.1 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: @@ -10184,7 +10209,7 @@ snapshots: dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': @@ -13755,8 +13780,15 @@ snapshots: prelude-ls@1.2.1: {} + prettier-plugin-svelte@4.1.1(prettier@3.9.4)(svelte@5.56.4(@typescript-eslint/types@8.61.1)): + dependencies: + prettier: 3.9.4 + svelte: 5.56.4(@typescript-eslint/types@8.61.1) + prettier@3.6.2: {} + prettier@3.9.4: {} + pretty-bytes@7.1.0: {} pretty-format@27.5.1: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 6fa5b7a..6117e6f 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,6 +1,7 @@ packages: - packages/** - apps/* + - samples/** minimumReleaseAgeExclude: # JUSTIFICATION: Temporary exclusion due to recent release. TODO: Remove after the time passes. diff --git a/samples/apps/sveltekit-b2c/.env.example b/samples/apps/sveltekit-b2c/.env.example new file mode 100644 index 0000000..a132425 --- /dev/null +++ b/samples/apps/sveltekit-b2c/.env.example @@ -0,0 +1,13 @@ +# ThunderID IdP base URL (e.g. https://localhost:8090) +THUNDERID_BASE_URL= + +# OAuth client credentials (from ThunderID console) +THUNDERID_CLIENT_ID= +THUNDERID_CLIENT_SECRET= + +# Application UUID (from ThunderID console; required for branding, sign-up flow) +THUNDERID_APPLICATION_ID= + +# HS256 key for signing session JWT cookies (min 32 chars) +# Generate with: openssl rand -base64 32 +THUNDERID_SESSION_SECRET= diff --git a/samples/apps/sveltekit-b2c/.gitignore b/samples/apps/sveltekit-b2c/.gitignore new file mode 100644 index 0000000..3b462cb --- /dev/null +++ b/samples/apps/sveltekit-b2c/.gitignore @@ -0,0 +1,23 @@ +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* diff --git a/samples/apps/sveltekit-b2c/.npmrc b/samples/apps/sveltekit-b2c/.npmrc new file mode 100644 index 0000000..b6f27f1 --- /dev/null +++ b/samples/apps/sveltekit-b2c/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/samples/apps/sveltekit-b2c/.prettierignore b/samples/apps/sveltekit-b2c/.prettierignore new file mode 100644 index 0000000..7d74fe2 --- /dev/null +++ b/samples/apps/sveltekit-b2c/.prettierignore @@ -0,0 +1,9 @@ +# Package Managers +package-lock.json +pnpm-lock.yaml +yarn.lock +bun.lock +bun.lockb + +# Miscellaneous +/static/ diff --git a/samples/apps/sveltekit-b2c/.prettierrc b/samples/apps/sveltekit-b2c/.prettierrc new file mode 100644 index 0000000..3f7802c --- /dev/null +++ b/samples/apps/sveltekit-b2c/.prettierrc @@ -0,0 +1,15 @@ +{ + "useTabs": true, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 100, + "plugins": ["prettier-plugin-svelte"], + "overrides": [ + { + "files": "*.svelte", + "options": { + "parser": "svelte" + } + } + ] +} diff --git a/samples/apps/sveltekit-b2c/.vscode/extensions.json b/samples/apps/sveltekit-b2c/.vscode/extensions.json new file mode 100644 index 0000000..38f928a --- /dev/null +++ b/samples/apps/sveltekit-b2c/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["svelte.svelte-vscode", "esbenp.prettier-vscode"] +} diff --git a/samples/apps/sveltekit-b2c/README.md b/samples/apps/sveltekit-b2c/README.md new file mode 100644 index 0000000..cb0017e --- /dev/null +++ b/samples/apps/sveltekit-b2c/README.md @@ -0,0 +1,55 @@ +# SvelteKit B2C Sample App + +B2C single-page app demonstrating the `@thunderid/sveltekit` SDK with SvelteKit SSR support. Shows sign-in with redirect, a protected route that redirects unauthenticated users, user profile display, and sign-out. + +## Prerequisites + +- Node.js 20+ +- pnpm 10+ +- A running ThunderID IdP instance (e.g. `https://localhost:8090`) +- An OAuth2 client registered in the ThunderID console with: + - Redirect URI: `http://localhost:5173/api/auth/callback` + - Post-Logout Redirect URI: `http://localhost:5173/` + +## Environment Setup + +Copy `.env.example` to `.env` and fill in the values: + +```sh +cp .env.example .env +``` + +| Variable | Description | +|----------|-------------| +| `THUNDERID_BASE_URL` | ThunderID IdP base URL (e.g. `https://localhost:8090`) | +| `THUNDERID_CLIENT_ID` | OAuth2 client ID from the ThunderID console | +| `THUNDERID_CLIENT_SECRET` | OAuth2 client secret | +| `THUNDERID_APPLICATION_ID` | Application UUID from the ThunderID console | +| `THUNDERID_SESSION_SECRET` | HS256 key for session cookies (generate with `openssl rand -base64 32`) | + +## Running + +```sh +pnpm install +pnpm dev +``` + +The app starts at `http://localhost:5173`. + +## What It Demonstrates + +1. **Sign-in** — unauthenticated users see a sign-in button; clicking it redirects to ThunderID +2. **Callback handling** — the `/callback` route exchanges the authorization code for tokens +3. **Protected route** — `/protected` requires a valid session; unauthenticated users are redirected to sign-in +4. **User profile** — displays the authenticated user's name, email, and avatar +5. **Token management** — buttons to fetch access/ID tokens and user info +6. **Sign-out** — terminates the session and returns to the unauthenticated state +7. **SDK events** — logs SDK lifecycle events (sign-in, sign-out, token refresh) in an expandable panel +8. **Language switcher** — switch UI language via the SDK's i18n support + +## Building + +```sh +pnpm build +pnpm preview +``` diff --git a/samples/apps/sveltekit-b2c/package.json b/samples/apps/sveltekit-b2c/package.json new file mode 100644 index 0000000..d3a5d97 --- /dev/null +++ b/samples/apps/sveltekit-b2c/package.json @@ -0,0 +1,30 @@ +{ + "name": "sveltekit-b2c", + "private": true, + "version": "0.0.1", + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "prepare": "svelte-kit sync || echo ''", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "lint": "prettier --check .", + "format": "prettier --write ." + }, + "dependencies": { + "@thunderid/sveltekit": "workspace:^" + }, + "devDependencies": { + "@sveltejs/adapter-auto": "^7.0.1", + "@sveltejs/kit": "^2.63.0", + "@sveltejs/vite-plugin-svelte": "^7.1.2", + "prettier": "^3.8.3", + "prettier-plugin-svelte": "^4.1.0", + "svelte": "^5.56.1", + "svelte-check": "^4.6.0", + "typescript": "^6.0.3", + "vite": "^8.0.16" + } +} diff --git a/samples/apps/sveltekit-b2c/src/app.d.ts b/samples/apps/sveltekit-b2c/src/app.d.ts new file mode 100644 index 0000000..516884d --- /dev/null +++ b/samples/apps/sveltekit-b2c/src/app.d.ts @@ -0,0 +1,11 @@ +import type {ThunderIDSSRData} from '@thunderid/sveltekit'; + +declare global { + namespace App { + interface Locals { + thunderid: ThunderIDSSRData; + } + } +} + +export {}; diff --git a/samples/apps/sveltekit-b2c/src/app.html b/samples/apps/sveltekit-b2c/src/app.html new file mode 100644 index 0000000..6a2bb58 --- /dev/null +++ b/samples/apps/sveltekit-b2c/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/samples/apps/sveltekit-b2c/src/hooks.server.ts b/samples/apps/sveltekit-b2c/src/hooks.server.ts new file mode 100644 index 0000000..a25df42 --- /dev/null +++ b/samples/apps/sveltekit-b2c/src/hooks.server.ts @@ -0,0 +1,14 @@ +import {THUNDERID_BASE_URL, THUNDERID_CLIENT_ID, THUNDERID_CLIENT_SECRET, THUNDERID_SESSION_SECRET} from '$env/static/private'; +import {createThunderIDHandle} from '@thunderid/sveltekit/server'; + +export const handle = createThunderIDHandle({ + baseUrl: THUNDERID_BASE_URL, + clientId: THUNDERID_CLIENT_ID, + clientSecret: THUNDERID_CLIENT_SECRET, + sessionSecret: THUNDERID_SESSION_SECRET, + afterSignInUrl: '/', + afterSignOutUrl: '/', + preferences: { + theme: {inheritFromBranding: false}, + }, +}); diff --git a/samples/apps/sveltekit-b2c/src/lib/AppShell.svelte b/samples/apps/sveltekit-b2c/src/lib/AppShell.svelte new file mode 100644 index 0000000..658f770 --- /dev/null +++ b/samples/apps/sveltekit-b2c/src/lib/AppShell.svelte @@ -0,0 +1,65 @@ + + + +
+ +{#if eventLog.length > 0} +
+ SDK Events ({eventLog.length}) +
+{#each eventLog as evt}{evt}
+{/each}
+
+
+{/if} + +{@render children()} diff --git a/samples/apps/sveltekit-b2c/src/lib/assets/favicon.svg b/samples/apps/sveltekit-b2c/src/lib/assets/favicon.svg new file mode 100644 index 0000000..cc5dc66 --- /dev/null +++ b/samples/apps/sveltekit-b2c/src/lib/assets/favicon.svg @@ -0,0 +1 @@ +svelte-logo \ No newline at end of file diff --git a/samples/apps/sveltekit-b2c/src/lib/index.ts b/samples/apps/sveltekit-b2c/src/lib/index.ts new file mode 100644 index 0000000..856f2b6 --- /dev/null +++ b/samples/apps/sveltekit-b2c/src/lib/index.ts @@ -0,0 +1 @@ +// place files you want to import through the `$lib` alias in this folder. diff --git a/samples/apps/sveltekit-b2c/src/routes/+layout.server.ts b/samples/apps/sveltekit-b2c/src/routes/+layout.server.ts new file mode 100644 index 0000000..611031b --- /dev/null +++ b/samples/apps/sveltekit-b2c/src/routes/+layout.server.ts @@ -0,0 +1,6 @@ +import {loadThunderID} from '@thunderid/sveltekit/server'; +import type {LayoutServerLoad} from './$types'; + +export const load: LayoutServerLoad = (event) => { + return {thunderid: loadThunderID(event)}; +}; diff --git a/samples/apps/sveltekit-b2c/src/routes/+layout.svelte b/samples/apps/sveltekit-b2c/src/routes/+layout.svelte new file mode 100644 index 0000000..8d4006b --- /dev/null +++ b/samples/apps/sveltekit-b2c/src/routes/+layout.svelte @@ -0,0 +1,13 @@ + + + + + {@render children()} + + diff --git a/samples/apps/sveltekit-b2c/src/routes/+page.svelte b/samples/apps/sveltekit-b2c/src/routes/+page.svelte new file mode 100644 index 0000000..ad3a147 --- /dev/null +++ b/samples/apps/sveltekit-b2c/src/routes/+page.svelte @@ -0,0 +1,92 @@ + + +

ThunderID SDK Test App

+ + +

Initializing...

+
+ + +

Welcome!

+ + + + +
+ +

Actions

+ + + + + + + {#if accessToken} +
+

Access Token

+
{accessToken}
+ {/if} + + {#if idToken} +
+

ID Token (raw)

+
{idToken}
+ {/if} + + {#if userInfo} +
+

UserInfo

+
{JSON.stringify(userInfo, null, 2)}
+ {/if} + +
+

Raw user data

+
{JSON.stringify(tid.user, null, 2)}
+ +
+

Raw user profile

+
{JSON.stringify(tid.userProfile, null, 2)}
+ +
+

Configuration

+
{JSON.stringify({locale: tid.locale, isInitialized: tid.isInitialized, isSignedIn: tid.isSignedIn, resolvedBaseUrl: tid.resolvedBaseUrl, clientId: tid.clientId, scopes: tid.scopes}, null, 2)}
+
+ + +

You are not signed in.

+ + +
diff --git a/samples/apps/sveltekit-b2c/src/routes/api/auth/callback/+server.ts b/samples/apps/sveltekit-b2c/src/routes/api/auth/callback/+server.ts new file mode 100644 index 0000000..baeabb9 --- /dev/null +++ b/samples/apps/sveltekit-b2c/src/routes/api/auth/callback/+server.ts @@ -0,0 +1,10 @@ +import {THUNDERID_BASE_URL, THUNDERID_CLIENT_ID, THUNDERID_CLIENT_SECRET, THUNDERID_SESSION_SECRET} from '$env/static/private'; +import {createCallbackHandler} from '@thunderid/sveltekit/server'; + +export const GET = createCallbackHandler({ + baseUrl: THUNDERID_BASE_URL, + clientId: THUNDERID_CLIENT_ID, + clientSecret: THUNDERID_CLIENT_SECRET, + sessionSecret: THUNDERID_SESSION_SECRET, + afterSignInUrl: '/', +}); diff --git a/samples/apps/sveltekit-b2c/src/routes/api/auth/signin/+server.ts b/samples/apps/sveltekit-b2c/src/routes/api/auth/signin/+server.ts new file mode 100644 index 0000000..f4b16ce --- /dev/null +++ b/samples/apps/sveltekit-b2c/src/routes/api/auth/signin/+server.ts @@ -0,0 +1,10 @@ +import {THUNDERID_BASE_URL, THUNDERID_CLIENT_ID, THUNDERID_CLIENT_SECRET, THUNDERID_SESSION_SECRET} from '$env/static/private'; +import {createSignInHandler} from '@thunderid/sveltekit/server'; + +export const GET = createSignInHandler({ + baseUrl: THUNDERID_BASE_URL, + clientId: THUNDERID_CLIENT_ID, + clientSecret: THUNDERID_CLIENT_SECRET, + sessionSecret: THUNDERID_SESSION_SECRET, + afterSignInUrl: 'http://localhost:5173/api/auth/callback', +}); diff --git a/samples/apps/sveltekit-b2c/src/routes/api/auth/signout/+server.ts b/samples/apps/sveltekit-b2c/src/routes/api/auth/signout/+server.ts new file mode 100644 index 0000000..dd672fd --- /dev/null +++ b/samples/apps/sveltekit-b2c/src/routes/api/auth/signout/+server.ts @@ -0,0 +1,10 @@ +import {THUNDERID_BASE_URL, THUNDERID_CLIENT_ID, THUNDERID_CLIENT_SECRET, THUNDERID_SESSION_SECRET} from '$env/static/private'; +import {createSignOutHandler} from '@thunderid/sveltekit/server'; + +export const GET = createSignOutHandler({ + baseUrl: THUNDERID_BASE_URL, + clientId: THUNDERID_CLIENT_ID, + clientSecret: THUNDERID_CLIENT_SECRET, + sessionSecret: THUNDERID_SESSION_SECRET, + afterSignOutUrl: '/', +}); diff --git a/samples/apps/sveltekit-b2c/src/routes/callback/+page.svelte b/samples/apps/sveltekit-b2c/src/routes/callback/+page.svelte new file mode 100644 index 0000000..8a0b0ec --- /dev/null +++ b/samples/apps/sveltekit-b2c/src/routes/callback/+page.svelte @@ -0,0 +1,6 @@ + + +

Signing you in...

+ diff --git a/samples/apps/sveltekit-b2c/src/routes/protected/+page.server.ts b/samples/apps/sveltekit-b2c/src/routes/protected/+page.server.ts new file mode 100644 index 0000000..173f464 --- /dev/null +++ b/samples/apps/sveltekit-b2c/src/routes/protected/+page.server.ts @@ -0,0 +1,19 @@ +import {requireServerSession} from '@thunderid/sveltekit/server'; +import {redirect} from '@sveltejs/kit'; +import {isGuardRedirect} from '@thunderid/sveltekit/server'; +import type {PageServerLoad} from './$types'; + +export const load: PageServerLoad = (event) => { + try { + const ssrData = requireServerSession(event, '/api/auth/signin'); + return { + user: ssrData.user, + session: ssrData.session, + }; + } catch (e) { + if (isGuardRedirect(e)) { + throw redirect(e.status, e.location); + } + throw e; + } +}; diff --git a/samples/apps/sveltekit-b2c/src/routes/protected/+page.svelte b/samples/apps/sveltekit-b2c/src/routes/protected/+page.svelte new file mode 100644 index 0000000..df60d88 --- /dev/null +++ b/samples/apps/sveltekit-b2c/src/routes/protected/+page.svelte @@ -0,0 +1,29 @@ + + +

Protected Page

+ + + + +

Back to home

+ + +
+ +

SSR Session Data

+
{JSON.stringify({
+		session: {
+			sub: data.session?.sub,
+			scopes: data.session?.scopes,
+			exp: data.session?.exp ? new Date((data.session.exp as number) * 1000).toISOString() : null,
+			iat: data.session?.iat ? new Date((data.session.iat as number) * 1000).toISOString() : null,
+			sessionId: data.session?.sessionId,
+			accessToken: (data.session?.accessToken as string)?.slice(0, 30) + '...',
+		},
+		user: data.user,
+	}, null, 2)}
+
diff --git a/samples/apps/sveltekit-b2c/static/robots.txt b/samples/apps/sveltekit-b2c/static/robots.txt new file mode 100644 index 0000000..b6dd667 --- /dev/null +++ b/samples/apps/sveltekit-b2c/static/robots.txt @@ -0,0 +1,3 @@ +# allow crawling everything by default +User-agent: * +Disallow: diff --git a/samples/apps/sveltekit-b2c/tsconfig.json b/samples/apps/sveltekit-b2c/tsconfig.json new file mode 100644 index 0000000..2c2ed3c --- /dev/null +++ b/samples/apps/sveltekit-b2c/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "rewriteRelativeImportExtensions": true, + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler" + } + // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias + // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files + // + // To make changes to top-level options such as include and exclude, we recommend extending + // the generated config; see https://svelte.dev/docs/kit/configuration#typescript +} diff --git a/samples/apps/sveltekit-b2c/vite.config.ts b/samples/apps/sveltekit-b2c/vite.config.ts new file mode 100644 index 0000000..cb76b81 --- /dev/null +++ b/samples/apps/sveltekit-b2c/vite.config.ts @@ -0,0 +1,20 @@ +import adapter from '@sveltejs/adapter-auto'; +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [ + sveltekit({ + compilerOptions: { + // Force runes mode for the project, except for libraries. Can be removed in svelte 6. + runes: ({ filename }) => + filename.split(/[/\\]/).includes('node_modules') ? undefined : true + }, + + // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. + // If your environment is not supported, or you settled on a specific environment, switch out the adapter. + // See https://svelte.dev/docs/kit/adapters for more information about adapters. + adapter: adapter() + }) + ] +}); From d8ce9bb0960f1541e2a6b47a68f8e09dcd73b6ec Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Thu, 16 Jul 2026 11:26:02 +0530 Subject: [PATCH 20/31] refactor(sveltekit): rename @thunderid/svelte to @thunderid/sveltekit - Rename package directory from packages/svelte to packages/sveltekit - Rename package from @thunderid/svelte to @thunderid/sveltekit - Add Base* presentation components (12 new components) - Add EventBus, i18n translations, IdTokenValidator - Add HTTPAdapter, StorageAdapter - Enhance server routes (callback, signin, signout, refresh) - Add sanitizer tests, session tests, guard tests - Combine CSR (BrowserClient) + SSR (Node SDK) in one package --- packages/svelte/.editorconfig | 1 - packages/svelte/.gitignore | 8 - packages/svelte/LICENSE | 1 - packages/svelte/README.md | 202 ------ packages/svelte/src/ThunderID.svelte | 125 ---- packages/svelte/src/ThunderIDSvelteClient.ts | 355 ---------- .../__tests__/ThunderIDSvelteClient.test.ts | 211 ------ .../svelte/src/api/getAllOrganizations.ts | 62 -- packages/svelte/src/api/getMeOrganizations.ts | 62 -- packages/svelte/src/api/getSchemas.ts | 58 -- packages/svelte/src/api/getScim2Me.ts | 58 -- packages/svelte/src/index.ts | 51 -- packages/svelte/src/models/config.ts | 65 -- packages/svelte/src/models/contexts.ts | 50 -- .../svelte/src/server/__tests__/guard.test.ts | 104 --- packages/svelte/src/server/app.d.ts | 27 - packages/svelte/src/server/guard.ts | 51 -- packages/svelte/src/server/hooks.ts | 116 ---- packages/svelte/src/server/index.ts | 36 - packages/svelte/src/server/load.ts | 24 - packages/svelte/src/server/refresh.ts | 137 ---- packages/svelte/src/server/routes/callback.ts | 92 --- .../svelte/src/server/routes/orgSwitch.ts | 92 --- packages/svelte/src/server/routes/signin.ts | 59 -- packages/svelte/src/server/routes/signout.ts | 47 -- packages/svelte/svelte.config.js | 23 - .../{svelte => sveltekit}/eslint.config.js | 11 +- packages/{svelte => sveltekit}/package.json | 13 +- packages/sveltekit/src/ThunderID.svelte | 297 +++++++++ .../sveltekit/src/ThunderIDSvelteClient.ts | 622 ++++++++++++++++++ .../__tests__/ThunderIDSvelteClient.test.ts | 193 ++++++ .../sveltekit/src/__tests__/sanitizer.test.ts | 106 +++ .../sveltekit/src/adapters/HTTPAdapter.ts | 43 ++ .../src/adapters/StorageAdapter.ts} | 31 +- .../src/adapters}/index.ts | 8 +- packages/sveltekit/src/api/getSchemas.ts | 30 + packages/sveltekit/src/api/getScim2Me.ts | 30 + packages/sveltekit/src/api/index.ts | 2 + .../actions/BaseSignInButton.svelte | 46 ++ .../actions/BaseSignOutButton.svelte | 45 ++ .../actions/BaseSignUpButton.svelte | 46 ++ .../components/actions/SignInButton.svelte | 20 +- .../components/actions/SignOutButton.svelte | 20 +- .../components/actions/SignUpButton.svelte | 20 +- .../src/components/auth/Callback.svelte | 57 +- .../src/components/control/Loading.svelte | 0 .../src/components/control/SignedIn.svelte | 0 .../src/components/control/SignedOut.svelte | 0 .../presentation/AcceptInvite.svelte | 31 + .../presentation/BaseAcceptInvite.svelte | 25 + .../presentation/BaseInviteUser.svelte | 24 + .../presentation/BaseLanguageSwitcher.svelte | 16 + .../components/presentation/BaseSignIn.svelte | 29 + .../components/presentation/BaseSignUp.svelte | 29 + .../components/presentation/BaseUser.svelte | 16 + .../presentation/BaseUserDropdown.svelte | 56 ++ .../presentation/BaseUserProfile.svelte | 13 + .../components/presentation/InviteUser.svelte | 43 ++ .../presentation/LanguageSwitcher.svelte | 27 + .../src/components/presentation/SignIn.svelte | 59 ++ .../src/components/presentation/SignUp.svelte | 59 ++ .../src/components/presentation/User.svelte | 42 ++ .../presentation/UserDropdown.svelte | 52 ++ .../presentation/UserProfile.svelte | 44 ++ .../src/composables/useThunderID.ts | 10 +- .../src/composables/useUser.ts | 0 packages/{svelte => sveltekit}/src/context.ts | 0 .../src/errors/IAMError.ts | 0 .../{svelte => sveltekit}/src/errors/index.ts | 0 packages/sveltekit/src/events/EventBus.ts | 72 ++ .../src/events/index.ts} | 5 +- packages/sveltekit/src/i18n/index.ts | 2 + packages/sveltekit/src/i18n/translations.ts | 76 +++ packages/sveltekit/src/index.ts | 114 ++++ .../src/logger/LoggerAdapter.ts | 0 .../{svelte => sveltekit}/src/logger/index.ts | 0 .../src/logger/sanitizer.ts | 10 + packages/sveltekit/src/models/config.ts | 111 ++++ packages/sveltekit/src/models/contexts.ts | 91 +++ .../src/models/session.ts | 6 +- .../src/server/__tests__/guard.test.ts | 139 ++++ .../src/server/__tests__/session.test.ts | 37 +- packages/sveltekit/src/server/app.d.ts | 9 + .../src/server/config.ts | 61 +- packages/sveltekit/src/server/getClient.ts | 19 + packages/sveltekit/src/server/guard.ts | 44 ++ packages/sveltekit/src/server/hooks.ts | 112 ++++ packages/sveltekit/src/server/index.ts | 18 + packages/sveltekit/src/server/load.ts | 6 + packages/sveltekit/src/server/refresh.ts | 150 +++++ .../sveltekit/src/server/routes/callback.ts | 111 ++++ packages/sveltekit/src/server/routes/index.ts | 3 + .../sveltekit/src/server/routes/signin.ts | 40 ++ .../sveltekit/src/server/routes/signout.ts | 34 + .../src/server/session.ts | 38 +- .../{svelte => sveltekit}/src/state.svelte.ts | 6 +- .../src/validation/IdTokenValidator.ts | 141 ++++ .../src/validation/index.ts} | 9 +- packages/{svelte => sveltekit}/tsconfig.json | 1 - .../{svelte => sveltekit}/tsconfig.lib.json | 0 packages/sveltekit/vitest.config.ts | 10 + 101 files changed, 3506 insertions(+), 2301 deletions(-) delete mode 100644 packages/svelte/.editorconfig delete mode 100644 packages/svelte/.gitignore delete mode 100644 packages/svelte/LICENSE delete mode 100644 packages/svelte/README.md delete mode 100644 packages/svelte/src/ThunderID.svelte delete mode 100644 packages/svelte/src/ThunderIDSvelteClient.ts delete mode 100644 packages/svelte/src/__tests__/ThunderIDSvelteClient.test.ts delete mode 100644 packages/svelte/src/api/getAllOrganizations.ts delete mode 100644 packages/svelte/src/api/getMeOrganizations.ts delete mode 100644 packages/svelte/src/api/getSchemas.ts delete mode 100644 packages/svelte/src/api/getScim2Me.ts delete mode 100644 packages/svelte/src/index.ts delete mode 100644 packages/svelte/src/models/config.ts delete mode 100644 packages/svelte/src/models/contexts.ts delete mode 100644 packages/svelte/src/server/__tests__/guard.test.ts delete mode 100644 packages/svelte/src/server/app.d.ts delete mode 100644 packages/svelte/src/server/guard.ts delete mode 100644 packages/svelte/src/server/hooks.ts delete mode 100644 packages/svelte/src/server/index.ts delete mode 100644 packages/svelte/src/server/load.ts delete mode 100644 packages/svelte/src/server/refresh.ts delete mode 100644 packages/svelte/src/server/routes/callback.ts delete mode 100644 packages/svelte/src/server/routes/orgSwitch.ts delete mode 100644 packages/svelte/src/server/routes/signin.ts delete mode 100644 packages/svelte/src/server/routes/signout.ts delete mode 100644 packages/svelte/svelte.config.js rename packages/{svelte => sveltekit}/eslint.config.js (79%) rename packages/{svelte => sveltekit}/package.json (88%) create mode 100644 packages/sveltekit/src/ThunderID.svelte create mode 100644 packages/sveltekit/src/ThunderIDSvelteClient.ts create mode 100644 packages/sveltekit/src/__tests__/ThunderIDSvelteClient.test.ts create mode 100644 packages/sveltekit/src/__tests__/sanitizer.test.ts create mode 100644 packages/sveltekit/src/adapters/HTTPAdapter.ts rename packages/{svelte/src/server/getClient.ts => sveltekit/src/adapters/StorageAdapter.ts} (50%) rename packages/{svelte/src/server/routes => sveltekit/src/adapters}/index.ts (75%) create mode 100644 packages/sveltekit/src/api/getSchemas.ts create mode 100644 packages/sveltekit/src/api/getScim2Me.ts create mode 100644 packages/sveltekit/src/api/index.ts create mode 100644 packages/sveltekit/src/components/actions/BaseSignInButton.svelte create mode 100644 packages/sveltekit/src/components/actions/BaseSignOutButton.svelte create mode 100644 packages/sveltekit/src/components/actions/BaseSignUpButton.svelte rename packages/{svelte => sveltekit}/src/components/actions/SignInButton.svelte (70%) rename packages/{svelte => sveltekit}/src/components/actions/SignOutButton.svelte (70%) rename packages/{svelte => sveltekit}/src/components/actions/SignUpButton.svelte (70%) rename packages/{svelte => sveltekit}/src/components/auth/Callback.svelte (61%) rename packages/{svelte => sveltekit}/src/components/control/Loading.svelte (100%) rename packages/{svelte => sveltekit}/src/components/control/SignedIn.svelte (100%) rename packages/{svelte => sveltekit}/src/components/control/SignedOut.svelte (100%) create mode 100644 packages/sveltekit/src/components/presentation/AcceptInvite.svelte create mode 100644 packages/sveltekit/src/components/presentation/BaseAcceptInvite.svelte create mode 100644 packages/sveltekit/src/components/presentation/BaseInviteUser.svelte create mode 100644 packages/sveltekit/src/components/presentation/BaseLanguageSwitcher.svelte create mode 100644 packages/sveltekit/src/components/presentation/BaseSignIn.svelte create mode 100644 packages/sveltekit/src/components/presentation/BaseSignUp.svelte create mode 100644 packages/sveltekit/src/components/presentation/BaseUser.svelte create mode 100644 packages/sveltekit/src/components/presentation/BaseUserDropdown.svelte create mode 100644 packages/sveltekit/src/components/presentation/BaseUserProfile.svelte create mode 100644 packages/sveltekit/src/components/presentation/InviteUser.svelte create mode 100644 packages/sveltekit/src/components/presentation/LanguageSwitcher.svelte create mode 100644 packages/sveltekit/src/components/presentation/SignIn.svelte create mode 100644 packages/sveltekit/src/components/presentation/SignUp.svelte create mode 100644 packages/sveltekit/src/components/presentation/User.svelte create mode 100644 packages/sveltekit/src/components/presentation/UserDropdown.svelte create mode 100644 packages/sveltekit/src/components/presentation/UserProfile.svelte rename packages/{svelte => sveltekit}/src/composables/useThunderID.ts (91%) rename packages/{svelte => sveltekit}/src/composables/useUser.ts (100%) rename packages/{svelte => sveltekit}/src/context.ts (100%) rename packages/{svelte => sveltekit}/src/errors/IAMError.ts (100%) rename packages/{svelte => sveltekit}/src/errors/index.ts (100%) create mode 100644 packages/sveltekit/src/events/EventBus.ts rename packages/{svelte/prettier.config.js => sveltekit/src/events/index.ts} (84%) create mode 100644 packages/sveltekit/src/i18n/index.ts create mode 100644 packages/sveltekit/src/i18n/translations.ts create mode 100644 packages/sveltekit/src/index.ts rename packages/{svelte => sveltekit}/src/logger/LoggerAdapter.ts (100%) rename packages/{svelte => sveltekit}/src/logger/index.ts (100%) rename packages/{svelte => sveltekit}/src/logger/sanitizer.ts (81%) create mode 100644 packages/sveltekit/src/models/config.ts create mode 100644 packages/sveltekit/src/models/contexts.ts rename packages/{svelte => sveltekit}/src/models/session.ts (88%) create mode 100644 packages/sveltekit/src/server/__tests__/guard.test.ts rename packages/{svelte => sveltekit}/src/server/__tests__/session.test.ts (85%) create mode 100644 packages/sveltekit/src/server/app.d.ts rename packages/{svelte => sveltekit}/src/server/config.ts (50%) create mode 100644 packages/sveltekit/src/server/getClient.ts create mode 100644 packages/sveltekit/src/server/guard.ts create mode 100644 packages/sveltekit/src/server/hooks.ts create mode 100644 packages/sveltekit/src/server/index.ts create mode 100644 packages/sveltekit/src/server/load.ts create mode 100644 packages/sveltekit/src/server/refresh.ts create mode 100644 packages/sveltekit/src/server/routes/callback.ts create mode 100644 packages/sveltekit/src/server/routes/index.ts create mode 100644 packages/sveltekit/src/server/routes/signin.ts create mode 100644 packages/sveltekit/src/server/routes/signout.ts rename packages/{svelte => sveltekit}/src/server/session.ts (80%) rename packages/{svelte => sveltekit}/src/state.svelte.ts (84%) create mode 100644 packages/sveltekit/src/validation/IdTokenValidator.ts rename packages/{svelte/vitest.config.ts => sveltekit/src/validation/index.ts} (81%) rename packages/{svelte => sveltekit}/tsconfig.json (97%) rename packages/{svelte => sveltekit}/tsconfig.lib.json (100%) create mode 100644 packages/sveltekit/vitest.config.ts diff --git a/packages/svelte/.editorconfig b/packages/svelte/.editorconfig deleted file mode 100644 index 1b3ce07..0000000 --- a/packages/svelte/.editorconfig +++ /dev/null @@ -1 +0,0 @@ -../../.editorconfig \ No newline at end of file diff --git a/packages/svelte/.gitignore b/packages/svelte/.gitignore deleted file mode 100644 index 556c98e..0000000 --- a/packages/svelte/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -dist/ -node_modules/ -*.tsbuildinfo -.svelte-kit/ -.svelte-kit -coverage/ -.eslintcache -.pnpm-store/ diff --git a/packages/svelte/LICENSE b/packages/svelte/LICENSE deleted file mode 100644 index 30cff74..0000000 --- a/packages/svelte/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../LICENSE \ No newline at end of file diff --git a/packages/svelte/README.md b/packages/svelte/README.md deleted file mode 100644 index 306f7e3..0000000 --- a/packages/svelte/README.md +++ /dev/null @@ -1,202 +0,0 @@ -# @thunderid/svelte - -**Svelte 5 SDK for [ThunderID](https://thunderid.dev)** - reactive auth with runes. - -## Installation - -```bash -pnpm add @thunderid/svelte -``` - -## SvelteKit (SSR) - -The SDK provides server utilities under the `@thunderid/svelte/server` subpath for SvelteKit SSR support. - -### Prerequisites - -Create a SvelteKit project if you don't have one: - -```bash -npx sv create my-app --template minimal --types ts -cd my-app -pnpm install -pnpm add @thunderid/svelte -``` - -### 1. Set environment variables - -```env -THUNDERID_SESSION_SECRET= -THUNDERID_BASE_URL=https://{tenant}.thunderid.dev -THUNDERID_CLIENT_ID= -THUNDERID_CLIENT_SECRET= -``` - -> **SvelteKit convention**: Import env vars from `$env/static/private` and pass them explicitly to factory functions for full type safety: -> -> ```ts -> import { THUNDERID_BASE_URL, THUNDERID_CLIENT_ID, THUNDERID_CLIENT_SECRET, THUNDERID_SESSION_SECRET } from '$env/static/private'; -> import { createThunderIDHandle } from '@thunderid/svelte/server'; -> -> export const handle = createThunderIDHandle({ -> baseUrl: THUNDERID_BASE_URL, -> clientId: THUNDERID_CLIENT_ID, -> clientSecret: THUNDERID_CLIENT_SECRET, -> sessionSecret: THUNDERID_SESSION_SECRET, -> }); -> ``` -> -> Passing no arguments falls back to `process.env` (works in dev, but `$env/static/private` is recommended). - -### 2. Apply the handle hook - -Create `src/hooks.server.ts`: - -```ts -import {createThunderIDHandle} from '@thunderid/svelte/server'; - -export const handle = createThunderIDHandle(); -``` - -The hook reads env vars from `process.env` (or accepts config explicitly). It resolves the session from the JWT cookie, refreshes expiring tokens proactively, fetches branding + organizations in parallel, and writes `event.locals.thunderid` with `ThunderIDSSRData`. - -**Token endpoint auth method**: The SDK defaults to `client_secret_post`. - -```ts -createThunderIDHandle({ - ...config, - tokenRequest: {authMethod: 'client_secret_basic'}, -}); -``` - -### 3. Create the sign-in, callback, and sign-out routes - -```ts -// src/routes/api/auth/signin/+server.ts -import {createSignInHandler} from '@thunderid/svelte/server'; -export const GET = createSignInHandler(); - -// src/routes/api/auth/callback/+server.ts -import {createCallbackHandler} from '@thunderid/svelte/server'; -export const GET = createCallbackHandler(); - -// src/routes/api/auth/signout/+server.ts -import {createSignOutHandler} from '@thunderid/svelte/server'; -export const GET = createSignOutHandler(); -``` - -You can pass config to each handler (e.g., `createSignInHandler({...config})`). If omitted, they use the same env-var fallbacks as the handle hook. - -### 6. Pass SSR data to the client - -Create `src/routes/+layout.server.ts`: - -```ts -import {loadThunderID} from '@thunderid/svelte/server'; - -export const load = loadThunderID; -``` - -### 7. Wire up the layout - -Wrap your root layout with the provider and add sign-in/out controls: - -`src/routes/+layout.svelte`: - -```svelte - - - - - {@render children()} - -``` - -### TypeScript - -For `event.locals.thunderid` to have proper types in your load functions, reference the SDK's type augmentation in your `src/app.d.ts`: - -```ts -declare global { - namespace App { - interface Locals { - thunderid: import('@thunderid/svelte/server').ThunderIDSSRData; - } - } -} - -export {}; -``` - -### Redirect URI Configuration - -In your ThunderID application settings, add the exact callback URL: - -- **Development**: `http://localhost:5173/api/auth/callback` (use `http`, not `https` - SvelteKit dev server runs on HTTP) -- **Production**: `https://your-domain.com/api/auth/callback` - -The hook automatically computes the callback URL from the request origin - no manual `afterSignInUrl` setup needed. - -> **Local dev with self-signed TLS**: If your IdP uses a self-signed certificate, launch the dev server with `NODE_TLS_REJECT_UNAUTHORIZED=0 pnpm dev` (dev only - never use in production). - -### Server-side route guard - -Protect pages or API routes from unauthenticated access: - -```ts -import {requireServerSession} from '@thunderid/svelte/server'; -import type {RequestEvent} from '@sveltejs/kit'; - -export function load(event: RequestEvent) { - // Redirects to /api/auth/signin if not signed in - const ssrData = requireServerSession(event, '/custom/signin'); -} -``` - -### Switch organization server-side - -```ts -import {createOrgSwitchHandler} from '@thunderid/svelte/server'; - -export const POST = createOrgSwitchHandler(); -``` - -## Composables - -```svelte - -``` - -## Components - -| Component | Description | -|-----------|-------------| -| `` | Provider - wraps your app with auth context | -| `` | Renders children when signed in | -| `` | Renders children when signed out | -| `` | Renders children while auth state initializes | -| `` | Sign-in button with loading state | -| `` | Sign-out button with loading state | -| `` | Sign-up button with loading state | - -## License - -Apache-2.0 diff --git a/packages/svelte/src/ThunderID.svelte b/packages/svelte/src/ThunderID.svelte deleted file mode 100644 index 609d48e..0000000 --- a/packages/svelte/src/ThunderID.svelte +++ /dev/null @@ -1,125 +0,0 @@ - - -{@render children?.()} diff --git a/packages/svelte/src/ThunderIDSvelteClient.ts b/packages/svelte/src/ThunderIDSvelteClient.ts deleted file mode 100644 index 6644402..0000000 --- a/packages/svelte/src/ThunderIDSvelteClient.ts +++ /dev/null @@ -1,355 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { - ThunderIDNodeClient, - type AuthClientConfig, - type BrandingPreference, - type GetBrandingPreferenceConfig, - type IdToken, - type Organization, - type Storage, - type TokenExchangeRequestConfig, - type TokenResponse, - type User, - type UserProfile, - getBrandingPreference, - getMeOrganizations, - MemoryCacheStore, -} from '@thunderid/node'; -import {IAMError, ErrorCode} from './errors/IAMError'; -import {getLogger, type LoggerAdapter} from './logger/LoggerAdapter'; -import type {ThunderIDSvelteConfig} from './models/config'; -import type {ThunderIDSessionPayload} from './models/session'; - -class ThunderIDSvelteClient extends ThunderIDNodeClient> { - private static instance: ThunderIDSvelteClient; - - public isInitialized = false; - private _isLoading = false; - private logger: LoggerAdapter = getLogger(); - - private constructor() { - super(); - } - - public static getInstance(): ThunderIDSvelteClient { - if (!ThunderIDSvelteClient.instance) { - ThunderIDSvelteClient.instance = new ThunderIDSvelteClient(); - } - return ThunderIDSvelteClient.instance; - } - - override async initialize(config: ThunderIDSvelteConfig, storage?: Storage): Promise { - if (this.isInitialized) { - throw new IAMError({ - code: ErrorCode.ALREADY_INITIALIZED, - message: 'ThunderID SDK is already initialized. Call reset() first if you need to re-initialize.', - }); - } - - if (!config.baseUrl) { - throw new IAMError({ - code: ErrorCode.INVALID_CONFIGURATION, - message: 'baseUrl is required. Set THUNDERID_BASE_URL environment variable or pass it in the config.', - }); - } - - if (!config.baseUrl.startsWith('https://')) { - throw new IAMError({ - code: ErrorCode.INVALID_CONFIGURATION, - message: 'baseUrl must use HTTPS.', - }); - } - - if (!config.clientId) { - throw new IAMError({ - code: ErrorCode.INVALID_CONFIGURATION, - message: 'clientId is required. Set THUNDERID_CLIENT_ID environment variable or pass it in the config.', - }); - } - - const authConfig: AuthClientConfig = { - afterSignInUrl: config.afterSignInUrl ?? '/', - afterSignOutUrl: config.afterSignOutUrl ?? '/', - baseUrl: config.baseUrl, - clientId: config.clientId, - clientSecret: config.clientSecret ?? undefined, - enablePKCE: true, - scopes: config.scopes ?? ['openid', 'profile'], - tokenRequest: config.tokenRequest ?? {authMethod: 'client_secret_post'}, - } as AuthClientConfig; - - const resolvedStorage: Storage = storage ?? new MemoryCacheStore(); - - this._isLoading = true; - try { - const result: boolean = await super.initialize( - authConfig as unknown as AuthClientConfig, - resolvedStorage, - ); - this.isInitialized = true; - return result; - } finally { - this._isLoading = false; - } - } - - override async reInitialize(config: Partial): Promise { - if (!this.isInitialized) { - throw new IAMError({ - code: ErrorCode.SDK_NOT_INITIALIZED, - message: 'SDK is not initialized. Call initialize() first.', - }); - } - await super.reInitialize(config as any); - return true; - } - - override isLoading(): boolean { - return this._isLoading; - } - - override getConfiguration(): AuthClientConfig { - const storageManager: any = this.getStorageManager(); - return storageManager?.getConfigData?.() as AuthClientConfig; - } - - async rehydrateSessionFromPayload(session: ThunderIDSessionPayload): Promise { - if (!this.isInitialized || !session?.sessionId || !session?.accessToken) { - return; - } - - const storageManager: any = this.getStorageManager(); - const iatSeconds: number = typeof session.iat === 'number' ? session.iat : Math.floor(Date.now() / 1000); - const expiresInSeconds: number = - typeof session.accessTokenExpiresAt === 'number' ? Math.max(0, session.accessTokenExpiresAt - iatSeconds) : 3600; - - await storageManager.setSessionData( - { - access_token: session.accessToken, - created_at: iatSeconds * 1000, - expires_in: String(expiresInSeconds || 3600), - id_token: session.idToken ?? '', - refresh_token: session.refreshToken ?? '', - scope: session.scopes ?? '', - session_state: '', - token_type: 'Bearer', - }, - session.sessionId, - ); - } - - override signIn(...args: any[]): Promise { - const arg0: unknown = args[0]; - - if (typeof arg0 === 'object' && arg0 !== null && ('code' in arg0 || 'state' in arg0)) { - const payload: {code?: unknown; session_state?: unknown; state?: unknown} = arg0 as { - code?: unknown; - session_state?: unknown; - state?: unknown; - }; - const code: string | undefined = typeof payload.code === 'string' ? payload.code : undefined; - const sessionState: string | undefined = - typeof payload.session_state === 'string' ? payload.session_state : undefined; - const state: string | undefined = typeof payload.state === 'string' ? payload.state : undefined; - const extraParams: Record = {}; - - if (code) extraParams['code'] = code; - if (sessionState) extraParams['session_state'] = sessionState; - if (state) extraParams['state'] = state; - - return super.signIn(args[3], args[2], code, sessionState, state, extraParams); - } - - return super.signIn(args[0], args[1], args[2], args[3], args[4], args[5]); - } - - override async signOut(...args: any[]): Promise { - const configData: any = await this.getStorageManager().getConfigData(); - return (configData?.afterSignOutUrl as string) || (configData?.afterSignInUrl as string) || '/'; - } - - override async signUp(options?: Record): Promise { - const configData: any = await this.getStorageManager().getConfigData(); - const signUpUrl: string | undefined = configData?.signUpUrl as string | undefined; - const baseUrl: string | undefined = configData?.baseUrl as string | undefined; - const applicationId: string | undefined = configData?.applicationId as string | undefined; - - if (signUpUrl) { - window.location.href = signUpUrl; - return; - } - - if (baseUrl) { - let url = `${baseUrl}/accountrecoveryendpoint/register.do`; - if (applicationId) { - url += `?spId=${applicationId}`; - } - window.location.href = url; - return; - } - - throw new IAMError({ - code: ErrorCode.INVALID_CONFIGURATION, - message: 'Cannot sign up: no signUpUrl or baseUrl configured.', - }); - } - - override async signInSilently(_options?: Record): Promise { - if (!this.isInitialized) { - throw new IAMError({ - code: ErrorCode.SDK_NOT_INITIALIZED, - message: 'SDK is not initialized. Call initialize() first.', - }); - } - - try { - const signedIn: boolean = await this.isSignedIn(); - if (signedIn) { - const user: User = await this.getUser(); - return user; - } - return false; - } catch { - return false; - } - } - - async changePassword(_currentPassword: string, _newPassword: string): Promise { - throw new IAMError({ - code: ErrorCode.NOT_IMPLEMENTED, - message: 'changePassword() is not yet implemented. This will be available in a future release.', - }); - } - - async setSessionData(sessionData: Record, sessionId?: string): Promise { - const storageManager: any = this.getStorageManager(); - await storageManager.setSessionData(sessionData, sessionId); - } - - clearSessionData(sessionId?: string): void { - const storageManager: any = this.getStorageManager(); - storageManager.clearSession(sessionId); - } - - override getAllOrganizations(_options?: Record, _sessionId?: string): Promise { - throw new IAMError({ - code: ErrorCode.NOT_IMPLEMENTED, - message: 'getAllOrganizations() is not yet implemented. Use getMyOrganizations() instead.', - }); - } - - public async getAuthorizeRequestUrl(customParams: Record, userId?: string): Promise { - return this.getSignInUrl(customParams, userId); - } - - override getUser(sessionId?: string): Promise { - return super.getUser(sessionId); - } - - override getAccessToken(sessionId?: string): Promise { - return super.getAccessToken(sessionId); - } - - override getDecodedIdToken(sessionId?: string, idToken?: string): Promise { - return super.getDecodedIdToken(sessionId, idToken); - } - - override isSignedIn(sessionId?: string): Promise { - return super.isSignedIn(sessionId); - } - - override exchangeToken( - config: TokenExchangeRequestConfig, - sessionId?: string, - ): Promise { - return super.exchangeToken(config, sessionId) as unknown as Promise; - } - - override async getUserProfile(sessionId?: string): Promise { - const user: User = await this.getUser(sessionId); - return {flattenedProfile: user, profile: user, schemas: []}; - } - - override async getCurrentOrganization(sessionId?: string): Promise { - try { - const idToken: IdToken = await this.getDecodedIdToken(sessionId); - if (!idToken?.org_id) { - return null; - } - return { - id: idToken.org_id, - name: idToken.org_name ?? '', - orgHandle: idToken.org_handle ?? '', - }; - } catch { - return null; - } - } - - override async getMyOrganizations(sessionId?: string): Promise { - const accessToken: string = await this.getAccessToken(sessionId); - const configData: any = await this.getStorageManager().getConfigData(); - const baseUrl: string = (configData?.baseUrl ?? '') as string; - - return getMeOrganizations({ - baseUrl, - headers: {Authorization: `Bearer ${accessToken}`}, - }); - } - - override async switchOrganization( - organization: Organization, - sessionId?: string, - ): Promise { - if (!organization.id) { - throw new IAMError({ - code: ErrorCode.INVALID_INPUT, - message: 'Organization ID is required for switching organizations.', - }); - } - - const exchangeConfig: TokenExchangeRequestConfig = { - attachToken: false, - data: { - client_id: '{{clientId}}', - client_secret: '{{clientSecret}}', - grant_type: 'organization_switch', - scope: '{{scopes}}', - switching_organization: organization.id, - token: '{{accessToken}}', - }, - id: 'organization-switch', - returnsSession: true, - signInRequired: true, - }; - - return this.exchangeToken(exchangeConfig, sessionId); - } - - async getBrandingPreference(config: GetBrandingPreferenceConfig): Promise { - return getBrandingPreference(config); - } - - public override getStorageManager(): any { - return super.getStorageManager(); - } -} - -export default ThunderIDSvelteClient; diff --git a/packages/svelte/src/__tests__/ThunderIDSvelteClient.test.ts b/packages/svelte/src/__tests__/ThunderIDSvelteClient.test.ts deleted file mode 100644 index 480e9f9..0000000 --- a/packages/svelte/src/__tests__/ThunderIDSvelteClient.test.ts +++ /dev/null @@ -1,211 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import {describe, it, expect, vi, beforeEach, afterEach} from 'vitest'; -import type {ThunderIDSessionPayload} from '../models/session'; - -vi.mock('@thunderid/node', () => { - const storageData: Record = {}; - - class MockMemoryCacheStore { - setItem(_key: string, _value: any): void {} - getItem(_key: string): any { - return null; - } - removeItem(_key: string): void {} - clear(): void {} - } - - const getMeOrganizations = vi.fn().mockResolvedValue([]); - - const getBrandingPreference = vi.fn().mockResolvedValue(null); - - return { - ThunderIDNodeClient: class MockNodeClient { - private storage: any; - initialized = false; - - async initialize(_config: any, storage?: any): Promise { - this.storage = storage; - this.initialized = true; - return true; - } - - async reInitialize(_config: any): Promise { - return true; - } - - getStorageManager(): any { - return { - setSessionData: vi.fn().mockResolvedValue(undefined), - getSessionData: vi.fn().mockResolvedValue(null), - getConfigData: vi.fn().mockResolvedValue({baseUrl: 'https://example.com'}), - }; - } - - isSignedIn(_sessionId?: string): Promise { - return Promise.resolve(true); - } - - getUser(_sessionId?: string): Promise { - return Promise.resolve({id: 'u1', name: 'Test User'}); - } - - getAccessToken(_sessionId?: string): Promise { - return Promise.resolve('mock-access-token'); - } - - getDecodedIdToken(_sessionId?: string, _idToken?: string): Promise { - return Promise.resolve({sub: 'user-001'}); - } - - getUserProfile(_sessionId: string): Promise { - return Promise.resolve({flattenedProfile: {id: 'u1'}, profile: {id: 'u1'}, schemas: []}); - } - - getCurrentOrganization(_sessionId: string): Promise { - return Promise.resolve(null); - } - - getMyOrganizations(_sessionId: string): Promise { - return getMeOrganizations(); - } - - getSignInUrl(_customParams?: any, _userId?: string): Promise { - return Promise.resolve('https://idp.example.com/authorize'); - } - - exchangeToken(_config: any, _sessionId?: string): Promise { - return Promise.resolve({accessToken: 'new-at'}); - } - - signOut(_sessionId?: string): Promise { - return Promise.resolve('/'); - } - - revokeAccessToken(_userId?: string): Promise { - return Promise.resolve(true); - } - - getInstanceId(): number { - return 0; - } - }, - MemoryCacheStore: MockMemoryCacheStore, - getMeOrganizations, - getBrandingPreference, - CookieConfig: {SESSION_COOKIE_NAME: 'thunderid_session', TEMP_SESSION_COOKIE_NAME: 'thunderid_temp_session'}, - }; -}); - -const {default: ThunderIDSvelteClient} = await import('../ThunderIDSvelteClient'); - -describe('ThunderIDSvelteClient', () => { - beforeEach(() => { - // Reset singleton between tests - (ThunderIDSvelteClient as any).instance = undefined; - }); - - describe('singleton', () => { - it('should return the same instance on getInstance()', () => { - const a = ThunderIDSvelteClient.getInstance(); - const b = ThunderIDSvelteClient.getInstance(); - expect(a).toBe(b); - }); - - it('should create new instances via constructor', () => { - const a = ThunderIDSvelteClient.getInstance(); - const b = new (ThunderIDSvelteClient as any)(); - expect(a).not.toBe(b); - }); - }); - - describe('initialize', () => { - it('should initialize successfully with valid config', async () => { - const client = ThunderIDSvelteClient.getInstance(); - const r1 = await client.initialize({baseUrl: 'https://example.com', clientId: 'cid'}); - expect(r1).toBe(true); - expect(client.isInitialized).toBe(true); - }); - - it('should throw AlreadyInitialized on second call', async () => { - const client = ThunderIDSvelteClient.getInstance(); - await client.initialize({baseUrl: 'https://example.com', clientId: 'cid'}); - await expect(client.initialize({baseUrl: 'https://example.com', clientId: 'cid'})).rejects.toThrow(); - }); - - it('should throw InvalidConfiguration when baseUrl is missing', async () => { - const client = ThunderIDSvelteClient.getInstance(); - await expect(client.initialize({} as any)).rejects.toThrow(); - }); - - it('should throw InvalidConfiguration when baseUrl uses HTTP', async () => { - const client = ThunderIDSvelteClient.getInstance(); - await expect(client.initialize({baseUrl: 'http://example.com', clientId: 'cid'})).rejects.toThrow(); - }); - - it('should throw InvalidConfiguration when clientId is missing', async () => { - const client = ThunderIDSvelteClient.getInstance(); - await expect(client.initialize({baseUrl: 'https://example.com'} as any)).rejects.toThrow(); - }); - }); - - describe('rehydrateSessionFromPayload', () => { - it('should not throw when session is valid', async () => { - const client = ThunderIDSvelteClient.getInstance(); - await client.initialize({baseUrl: 'https://example.com', clientId: 'cid'}); - - const session: ThunderIDSessionPayload = { - accessToken: 'at-valid', - accessTokenExpiresAt: Math.floor(Date.now() / 1000) + 3600, - exp: Math.floor(Date.now() / 1000) + 3600, - iat: Math.floor(Date.now() / 1000), - scopes: 'openid', - sessionId: 'sess-001', - sub: 'user-001', - } as ThunderIDSessionPayload; - - await expect(client.rehydrateSessionFromPayload(session)).resolves.toBeUndefined(); - }); - - it('should silently return when not initialized', async () => { - const client = ThunderIDSvelteClient.getInstance(); - - const session: ThunderIDSessionPayload = { - accessToken: 'at', - exp: Math.floor(Date.now() / 1000) + 3600, - iat: Math.floor(Date.now() / 1000), - scopes: 'openid', - sessionId: 'sess-002', - sub: 'user-002', - } as ThunderIDSessionPayload; - - // Clearing any earlier initialized state - client.isInitialized = false; - await expect(client.rehydrateSessionFromPayload(session)).resolves.toBeUndefined(); - }); - - it('should silently return when session has no sessionId', async () => { - const client = ThunderIDSvelteClient.getInstance(); - await client.initialize({baseUrl: 'https://example.com', clientId: 'cid'}); - - const session = {accessToken: 'at'} as ThunderIDSessionPayload; - await expect(client.rehydrateSessionFromPayload(session)).resolves.toBeUndefined(); - }); - }); -}); diff --git a/packages/svelte/src/api/getAllOrganizations.ts b/packages/svelte/src/api/getAllOrganizations.ts deleted file mode 100644 index 64938fd..0000000 --- a/packages/svelte/src/api/getAllOrganizations.ts +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { - HttpResponse, - FetchHttpClient, - HttpRequestConfig, - getAllOrganizations as baseGetAllOrganizations, - GetAllOrganizationsConfig as BaseGetAllOrganizationsConfig, - AllOrganizationsApiResponse, -} from '@thunderid/browser'; - -export interface GetAllOrganizationsConfig extends Omit { - fetcher?: (url: string, config: RequestInit) => Promise; - instanceId?: number; -} - -const getAllOrganizations = async ({ - fetcher, - instanceId = 0, - ...requestConfig -}: GetAllOrganizationsConfig): Promise => { - const defaultFetcher = async (url: string, config: RequestInit): Promise => { - const httpClient: FetchHttpClient = FetchHttpClient.getInstance(instanceId); - - const response: HttpResponse = await httpClient.request({ - headers: config.headers as Record, - method: config.method || 'GET', - url, - } as HttpRequestConfig); - - return { - json: () => Promise.resolve(response.data), - ok: response.status >= 200 && response.status < 300, - status: response.status, - statusText: response.statusText || '', - text: () => Promise.resolve(typeof response.data === 'string' ? response.data : JSON.stringify(response.data)), - } as Response; - }; - - return baseGetAllOrganizations({ - ...requestConfig, - fetcher: fetcher || defaultFetcher, - }); -}; - -export default getAllOrganizations; diff --git a/packages/svelte/src/api/getMeOrganizations.ts b/packages/svelte/src/api/getMeOrganizations.ts deleted file mode 100644 index bbb05f7..0000000 --- a/packages/svelte/src/api/getMeOrganizations.ts +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { - Organization, - HttpResponse, - FetchHttpClient, - HttpRequestConfig, - getMeOrganizations as baseGetMeOrganizations, - GetMeOrganizationsConfig as BaseGetMeOrganizationsConfig, -} from '@thunderid/browser'; - -export interface GetMeOrganizationsConfig extends Omit { - fetcher?: (url: string, config: RequestInit) => Promise; - instanceId?: number; -} - -const getMeOrganizations = async ({ - fetcher, - instanceId = 0, - ...requestConfig -}: GetMeOrganizationsConfig): Promise => { - const defaultFetcher = async (url: string, config: RequestInit): Promise => { - const httpClient: FetchHttpClient = FetchHttpClient.getInstance(instanceId); - - const response: HttpResponse = await httpClient.request({ - headers: config.headers as Record, - method: config.method || 'GET', - url, - } as HttpRequestConfig); - - return { - json: () => Promise.resolve(response.data), - ok: response.status >= 200 && response.status < 300, - status: response.status, - statusText: response.statusText || '', - text: () => Promise.resolve(typeof response.data === 'string' ? response.data : JSON.stringify(response.data)), - } as Response; - }; - - return baseGetMeOrganizations({ - ...requestConfig, - fetcher: fetcher || defaultFetcher, - }); -}; - -export default getMeOrganizations; diff --git a/packages/svelte/src/api/getSchemas.ts b/packages/svelte/src/api/getSchemas.ts deleted file mode 100644 index 365cd28..0000000 --- a/packages/svelte/src/api/getSchemas.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { - Schema, - HttpResponse, - FetchHttpClient, - HttpRequestConfig, - getSchemas as baseGetSchemas, - GetSchemasConfig as BaseGetSchemasConfig, -} from '@thunderid/browser'; - -export interface GetSchemasConfig extends Omit { - fetcher?: (url: string, config: RequestInit) => Promise; - instanceId?: number; -} - -const getSchemas = async ({fetcher, instanceId = 0, ...requestConfig}: GetSchemasConfig): Promise => { - const defaultFetcher = async (url: string, config: RequestInit): Promise => { - const httpClient: FetchHttpClient = FetchHttpClient.getInstance(instanceId); - - const response: HttpResponse = await httpClient.request({ - headers: config.headers as Record, - method: config.method || 'GET', - url, - } as HttpRequestConfig); - - return { - json: () => Promise.resolve(response.data), - ok: response.status >= 200 && response.status < 300, - status: response.status, - statusText: response.statusText || '', - text: () => Promise.resolve(typeof response.data === 'string' ? response.data : JSON.stringify(response.data)), - } as Response; - }; - - return baseGetSchemas({ - ...requestConfig, - fetcher: fetcher || defaultFetcher, - }); -}; - -export default getSchemas; diff --git a/packages/svelte/src/api/getScim2Me.ts b/packages/svelte/src/api/getScim2Me.ts deleted file mode 100644 index 8ddc5e7..0000000 --- a/packages/svelte/src/api/getScim2Me.ts +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import { - User, - HttpResponse, - FetchHttpClient, - HttpRequestConfig, - getScim2Me as baseGetScim2Me, - GetScim2MeConfig as BaseGetScim2MeConfig, -} from '@thunderid/browser'; - -export interface GetScim2MeConfig extends Omit { - fetcher?: (url: string, config: RequestInit) => Promise; - instanceId?: number; -} - -const getScim2Me = async ({fetcher, instanceId = 0, ...requestConfig}: GetScim2MeConfig): Promise => { - const defaultFetcher = async (url: string, config: RequestInit): Promise => { - const httpClient: FetchHttpClient = FetchHttpClient.getInstance(instanceId); - - const response: HttpResponse = await httpClient.request({ - headers: config.headers as Record, - method: config.method || 'GET', - url, - } as HttpRequestConfig); - - return { - json: () => Promise.resolve(response.data), - ok: response.status >= 200 && response.status < 300, - status: response.status, - statusText: response.statusText || '', - text: () => Promise.resolve(typeof response.data === 'string' ? response.data : JSON.stringify(response.data)), - } as Response; - }; - - return baseGetScim2Me({ - ...requestConfig, - fetcher: fetcher || defaultFetcher, - }); -}; - -export default getScim2Me; diff --git a/packages/svelte/src/index.ts b/packages/svelte/src/index.ts deleted file mode 100644 index 933ff84..0000000 --- a/packages/svelte/src/index.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -// ── Components ── -export {default as ThunderID} from './ThunderID.svelte'; -export {default as SignedIn} from './components/control/SignedIn.svelte'; -export {default as SignedOut} from './components/control/SignedOut.svelte'; -export {default as Loading} from './components/control/Loading.svelte'; -export {default as SignInButton} from './components/actions/SignInButton.svelte'; -export {default as SignOutButton} from './components/actions/SignOutButton.svelte'; -export {default as SignUpButton} from './components/actions/SignUpButton.svelte'; -export {default as Callback} from './components/auth/Callback.svelte'; - -// ── Client ── -export {default as ThunderIDSvelteClient} from './ThunderIDSvelteClient'; - -// ── Context ── -export {THUNDERID_KEY, USER_KEY} from './context'; - -// ── Composables ── -export {useThunderID} from './composables/useThunderID'; -export {useUser} from './composables/useUser'; - -// ── Models / Types ── -export type {ThunderIDSvelteConfig} from './models/config'; -export type {ThunderIDSSRData, ThunderIDSessionPayload} from './models/session'; -export type {BrandingPreference} from '@thunderid/node'; -export type {ThunderIDContext, UserContextValue} from './models/contexts'; - -// ── Errors ── -export {IAMError, ErrorCode} from './errors/IAMError'; - -// ── Logger ── -export {DefaultLogger, setLogger, getLogger} from './logger/LoggerAdapter'; -export type {LoggerAdapter} from './logger/LoggerAdapter'; -export {sanitizeForLog, sanitizeTokenForLog} from './logger/sanitizer'; diff --git a/packages/svelte/src/models/config.ts b/packages/svelte/src/models/config.ts deleted file mode 100644 index 444072a..0000000 --- a/packages/svelte/src/models/config.ts +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export interface ThunderIDSvelteConfig { - /** URL to redirect to after sign-in (default: '/') */ - afterSignInUrl?: string; - /** URL to redirect to after sign-out (default: '/') */ - afterSignOutUrl?: string; - /** - * ThunderID application id (`spId`) — appended to the redirect-based sign-up - * URL when present. - */ - applicationId?: string; - /** Base URL of the ThunderID org tenant (e.g. https://localhost:8090) */ - baseUrl?: string; - /** OAuth2 Client ID */ - clientId?: string; - /** OAuth2 Client Secret (server-only, use THUNDERID_CLIENT_SECRET env var) */ - clientSecret?: string; - /** @deprecated PKCE is always enabled and cannot be disabled. */ - enablePKCE?: boolean; - /** Token endpoint authentication method (default: 'client_secret_post') */ - tokenRequest?: { - authMethod?: 'client_secret_basic' | 'client_secret_post' | 'private_key_jwt' | 'none'; - }; - /** OAuth2 scopes to request */ - scopes?: string | string[]; - /** Secret for signing session JWTs (use THUNDERID_SESSION_SECRET env var) */ - sessionSecret?: string; - /** Custom sign-in URL (overrides the default authorize endpoint URL) */ - signInUrl?: string; - /** Custom sign-up URL */ - signUpUrl?: string; - /** Controls which server-side data fetches to perform on every SSR request */ - preferences?: { - theme?: { - /** - * When true (default), the handle hook fetches the branding preference - * from ThunderID and passes it in `ThunderIDSSRData.brandingPreference`. - */ - inheritFromBranding?: boolean; - }; - user?: { - /** Whether to fetch the user's organizations during SSR (default: true) */ - fetchOrganizations?: boolean; - /** Whether to fetch the SCIM2 user profile during SSR (default: true) */ - fetchUserProfile?: boolean; - }; - }; -} diff --git a/packages/svelte/src/models/contexts.ts b/packages/svelte/src/models/contexts.ts deleted file mode 100644 index 1224205..0000000 --- a/packages/svelte/src/models/contexts.ts +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import type {BrandingPreference, Organization, User, UserProfile} from '@thunderid/node'; - -export interface ThunderIDContext { - afterSignInUrl?: string; - afterSignOutUrl?: string; - applicationId?: string; - baseUrl?: string; - brandingPreference: BrandingPreference | null; - clientId?: string; - isInitialized: boolean; - isLoading: boolean; - isSignedIn: boolean; - myOrganizations: Organization[]; - organization: Organization | null; - organizationHandle?: string; - resolvedBaseUrl: string; - scopes?: string | string[]; - signInUrl?: string; - signUpUrl?: string; - user: User | null; - userProfile: UserProfile | null; - - signIn: (...args: any[]) => Promise; - signOut: (...args: any[]) => Promise; - signUp: (...args: any[]) => Promise; -} - -export interface UserContextValue { - flattenedProfile: User | null; - profile: UserProfile | null; - schemas: any[] | null; -} diff --git a/packages/svelte/src/server/__tests__/guard.test.ts b/packages/svelte/src/server/__tests__/guard.test.ts deleted file mode 100644 index bdd82db..0000000 --- a/packages/svelte/src/server/__tests__/guard.test.ts +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import type {RequestEvent} from '@sveltejs/kit'; -import {describe, it, expect, vi, beforeEach} from 'vitest'; -import type {ThunderIDSSRData} from '../../models/session'; - -const mockRedirect = vi.fn(); -vi.mock('@sveltejs/kit', () => ({ - redirect: mockRedirect, -})); - -const {requireServerSession} = await import('../guard'); - -function createMockEvent(ssrData: ThunderIDSSRData | undefined): RequestEvent { - return {locals: {thunderid: ssrData}} as unknown as RequestEvent; -} - -describe('requireServerSession', () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - it('should return SSR data when signed in', () => { - const ssrData: ThunderIDSSRData = { - brandingPreference: null, - isSignedIn: true, - myOrganizations: [], - organization: null, - resolvedBaseUrl: null, - session: null, - user: {id: 'u1'} as any, - userProfile: null, - }; - - const result = requireServerSession(createMockEvent(ssrData)); - expect(result).toBe(ssrData); - expect(mockRedirect).not.toHaveBeenCalled(); - }); - - it('should throw redirect when not signed in', () => { - const ssrData: ThunderIDSSRData = { - brandingPreference: null, - isSignedIn: false, - myOrganizations: [], - organization: null, - resolvedBaseUrl: null, - session: null, - user: null, - userProfile: null, - }; - - mockRedirect.mockImplementation(() => { - throw new Error('redirect thrown'); - }); - - expect(() => requireServerSession(createMockEvent(ssrData))).toThrow('redirect thrown'); - expect(mockRedirect).toHaveBeenCalledWith(307, '/api/auth/signin'); - }); - - it('should throw redirect with custom redirectTo', () => { - const ssrData: ThunderIDSSRData = { - brandingPreference: null, - isSignedIn: false, - myOrganizations: [], - organization: null, - resolvedBaseUrl: null, - session: null, - user: null, - userProfile: null, - }; - - mockRedirect.mockImplementation(() => { - throw new Error('redirect thrown'); - }); - - expect(() => requireServerSession(createMockEvent(ssrData), '/custom/signin')).toThrow('redirect thrown'); - expect(mockRedirect).toHaveBeenCalledWith(307, '/custom/signin'); - }); - - it('should throw redirect when SSR data is undefined', () => { - mockRedirect.mockImplementation(() => { - throw new Error('redirect thrown'); - }); - - const event = {locals: {}} as RequestEvent; - expect(() => requireServerSession(event)).toThrow('redirect thrown'); - }); -}); diff --git a/packages/svelte/src/server/app.d.ts b/packages/svelte/src/server/app.d.ts deleted file mode 100644 index 393456b..0000000 --- a/packages/svelte/src/server/app.d.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import type {ThunderIDSSRData} from '../models/session'; - -declare global { - namespace App { - interface Locals { - thunderid: ThunderIDSSRData; - } - } -} diff --git a/packages/svelte/src/server/guard.ts b/packages/svelte/src/server/guard.ts deleted file mode 100644 index 8ff4f48..0000000 --- a/packages/svelte/src/server/guard.ts +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import {redirect} from '@sveltejs/kit'; -import type {RequestEvent} from '@sveltejs/kit'; -import type {ThunderIDSSRData, ThunderIDSessionPayload} from '../models/session'; - -/** - * Read the ThunderID SSR data from `event.locals` (set by `createThunderIDHandle`) - * and throw a SvelteKit redirect to the sign-in page if the user is not authenticated. - * - * Use in `+page.server.ts` or `+layout.server.ts` load functions to protect routes. - * - * @example - * ```ts - * // src/routes/dashboard/+page.server.ts - * import { requireServerSession } from '@thunderid/svelte/server'; - * - * export const load = (event) => { - * const session = requireServerSession(event); - * return { email: session.user?.email }; - * }; - * ``` - */ -export function requireServerSession( - event: RequestEvent, - redirectTo?: string, -): ThunderIDSSRData { - const ssrData: ThunderIDSSRData = event.locals.thunderid; - - if (!ssrData?.isSignedIn) { - redirect(307, redirectTo || '/api/auth/signin'); - } - - return ssrData; -} diff --git a/packages/svelte/src/server/hooks.ts b/packages/svelte/src/server/hooks.ts deleted file mode 100644 index 7f6a71c..0000000 --- a/packages/svelte/src/server/hooks.ts +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import type {Handle, RequestEvent} from '@sveltejs/kit'; -import type {BrandingPreference} from '@thunderid/node'; -import {resolveConfig} from './config'; -import {getClient} from './getClient'; -import {maybeRefreshToken} from './refresh'; -import {verifySessionToken, getSessionCookieName} from './session'; -import type {ThunderIDSvelteConfig} from '../models/config'; -import type {ThunderIDSSRData, ThunderIDSessionPayload} from '../models/session'; -import ThunderIDSvelteClient from '../ThunderIDSvelteClient'; - -const CALLBACK_PATH = '/api/auth/callback'; - -export function createThunderIDHandle(config?: ThunderIDSvelteConfig): Handle { - const resolvedConfig: ThunderIDSvelteConfig = resolveConfig(config); - - return async ({event, resolve}) => { - const callbackUrl = `${event.url.origin}${CALLBACK_PATH}`; - const client: ThunderIDSvelteClient = await getClient({...resolvedConfig, afterSignInUrl: callbackUrl}); - - const sessionCookie: string | undefined = event.cookies.get(getSessionCookieName()); - let session: ThunderIDSessionPayload | null = null; - - if (sessionCookie) { - try { - session = await verifySessionToken(sessionCookie, resolvedConfig.sessionSecret); - - session = await maybeRefreshToken(session, resolvedConfig, event); - - if (session) { - await client.rehydrateSessionFromPayload(session); - } - } catch { - event.cookies.delete(getSessionCookieName(), {path: '/'}); - session = null; - } - } - - const isSignedIn: boolean = session !== null && (await client.isSignedIn(session.sessionId)); - - const shouldFetchBranding: boolean = resolvedConfig.preferences?.theme?.inheritFromBranding !== false; - - let ssrData: ThunderIDSSRData; - - if (isSignedIn && session) { - const [user, userProfile, organization, myOrganizations, branding] = await Promise.all([ - client.getUser(session.sessionId), - client.getUserProfile(session.sessionId), - client.getCurrentOrganization(session.sessionId), - resolvedConfig.preferences?.user?.fetchOrganizations !== false - ? client.getMyOrganizations(session.sessionId).catch(() => []) - : Promise.resolve([]), - shouldFetchBranding - ? client.getBrandingPreference({baseUrl: resolvedConfig.baseUrl!}).catch(() => null) - : Promise.resolve(null), - ]); - - ssrData = { - brandingPreference: (branding!) ?? null, - isSignedIn: true, - myOrganizations: myOrganizations as any[], - organization: organization as any, - resolvedBaseUrl: resolvedConfig.baseUrl ?? null, - session, - user: user as any, - userProfile: userProfile as any, - }; - } else { - let branding: BrandingPreference | null = null; - - if (shouldFetchBranding) { - try { - branding = await client.getBrandingPreference({baseUrl: resolvedConfig.baseUrl!}); - } catch { - // branding fetch failed — continue without - } - } - - ssrData = { - brandingPreference: branding, - isSignedIn: false, - myOrganizations: [], - organization: null, - resolvedBaseUrl: null, - session: null, - user: null, - userProfile: null, - }; - } - - event.locals.thunderid = ssrData; - - return resolve(event); - }; -} - -export function loadThunderID(event: RequestEvent): ThunderIDSSRData { - return event.locals.thunderid; -} diff --git a/packages/svelte/src/server/index.ts b/packages/svelte/src/server/index.ts deleted file mode 100644 index 26677c0..0000000 --- a/packages/svelte/src/server/index.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -export {resolveConfig} from './config'; -export {createThunderIDHandle} from './hooks'; -export {loadThunderID} from './load'; -export {getClient, resetClient} from './getClient'; -export { - createSessionToken, - createTempSessionToken, - verifySessionToken, - verifyTempSessionToken, - issueSessionCookie, - getSessionCookieName, - getTempSessionCookieName, - getSessionCookieOptions, - getTempSessionCookieOptions, -} from './session'; -export {maybeRefreshToken, getValidAccessToken} from './refresh'; -export {requireServerSession} from './guard'; -export {createSignInHandler, createCallbackHandler, createSignOutHandler, createOrgSwitchHandler} from './routes'; diff --git a/packages/svelte/src/server/load.ts b/packages/svelte/src/server/load.ts deleted file mode 100644 index 29d2514..0000000 --- a/packages/svelte/src/server/load.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import type {RequestEvent} from '@sveltejs/kit'; -import type {ThunderIDSSRData} from '../models/session'; - -export function loadThunderID(event: RequestEvent): ThunderIDSSRData { - return event.locals.thunderid; -} diff --git a/packages/svelte/src/server/refresh.ts b/packages/svelte/src/server/refresh.ts deleted file mode 100644 index a6f4274..0000000 --- a/packages/svelte/src/server/refresh.ts +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import type {RequestEvent} from '@sveltejs/kit'; -import {createSessionToken, getSessionCookieName, getSessionCookieOptions} from './session'; -import type {ThunderIDSvelteConfig} from '../models/config'; -import type {ThunderIDSessionPayload} from '../models/session'; - -const REFRESH_SKEW_SECONDS = 60; - -interface OIDCTokenRefreshResponse { - access_token: string; - expires_in?: number; - id_token?: string; - refresh_token?: string; - scope?: string; - token_type?: string; -} - -/** - * Attempt to refresh the access token when it is within the skew window of - * expiry. Returns the (potentially refreshed) session payload, or null when - * the refresh fails. - */ -export async function maybeRefreshToken( - session: ThunderIDSessionPayload, - config: ThunderIDSvelteConfig, - event: Pick, -): Promise { - const now: number = Math.floor(Date.now() / 1000); - - if (!session.accessTokenExpiresAt || session.accessTokenExpiresAt - REFRESH_SKEW_SECONDS > now) { - return session; - } - - if (!session.refreshToken) { - return null; - } - - const tokenEndpoint = `${config.baseUrl}/oauth2/token`; - - const body: URLSearchParams = new URLSearchParams({ - client_id: config.clientId!, - grant_type: 'refresh_token', - refresh_token: session.refreshToken, - }); - - if (config.clientSecret) { - body.set('client_secret', config.clientSecret); - } - - let refreshed: OIDCTokenRefreshResponse; - try { - const res: Response = await fetch(tokenEndpoint, { - body, - headers: {'Content-Type': 'application/x-www-form-urlencoded'}, - method: 'POST', - }); - - if (!res.ok) { - return null; - } - - refreshed = (await res.json()) as OIDCTokenRefreshResponse; - } catch { - return null; - } - - const newSessionToken: string = await createSessionToken( - { - accessToken: refreshed.access_token, - accessTokenExpiresAt: now + (refreshed.expires_in ?? 3600), - idToken: refreshed.id_token ?? session.idToken, - organizationId: session.organizationId, - refreshToken: refreshed.refresh_token ?? session.refreshToken, - scopes: refreshed.scope ?? session.scopes, - sessionId: session.sessionId, - userId: session.sub, - }, - config.sessionSecret, - ); - - event.cookies.set(getSessionCookieName(), newSessionToken, getSessionCookieOptions()); - - return { - ...session, - accessToken: refreshed.access_token, - accessTokenExpiresAt: now + (refreshed.expires_in ?? 3600), - idToken: refreshed.id_token ?? session.idToken, - refreshToken: refreshed.refresh_token ?? session.refreshToken, - scopes: refreshed.scope ?? session.scopes, - }; -} - -/** - * Return a valid access token for the current request. - * - * Reads the session from `event.locals` (set by the handle hook), refreshes - * it if needed, and returns a guaranteed-fresh access token. - * - * Throws a redirect to the sign-in page when the session is missing or the - * refresh fails. - */ -export async function getValidAccessToken( - event: RequestEvent, - config: ThunderIDSvelteConfig, -): Promise { - const {loadThunderID} = await import('./load'); - const ssrData = loadThunderID(event); - - if (!ssrData.session) { - throw new Error('Not authenticated'); - } - - const refreshed = await maybeRefreshToken(ssrData.session, config, event); - - if (!refreshed) { - throw new Error('Session expired'); - } - - return refreshed.accessToken; -} diff --git a/packages/svelte/src/server/routes/callback.ts b/packages/svelte/src/server/routes/callback.ts deleted file mode 100644 index 1df6e42..0000000 --- a/packages/svelte/src/server/routes/callback.ts +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import type {TokenResponse} from '@thunderid/node'; -import {getLogger} from '../../logger/LoggerAdapter'; -import type {ThunderIDSvelteConfig} from '../../models/config'; -import ThunderIDSvelteClient from '../../ThunderIDSvelteClient'; -import {resolveConfig} from '../config'; -import {issueSessionCookie, verifyTempSessionToken, getTempSessionCookieName, getSessionCookieName} from '../session'; - -export function createCallbackHandler(config?: ThunderIDSvelteConfig): (event: {url: URL; cookies: any}) => Promise { - const resolvedConfig: ThunderIDSvelteConfig = resolveConfig(config); - const logger = getLogger(); - - return async (event) => { - const client: ThunderIDSvelteClient = ThunderIDSvelteClient.getInstance(); - - const tempCookie: string | undefined = event.cookies.get(getTempSessionCookieName()); - - if (!tempCookie) { - return new Response(null, {status: 302, headers: {Location: resolvedConfig.afterSignInUrl || '/'}}); - } - - let sessionId: string; - let returnTo: string | undefined; - - try { - const tempPayload = await verifyTempSessionToken(tempCookie, resolvedConfig.sessionSecret); - sessionId = tempPayload.sessionId; - returnTo = tempPayload.returnTo; - } catch { - event.cookies.delete(getTempSessionCookieName(), {path: '/'}); - return new Response(null, {status: 302, headers: {Location: resolvedConfig.afterSignInUrl || '/'}}); - } - - const code: string | null = event.url.searchParams.get('code'); - const state: string | null = event.url.searchParams.get('state'); - const sessionState: string | null = event.url.searchParams.get('session_state'); - - if (code) { - let tokenResponse: TokenResponse; - - try { - tokenResponse = (await client.signIn( - {code, session_state: sessionState ?? undefined, state: state ?? undefined}, - undefined, - sessionId, - )) as unknown as TokenResponse; - } catch (err: unknown) { - const message: string = (err as any)?.message ?? String(err); - logger.error('callback signIn failed', err instanceof Error ? err : new Error(message)); - return new Response(JSON.stringify({error: message}), { - status: 500, - headers: {'content-type': 'application/json'}, - }); - } - - if (tokenResponse.accessToken) { - const sessionPayload = await issueSessionCookie(event, sessionId, tokenResponse, resolvedConfig.sessionSecret); - - event.cookies.delete(getTempSessionCookieName(), {path: '/'}); - - return new Response(null, { - status: 302, - headers: {Location: returnTo || resolvedConfig.afterSignInUrl || '/'}, - }); - } - } - - const error: string | null = event.url.searchParams.get('error'); - if (error) { - return new Response(null, {status: 302, headers: {Location: `${resolvedConfig.afterSignInUrl || '/'}?error=${error}`}}); - } - - return new Response(null, {status: 302, headers: {Location: resolvedConfig.afterSignInUrl || '/'}}); - }; -} diff --git a/packages/svelte/src/server/routes/orgSwitch.ts b/packages/svelte/src/server/routes/orgSwitch.ts deleted file mode 100644 index 2de97b1..0000000 --- a/packages/svelte/src/server/routes/orgSwitch.ts +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import type {Organization, TokenResponse} from '@thunderid/node'; -import {getLogger} from '../../logger/LoggerAdapter'; -import type {ThunderIDSvelteConfig} from '../../models/config'; -import ThunderIDSvelteClient from '../../ThunderIDSvelteClient'; -import {resolveConfig} from '../config'; -import {verifySessionToken, issueSessionCookie, getSessionCookieName} from '../session'; - -export function createOrgSwitchHandler( - config?: ThunderIDSvelteConfig, -): (event: {request: Request; cookies: any; url: URL}) => Promise { - const resolvedConfig: ThunderIDSvelteConfig = resolveConfig(config); - const logger = getLogger(); - - return async (event) => { - const client: ThunderIDSvelteClient = ThunderIDSvelteClient.getInstance(); - - const sessionCookie: string | undefined = event.cookies.get(getSessionCookieName()); - if (!sessionCookie) { - return new Response(null, {status: 401, statusText: 'Not authenticated'}); - } - - let sessionId: string; - try { - const session = await verifySessionToken(sessionCookie, resolvedConfig.sessionSecret); - sessionId = session.sessionId; - } catch { - return new Response(null, {status: 401, statusText: 'Invalid session'}); - } - - let body: {organizationId?: string}; - try { - body = (await event.request.json()) as {organizationId?: string}; - } catch { - return new Response(JSON.stringify({error: 'Invalid request body'}), { - status: 400, - headers: {'content-type': 'application/json'}, - }); - } - - if (!body.organizationId) { - return new Response(JSON.stringify({error: 'organizationId is required'}), { - status: 400, - headers: {'content-type': 'application/json'}, - }); - } - - const organization: Organization = {id: body.organizationId, name: '', orgHandle: ''}; - - try { - const tokenResponse: TokenResponse = (await client.switchOrganization( - organization, - sessionId, - )) as unknown as TokenResponse; - - if (!tokenResponse.accessToken) { - throw new Error('No access token in response'); - } - - await issueSessionCookie(event, sessionId, tokenResponse, resolvedConfig.sessionSecret); - - return new Response(null, { - status: 302, - headers: {Location: resolvedConfig.afterSignInUrl || '/'}, - }); - } catch (err: unknown) { - const message: string = (err as any)?.message ?? 'Organization switch failed'; - logger.error('organization switch failed', err instanceof Error ? err : new Error(message)); - return new Response(JSON.stringify({error: message}), { - status: 500, - headers: {'content-type': 'application/json'}, - }); - } - }; -} diff --git a/packages/svelte/src/server/routes/signin.ts b/packages/svelte/src/server/routes/signin.ts deleted file mode 100644 index 949cd2e..0000000 --- a/packages/svelte/src/server/routes/signin.ts +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import {generateSessionId} from '@thunderid/node'; -import {getLogger} from '../../logger/LoggerAdapter'; -import type {ThunderIDSvelteConfig} from '../../models/config'; -import ThunderIDSvelteClient from '../../ThunderIDSvelteClient'; -import {resolveConfig} from '../config'; -import {createTempSessionToken, getTempSessionCookieName, getTempSessionCookieOptions} from '../session'; - -export function createSignInHandler(config?: ThunderIDSvelteConfig): (event: {url: URL; cookies: any}) => Promise { - const resolvedConfig: ThunderIDSvelteConfig = resolveConfig(config); - const logger = getLogger(); - - return async (event) => { - try { - const client: ThunderIDSvelteClient = ThunderIDSvelteClient.getInstance(); - const sessionId: string = generateSessionId(); - - const returnTo: string = event.url.searchParams.get('returnTo') || resolvedConfig.afterSignInUrl || '/'; - - const authorizeUrl: string = await client.getAuthorizeRequestUrl( - {client_secret: '{{clientSecret}}'}, - sessionId, - ); - - const tempToken: string = await createTempSessionToken(sessionId, resolvedConfig.sessionSecret, returnTo); - - event.cookies.set(getTempSessionCookieName(), tempToken, getTempSessionCookieOptions()); - - return new Response(null, { - status: 302, - headers: {Location: authorizeUrl}, - }); - } catch (err: unknown) { - const message: string = (err as any)?.message ?? String(err); - logger.error('sign-in failed', err instanceof Error ? err : new Error(message)); - return new Response(JSON.stringify({error: message}), { - status: 500, - headers: {'content-type': 'application/json'}, - }); - } - }; -} diff --git a/packages/svelte/src/server/routes/signout.ts b/packages/svelte/src/server/routes/signout.ts deleted file mode 100644 index 1f803b0..0000000 --- a/packages/svelte/src/server/routes/signout.ts +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import {getLogger} from '../../logger/LoggerAdapter'; -import type {ThunderIDSvelteConfig} from '../../models/config'; -import ThunderIDSvelteClient from '../../ThunderIDSvelteClient'; -import {resolveConfig} from '../config'; -import {getSessionCookieName} from '../session'; - -export function createSignOutHandler(config?: ThunderIDSvelteConfig): (event: {cookies: any}) => Promise { - const resolvedConfig: ThunderIDSvelteConfig = resolveConfig(config); - const logger = getLogger(); - - return async (event) => { - const client: ThunderIDSvelteClient = ThunderIDSvelteClient.getInstance(); - const redirectUrl: string = resolvedConfig.afterSignOutUrl || '/'; - - try { - await client.revokeAccessToken(); - await client.signOut(); - } catch (err: unknown) { - logger.warn('sign-out encountered an issue', {error: (err as any)?.message ?? String(err)}); - } - - event.cookies.delete(getSessionCookieName(), {path: '/'}); - - return new Response(null, { - status: 302, - headers: {Location: redirectUrl}, - }); - }; -} diff --git a/packages/svelte/svelte.config.js b/packages/svelte/svelte.config.js deleted file mode 100644 index bc82cc5..0000000 --- a/packages/svelte/svelte.config.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import {vitePreprocess} from '@sveltejs/vite-plugin-svelte'; - -export default { - preprocess: vitePreprocess(), -}; diff --git a/packages/svelte/eslint.config.js b/packages/sveltekit/eslint.config.js similarity index 79% rename from packages/svelte/eslint.config.js rename to packages/sveltekit/eslint.config.js index a70b578..a6d562c 100644 --- a/packages/svelte/eslint.config.js +++ b/packages/sveltekit/eslint.config.js @@ -20,14 +20,7 @@ import thunderIdPlugin from '@thunderid/eslint-plugin'; export default [ { - ignores: [ - 'dist/**', - 'build/**', - 'node_modules/**', - 'coverage/**', - '.svelte-kit/**', - 'vitest.config.ts', - ], + ignores: ['.svelte-kit/**', 'dist/**', 'build/**', 'node_modules/**', 'coverage/**'], }, - ...thunderIdPlugin.configs.vue, + ...thunderIdPlugin.configs.typescript, ]; diff --git a/packages/svelte/package.json b/packages/sveltekit/package.json similarity index 88% rename from packages/svelte/package.json rename to packages/sveltekit/package.json index 2c8fb25..bc1da7d 100644 --- a/packages/svelte/package.json +++ b/packages/sveltekit/package.json @@ -1,15 +1,16 @@ { - "name": "@thunderid/svelte", - "version": "0.2.0", - "description": "Svelte SDK for ThunderID — full SvelteKit SSR support", + "name": "@thunderid/sveltekit", + "version": "0.1.0", + "description": "SvelteKit SDK for ThunderID — Svelte 5 / SvelteKit authentication with CSR and SSR support", "keywords": [ "thunderid", "svelte", "sveltekit", - "svelteauth", - "ssr" + "auth", + "oauth", + "oidc" ], - "homepage": "https://github.com/thunder-id/javascript-sdks/tree/main/packages/svelte#readme", + "homepage": "https://github.com/thunder-id/javascript-sdks/tree/main/packages/sveltekit#readme", "bugs": { "url": "https://github.com/thunder-id/thunderid/issues" }, diff --git a/packages/sveltekit/src/ThunderID.svelte b/packages/sveltekit/src/ThunderID.svelte new file mode 100644 index 0000000..407cc5e --- /dev/null +++ b/packages/sveltekit/src/ThunderID.svelte @@ -0,0 +1,297 @@ + + +{@render children?.()} diff --git a/packages/sveltekit/src/ThunderIDSvelteClient.ts b/packages/sveltekit/src/ThunderIDSvelteClient.ts new file mode 100644 index 0000000..28959e6 --- /dev/null +++ b/packages/sveltekit/src/ThunderIDSvelteClient.ts @@ -0,0 +1,622 @@ +import { + ThunderIDBrowserClient, + type AuthClientConfig, + type BrandingPreference, + type GetBrandingPreferenceConfig, + type IdToken, + type SPATokenExchangeConfig, + type Storage, + type StorageManager, + type TokenResponse, + type UpdateMeProfileConfig, + type User, + type UserProfile, + getBrandingPreference, + updateMeProfile, +} from '@thunderid/browser'; +import {emit, on, off, clearListeners, SDKEvent} from './events/EventBus'; +import type {SDKEventListener} from './events/EventBus'; +import {IAMError, ErrorCode} from './errors/IAMError'; +import {getLogger, type LoggerAdapter} from './logger/LoggerAdapter'; +import {verifyIdToken, type IdTokenValidationResult} from './validation/IdTokenValidator'; +import {DefaultHTTPAdapter, type HTTPAdapter, type HTTPResponse} from './adapters/HTTPAdapter'; +import type {ThunderIDSvelteKitConfig} from './models/config'; + +class ThunderIDSvelteClient extends ThunderIDBrowserClient> { + private static instance: ThunderIDSvelteClient; + + private _clientInitialized = false; + private _isLoading = false; + private logger: LoggerAdapter = getLogger(); + private httpAdapter?: HTTPAdapter; + + private constructor() { + super(); + } + + public static getInstance(): ThunderIDSvelteClient { + if (!ThunderIDSvelteClient.instance) { + ThunderIDSvelteClient.instance = new ThunderIDSvelteClient(); + } + return ThunderIDSvelteClient.instance; + } + + override async isInitialized(): Promise { + return this._clientInitialized; + } + + override async initialize(config: ThunderIDSvelteKitConfig, storage?: Storage): Promise { + if (this._clientInitialized) { + throw new IAMError({ + code: ErrorCode.ALREADY_INITIALIZED, + message: 'ThunderID SDK is already initialized. Call reset() first if you need to re-initialize.', + }); + } + + if (!config.baseUrl) { + throw new IAMError({ + code: ErrorCode.INVALID_CONFIGURATION, + message: 'baseUrl is required. Set THUNDERID_BASE_URL environment variable or pass it in the config.', + }); + } + + if (!config.baseUrl.startsWith('https://')) { + throw new IAMError({ + code: ErrorCode.INVALID_CONFIGURATION, + message: 'baseUrl must use HTTPS.', + }); + } + + if (!config.clientId) { + throw new IAMError({ + code: ErrorCode.INVALID_CONFIGURATION, + message: 'clientId is required. Set THUNDERID_CLIENT_ID environment variable or pass it in the config.', + }); + } + + const authConfig: AuthClientConfig = { + afterSignInUrl: config.afterSignInUrl ?? '/', + afterSignOutUrl: config.afterSignOutUrl ?? '/', + allowedExternalUrls: config.allowedExternalUrls, + applicationId: config.applicationId, + baseUrl: config.baseUrl, + clientId: config.clientId, + clientSecret: config.clientSecret ?? undefined, + discovery: config.discovery, + enablePKCE: true, + endpoints: { + ...(config.discovery?.wellKnown?.enabled !== false + ? {wellKnown: `${config.baseUrl}/.well-known/openid-configuration`} + : {}), + ...(config.endpoints ?? {}), + } as any, + instanceId: config.instanceId, + mode: config.mode, + + platform: config.platform, + preferences: config.preferences, + prompt: config.prompt, + responseMode: config.responseMode, + scopes: config.scopes ?? ['openid'], + sendCookiesInRequests: config.sendCookiesInRequests, + sendIdTokenInLogoutRequest: config.sendIdTokenInLogoutRequest, + signInOptions: config.signInOptions, + signInUrl: config.signInUrl, + signOutOptions: config.signOutOptions, + signUpOptions: config.signUpOptions, + signUpUrl: config.signUpUrl, + storage: config.storage, + syncSession: config.syncSession, + tokenLifecycle: config.tokenLifecycle, + tokenRequest: config.tokenRequest ?? {authMethod: 'client_secret_post'}, + tokenValidation: config.tokenValidation, + } as AuthClientConfig; + + this.httpAdapter = config.httpAdapter; + + this._isLoading = true; + try { + await super.initialize(authConfig as any, storage); + + this._clientInitialized = true; + emit(SDKEvent.INITIALIZED); + return true; + } finally { + this._isLoading = false; + } + } + + override async reInitialize(config: Partial): Promise { + if (!this._clientInitialized) { + throw new IAMError({ + code: ErrorCode.SDK_NOT_INITIALIZED, + message: 'SDK is not initialized. Call initialize() first.', + }); + } + await super.reInitialize(config as any); + return true; + } + + async reset(): Promise { + this._clientInitialized = false; + this._isLoading = false; + try { + await super.clearSession(); + const sm = this.getStorageManager(); + await sm.removeConfigData(); + await sm.removeOIDCProviderMetaData(); + await sm.removeTemporaryData(); + await sm.removeHybridData(); + } catch { + // ignore — session may not exist + } + } + + addEventListener(event: SDKEvent | string, listener: SDKEventListener): void { + on(event, listener); + } + + removeEventListener(event: SDKEvent | string, listener: SDKEventListener): void { + off(event, listener); + } + + clearEventListeners(event?: SDKEvent | string): void { + clearListeners(event); + } + + override isLoading(): boolean { + return this._isLoading; + } + + override getConfiguration(): AuthClientConfig { + return this.getStorageManager().getConfigData() as unknown as AuthClientConfig; + } + + override async signIn( + config?: any, + authorizationCode?: string, + sessionState?: string, + state?: string, + tokenRequestConfig?: {params: Record}, + onSuccess?: (user: any) => void, + ): Promise { + const arg0: unknown = config; + + if (typeof arg0 === 'object' && arg0 !== null) { + if ('_subject' in arg0 || 'stepType' in arg0) { + emit(SDKEvent.MFA_STEP_REQUIRED, { + message: 'App-Native (embedded) sign-in mode is not yet implemented. Multi-factor authentication steps will be emitted here in a future release.', + }); + throw new IAMError({ + code: ErrorCode.NOT_IMPLEMENTED, + message: 'App-Native (embedded) sign-in mode is not yet implemented. This will be available in a future release.', + }); + } + + if ('code' in arg0 || 'state' in arg0) { + const payload: {code?: unknown; session_state?: unknown; state?: unknown} = arg0 as { + code?: unknown; + session_state?: unknown; + state?: unknown; + }; + const code: string | undefined = typeof payload.code === 'string' ? payload.code : undefined; + const ss: string | undefined = typeof payload.session_state === 'string' ? payload.session_state : undefined; + const st: string | undefined = typeof payload.state === 'string' ? payload.state : undefined; + + try { + const user = await super.signIn(undefined, code, ss, st); + if (user && onSuccess) { + onSuccess(user); + } + return user; + } catch (err: unknown) { + emit(SDKEvent.SIGN_IN_FAILED, {error: err instanceof Error ? err.message : String(err)}); + throw err; + } + } + } + + const nonce: string = crypto.randomUUID(); + if (typeof sessionStorage !== 'undefined') { + sessionStorage.setItem('thunderid-nonce', nonce); + } + try { + const user = await super.signIn({...(config || {}), nonce}, authorizationCode, sessionState, state, tokenRequestConfig); + if (user) { + emit(SDKEvent.SIGN_IN, {user}); + onSuccess?.(user); + } + return user; + } catch (err: unknown) { + emit(SDKEvent.SIGN_IN_FAILED, {error: err instanceof Error ? err.message : String(err)}); + throw err; + } + } + + override async signOut(...args: any[]): Promise { + const configData: any = await this.getStorageManager().getConfigData(); + const afterSignOutUrl: string = + (configData?.afterSignOutUrl as string) || (configData?.afterSignInUrl as string) || '/'; + emit(SDKEvent.SIGN_OUT, {afterSignOutUrl}); + + return super.signOut(args[0], args[1], args[2]); + } + + override async revokeAccessToken(userId?: string): Promise { + // Bypass BrowserClient._validateMethod() which calls isSignedIn() without userId + const jsProto = Object.getPrototypeOf(ThunderIDBrowserClient.prototype); + return (jsProto as any).revokeAccessToken.call(this, userId); + } + + override async signUp(options?: Record): Promise { + const configData: any = await this.getStorageManager().getConfigData(); + const signUpUrl: string | undefined = configData?.signUpUrl as string | undefined; + const baseUrl: string | undefined = configData?.baseUrl as string | undefined; + const applicationId: string | undefined = configData?.applicationId as string | undefined; + + if (signUpUrl) { + window.location.href = signUpUrl; + return; + } + + if (baseUrl) { + let url: string = `${baseUrl}/accountrecoveryendpoint/register.do`; + if (applicationId) { + url += `?spId=${applicationId}`; + } + window.location.href = url; + return; + } + + throw new IAMError({ + code: ErrorCode.INVALID_CONFIGURATION, + message: 'Cannot sign up: no signUpUrl or baseUrl configured.', + }); + } + + override async signInSilently(_options?: Record): Promise { + if (!this._clientInitialized) { + throw new IAMError({ + code: ErrorCode.SDK_NOT_INITIALIZED, + message: 'SDK is not initialized. Call initialize() first.', + }); + } + + try { + const nonce: string = crypto.randomUUID(); + const authUrl: string = await this.getSignInUrl({...(_options as any), nonce}); + const url: URL = new URL(authUrl); + url.searchParams.set('prompt', 'none'); + const stateParam: string | null = url.searchParams.get('state'); + const origin: string = window.location.origin; + + const iframe: HTMLIFrameElement = document.createElement('iframe'); + iframe.style.display = 'none'; + document.body.appendChild(iframe); + + const code: string | null = await new Promise((resolve) => { + let attempts = 0; + + const interval = setInterval((): void => { + attempts++; + try { + const href: string | undefined = iframe.contentWindow?.location?.href; + if (href && href.startsWith(origin)) { + const parsed: URL = new URL(href); + const c: string | null = parsed.searchParams.get('code'); + if (c) { + clearInterval(interval); + resolve(c); + return; + } + if (parsed.searchParams.get('error')) { + clearInterval(interval); + resolve(null); + return; + } + } + } catch { + // cross-origin — keep polling + } + if (attempts >= 100) { + clearInterval(interval); + resolve(null); + } + }, 100); + }); + + document.body.removeChild(iframe); + + if (code) { + await (this as any).requestAccessToken(code, '', stateParam ?? ''); + const idToken: string = await this.getIdToken(); + if (idToken) { + const payload: Record = await this.decodeJwtToken(idToken); + if (payload['nonce'] !== nonce) { + throw new IAMError({ + code: ErrorCode.AUTHENTICATION_FAILED, + message: 'ID token nonce mismatch in silent sign-in', + }); + } + } + const user: User = await this.getUser(); + emit(SDKEvent.SIGN_IN, {user}); + return user; + } + + return false; + } catch { + return false; + } + } + + async changePassword(_currentPassword?: string, _newPassword?: string): Promise { + const configData: any = await this.getStorageManager().getConfigData(); + const changePasswordUrl: string | undefined = configData?.changePasswordUrl as string | undefined; + const baseUrl: string | undefined = configData?.baseUrl as string | undefined; + + if (changePasswordUrl) { + window.location.href = changePasswordUrl; + return; + } + + if (baseUrl) { + window.location.href = `${baseUrl}/myaccount/security`; + return; + } + + throw new IAMError({ + code: ErrorCode.INVALID_CONFIGURATION, + message: 'Cannot change password: no changePasswordUrl or baseUrl configured.', + }); + } + + async forgotPassword(options?: Record): Promise { + const configData: any = await this.getStorageManager().getConfigData(); + const forgotPasswordUrl: string | undefined = configData?.forgotPasswordUrl as string | undefined; + const baseUrl: string | undefined = configData?.baseUrl as string | undefined; + const applicationId: string | undefined = configData?.applicationId as string | undefined; + + const effectiveUrl: string | undefined = forgotPasswordUrl ?? (options?.['forgotPasswordUrl'] as string | undefined); + + if (effectiveUrl) { + window.location.href = effectiveUrl; + return; + } + + if (baseUrl) { + let url: string = `${baseUrl}/accountrecoveryendpoint/recoverpassword.do`; + if (applicationId) { + url += `?spId=${applicationId}`; + } + window.location.href = url; + return; + } + + throw new IAMError({ + code: ErrorCode.INVALID_CONFIGURATION, + message: 'Cannot initiate password recovery: no forgotPasswordUrl or baseUrl configured.', + }); + } + + async forgotUsername(options?: Record): Promise { + const configData: any = await this.getStorageManager().getConfigData(); + const forgotUsernameUrl: string | undefined = configData?.forgotUsernameUrl as string | undefined; + const baseUrl: string | undefined = configData?.baseUrl as string | undefined; + const applicationId: string | undefined = configData?.applicationId as string | undefined; + + const effectiveUrl: string | undefined = forgotUsernameUrl ?? (options?.['forgotUsernameUrl'] as string | undefined); + + if (effectiveUrl) { + window.location.href = effectiveUrl; + return; + } + + if (baseUrl) { + let url: string = `${baseUrl}/accountrecoveryendpoint/recoverusername.do`; + if (applicationId) { + url += `?spId=${applicationId}`; + } + window.location.href = url; + return; + } + + throw new IAMError({ + code: ErrorCode.INVALID_CONFIGURATION, + message: 'Cannot initiate username recovery: no forgotUsernameUrl or baseUrl configured.', + }); + } + + override async decodeJwtToken>(token: string): Promise { + return super.decodeJwtToken(token); + } + + override async setSession(sessionData: Record, sessionId?: string): Promise { + return super.setSession(sessionData, sessionId); + } + + override clearSession(sessionId?: string): void { + super.clearSession(sessionId); + } + + override async updateUserProfile(payload: any, userId?: string): Promise { + const accessToken: string = await this.getAccessToken(userId); + const configData: any = await this.getStorageManager().getConfigData(); + const baseUrl: string = (configData?.baseUrl ?? '') as string; + + const updateConfig: UpdateMeProfileConfig = { + url: `${baseUrl}/scim2/Me`, + payload, + headers: {Authorization: `Bearer ${accessToken}`}, + }; + + return updateMeProfile(updateConfig) as Promise as Promise; + } + + async verifyIdToken( + idToken: string, + options?: { + clientId?: string; + issuer?: string; + clockTolerance?: number; + validateIssuer?: boolean; + nonce?: string; + }, + ): Promise { + const configData: any = await this.getStorageManager().getConfigData(); + return verifyIdToken(idToken, configData as ThunderIDSvelteKitConfig, options); + } + + public async getAuthorizeRequestUrl(customParams: Record, userId?: string): Promise { + return this.getSignInUrl(customParams, userId); + } + + async createOrganization( + payload: {description: string; name: string; orgHandle?: string; parentId: string; type: 'TENANT'}, + userId?: string, + ): Promise<{id: string; name: string; orgHandle: string; ref?: string; status?: string}> { + const configData: any = await this.getStorageManager().getConfigData(); + const baseUrl: string = (configData?.baseUrl ?? '') as string; + const accessToken: string = await this.getAccessToken(userId); + const adapter: HTTPAdapter = this.httpAdapter ?? new DefaultHTTPAdapter(); + const res: HTTPResponse = await adapter.request('POST', `${baseUrl}/api/server/v1/organizations`, { + Authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, payload); + if (res.statusCode !== 201) { + throw new IAMError({ + code: ErrorCode.SERVER_ERROR, + message: `Failed to create organization: ${res.statusCode}`, + requestId: res.headers['x-request-id'] || res.headers['correlation-id'] || undefined, + statusCode: res.statusCode, + }); + } + return JSON.parse(res.body); + } + + async getOrganization( + organizationId: string, + userId?: string, + ): Promise<{ + attributes?: Record; created?: string; description?: string; id: string; + lastModified?: string; name: string; orgHandle: string; parent?: {id: string; ref: string}; + permissions?: string[]; status?: string; type?: string; + }> { + const configData: any = await this.getStorageManager().getConfigData(); + const baseUrl: string = (configData?.baseUrl ?? '') as string; + const accessToken: string = await this.getAccessToken(userId); + const adapter: HTTPAdapter = this.httpAdapter ?? new DefaultHTTPAdapter(); + const res: HTTPResponse = await adapter.request('GET', `${baseUrl}/api/server/v1/organizations/${organizationId}`, { + Authorization: `Bearer ${accessToken}`, + }); + if (res.statusCode !== 200) { + throw new IAMError({ + code: ErrorCode.SERVER_ERROR, + message: `Failed to fetch organization ${organizationId}: ${res.statusCode}`, + requestId: res.headers['x-request-id'] || res.headers['correlation-id'] || undefined, + statusCode: res.statusCode, + }); + } + return JSON.parse(res.body); + } + + async getUserInfo(sessionId?: string): Promise { + const accessToken: string = await this.getAccessToken(sessionId); + const configData: any = await this.getStorageManager().getConfigData(); + const baseUrl: string = (configData?.baseUrl ?? '') as string; + const adapter: HTTPAdapter = this.httpAdapter ?? new DefaultHTTPAdapter(); + const res: HTTPResponse = await adapter.request('GET', `${baseUrl}/oauth2/userinfo`, { + Authorization: `Bearer ${accessToken}`, + }); + if (res.statusCode !== 200) { + throw new IAMError({ + code: ErrorCode.AUTHENTICATION_FAILED, + message: `Failed to fetch user info: ${res.statusCode}`, + requestId: res.headers['x-request-id'] || res.headers['correlation-id'] || undefined, + statusCode: res.statusCode, + }); + } + return JSON.parse(res.body) as User; + } + + override async getUser(sessionId?: string): Promise { + // Bypass BrowserClient._validateMethod() which calls isSignedIn() without userId + const sessionData: any = await (this as any).storageManager.getSessionData(sessionId); + const user: any = (this as any).authHelper.getAuthenticatedUserInfo(sessionData?.id_token); + Object.keys(user).forEach((key: string) => { + if (user[key] === undefined || user[key] === '' || user[key] === null) { + delete user[key]; + } + }); + return user; + } + + override getAccessToken(sessionId?: string): Promise { + return super.getAccessToken(sessionId); + } + + override async getIdToken(sessionId?: string): Promise { + // Bypass BrowserClient._validateMethod() which calls isSignedIn() without userId + const sessionData = await (this as any).storageManager.getSessionData(sessionId); + return sessionData?.id_token; + } + + override async getDecodedIdToken(sessionId?: string, idToken?: string): Promise { + // Bypass BrowserClient._validateMethod() which calls isSignedIn() without userId + const sessionData = await (this as any).storageManager.getSessionData(sessionId); + const storedIdToken = sessionData?.id_token; + return (this as any).cryptoHelper.decodeJwtToken(storedIdToken ?? idToken) as Promise; + } + + override isSignedIn(sessionId?: string): Promise { + return super.isSignedIn(sessionId); + } + + override async exchangeToken(config: SPATokenExchangeConfig): Promise { + if (!config.id) { + return Promise.reject( + new Error('The custom grant request id not found. Set the `id` attribute on the token exchange config.'), + ); + } + + const response = await (this as any)._authHelper!.exchangeToken(config, (cfg: any) => + (this as any)._enableRetrievingSignOutURLFromSession(cfg), + ); + + const cb = (this as any)._onCustomGrant.get(config.id); + cb?.(response); + + return response; + } + + override async getUserProfile(sessionId?: string): Promise { + const user: User = await this.getUser(sessionId); + return {flattenedProfile: user, profile: user, schemas: []}; + } + + async exchangeCodeForTokens( + authorizationCode: string, + sessionState: string, + state: string, + userId: string, + ): Promise { + if (!this._clientInitialized) { + throw new IAMError({ + code: ErrorCode.SDK_NOT_INITIALIZED, + message: 'SDK is not initialized. Call initialize() first.', + }); + } + + return (this as any).requestAccessToken(authorizationCode, sessionState, state, userId); + } + + async getBrandingPreference(config: GetBrandingPreferenceConfig): Promise { + return getBrandingPreference(config); + } + + public override getStorageManager(): StorageManager> { + return super.getStorageManager() as StorageManager>; + } +} + +export default ThunderIDSvelteClient; diff --git a/packages/sveltekit/src/__tests__/ThunderIDSvelteClient.test.ts b/packages/sveltekit/src/__tests__/ThunderIDSvelteClient.test.ts new file mode 100644 index 0000000..bbf4e3b --- /dev/null +++ b/packages/sveltekit/src/__tests__/ThunderIDSvelteClient.test.ts @@ -0,0 +1,193 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {describe, it, expect, vi, beforeEach} from 'vitest'; + +vi.mock('@thunderid/browser', () => { + const getBrandingPreference = vi.fn().mockResolvedValue(null); + const updateMeProfile = vi.fn().mockResolvedValue({}); + + class MockBrowserClient { + private _initialized = false; + private _config: any = null; + private _storage: any = null; + + async initialize(config: any, storage?: any): Promise { + this._config = config; + this._storage = storage; + this._initialized = true; + return true; + } + + async reInitialize(_config: any): Promise { + return true; + } + + getStorageManager(): any { + return { + setSessionData: vi.fn().mockResolvedValue(undefined), + getSessionData: vi.fn().mockResolvedValue(null), + getConfigData: vi.fn().mockResolvedValue({baseUrl: 'https://example.com', clientId: 'cid'}), + }; + } + + isSignedIn(_sessionId?: string): Promise { + return Promise.resolve(true); + } + + getUser(_sessionId?: string): Promise { + return Promise.resolve({id: 'u1', name: 'Test User'}); + } + + getAccessToken(_sessionId?: string): Promise { + return Promise.resolve('mock-access-token'); + } + + getDecodedIdToken(_sessionId?: string, _idToken?: string): Promise { + return Promise.resolve({sub: 'user-001'}); + } + + getUserProfile(_sessionId: string): Promise { + return Promise.resolve({flattenedProfile: {id: 'u1'}, profile: {id: 'u1'}, schemas: []}); + } + + getSignInUrl(_customParams?: any, _userId?: string): Promise { + return Promise.resolve('https://idp.example.com/authorize'); + } + + exchangeToken(_config: any, _sessionId?: string): Promise { + return Promise.resolve({accessToken: 'new-at'}); + } + + signOut(_sessionId?: string): Promise { + return Promise.resolve('/'); + } + + revokeAccessToken(_userId?: string): Promise { + return Promise.resolve(true); + } + + getInstanceId(): number { + return 0; + } + + clearSession(_sessionId?: string): void { + // no-op + } + } + + return { + ThunderIDBrowserClient: MockBrowserClient, + getBrandingPreference, + updateMeProfile, + } as any; +}); + +const {default: ThunderIDSvelteClient} = await import('../ThunderIDSvelteClient'); + +describe('ThunderIDSvelteClient', () => { + beforeEach(() => { + (ThunderIDSvelteClient as any).instance = undefined; + }); + + describe('singleton', () => { + it('should return the same instance on getInstance()', () => { + const a = ThunderIDSvelteClient.getInstance(); + const b = ThunderIDSvelteClient.getInstance(); + expect(a).toBe(b); + }); + + it('should create new instances via constructor', () => { + const a = ThunderIDSvelteClient.getInstance(); + const b = new (ThunderIDSvelteClient as any)(); + expect(a).not.toBe(b); + }); + }); + + describe('reset', () => { + it('should reset initialized state', async () => { + const client = ThunderIDSvelteClient.getInstance(); + await client.initialize({baseUrl: 'https://example.com', clientId: 'cid'}); + expect(await client.isInitialized()).toBe(true); + await client.reset(); + expect(await client.isInitialized()).toBe(false); + }); + + it('should allow re-initialization after reset', async () => { + const client = ThunderIDSvelteClient.getInstance(); + await client.initialize({baseUrl: 'https://example.com', clientId: 'cid'}); + expect(await client.isInitialized()).toBe(true); + await client.reset(); + expect(await client.isInitialized()).toBe(false); + const r2 = await client.initialize({baseUrl: 'https://example.com', clientId: 'cid'}); + expect(r2).toBe(true); + expect(await client.isInitialized()).toBe(true); + }); + + it('should not throw when resetting uninitialized client', async () => { + const client = ThunderIDSvelteClient.getInstance(); + await expect(client.reset()).resolves.toBeUndefined(); + }); + }); + + describe('app-native signIn skeleton', () => { + it('should throw NOT_IMPLEMENTED for app-native payload with _subject', async () => { + const client = ThunderIDSvelteClient.getInstance(); + await client.initialize({baseUrl: 'https://example.com', clientId: 'cid'}); + const payload = {_subject: 'user', stepType: 'authenticate', authType: 'basic'}; + await expect(client.signIn(payload)).rejects.toThrow('not yet implemented'); + }); + + it('should throw NOT_IMPLEMENTED for app-native payload with stepType', async () => { + const client = ThunderIDSvelteClient.getInstance(); + await client.initialize({baseUrl: 'https://example.com', clientId: 'cid'}); + const payload = {stepType: 'authenticate', authType: 'basic', properties: {}}; + await expect(client.signIn(payload)).rejects.toThrow('not yet implemented'); + }); + }); + + describe('initialize', () => { + it('should initialize successfully with valid config', async () => { + const client = ThunderIDSvelteClient.getInstance(); + const r1 = await client.initialize({baseUrl: 'https://example.com', clientId: 'cid'}); + expect(r1).toBe(true); + expect(await client.isInitialized()).toBe(true); + }); + + it('should throw AlreadyInitialized on second call', async () => { + const client = ThunderIDSvelteClient.getInstance(); + await client.initialize({baseUrl: 'https://example.com', clientId: 'cid'}); + await expect(client.initialize({baseUrl: 'https://example.com', clientId: 'cid'})).rejects.toThrow(); + }); + + it('should throw InvalidConfiguration when baseUrl is missing', async () => { + const client = ThunderIDSvelteClient.getInstance(); + await expect(client.initialize({} as any)).rejects.toThrow(); + }); + + it('should throw InvalidConfiguration when baseUrl uses HTTP', async () => { + const client = ThunderIDSvelteClient.getInstance(); + await expect(client.initialize({baseUrl: 'http://example.com', clientId: 'cid'})).rejects.toThrow(); + }); + + it('should throw InvalidConfiguration when clientId is missing', async () => { + const client = ThunderIDSvelteClient.getInstance(); + await expect(client.initialize({baseUrl: 'https://example.com'} as any)).rejects.toThrow(); + }); + }); +}); diff --git a/packages/sveltekit/src/__tests__/sanitizer.test.ts b/packages/sveltekit/src/__tests__/sanitizer.test.ts new file mode 100644 index 0000000..1262331 --- /dev/null +++ b/packages/sveltekit/src/__tests__/sanitizer.test.ts @@ -0,0 +1,106 @@ +import {describe, it, expect} from 'vitest'; +import {sanitizeForLog, sanitizeTokenForLog} from '../logger/sanitizer'; + +describe('sanitizeForLog', () => { + it('should mask email addresses', () => { + expect(sanitizeForLog('user@example.com')).toBe('u***@*******.com'); + }); + + it('should mask phone numbers', () => { + const result = sanitizeForLog('+1 (555) 123-4567'); + expect(result).not.toContain('5551234567'); + expect(result).toContain('4567'); + }); + + it('should mask password in JSON format', () => { + const result = sanitizeForLog('{"password":"mySecret123"}'); + expect(result).toBe('{"password":"***"}'); + }); + + it('should mask newPassword in JSON format', () => { + const result = sanitizeForLog('{"newPassword":"newSecret456"}'); + expect(result).toBe('{"newPassword":"***"}'); + }); + + it('should mask currentPassword in JSON format', () => { + const result = sanitizeForLog('{"currentPassword":"oldPass789"}'); + expect(result).toBe('{"currentPassword":"***"}'); + }); + + it('should mask secret in JSON format', () => { + const result = sanitizeForLog('{"secret":"very-secret-value"}'); + expect(result).toBe('{"secret":"***"}'); + }); + + it('should mask otp in JSON format', () => { + const result = sanitizeForLog('{"otp":"123456"}'); + expect(result).toBe('{"otp":"***"}'); + }); + + it('should mask pin in JSON format', () => { + const result = sanitizeForLog('{"pin":"7890"}'); + expect(result).toBe('{"pin":"***"}'); + }); + + it('should mask verificationCode in JSON format', () => { + const result = sanitizeForLog('{"verificationCode":"abc123"}'); + expect(result).toBe('{"verificationCode":"***"}'); + }); + + it('should mask mfaToken in JSON format', () => { + const result = sanitizeForLog('{"mfaToken":"some-token"}'); + expect(result).toBe('{"mfaToken":"***"}'); + }); + + it('should mask password in query string format', () => { + const result = sanitizeForLog('password=mySecret123&grant_type=password'); + expect(result).toBe('password=***&grant_type=password'); + }); + + it('should mask otp in query string format', () => { + const result = sanitizeForLog('otp=123456&user=test'); + expect(result).toBe('otp=***&user=test'); + }); + + it('should mask code in query string format', () => { + const result = sanitizeForLog('code=abc123&state=xyz'); + expect(result).toBe('code=***&state=xyz'); + }); + + it('should mask secret in query string format', () => { + const result = sanitizeForLog('client_secret=supersecret&client_id=myapp'); + expect(result).toBe('client_secret=***&client_id=myapp'); + }); + + it('should mask multiple sensitive fields', () => { + const result = sanitizeForLog('{"password":"pass","otp":"123456","email":"test@example.com"}'); + expect(result).toContain('"password":"***"'); + expect(result).toContain('"otp":"***"'); + expect(result).toContain('"email"'); + expect(result).toMatch(/"password":"\*\*\*"/); + expect(result).not.toMatch(/"password":"pass"/); + expect(result).not.toContain('123456'); + }); + + it('should not modify safe strings', () => { + const safe = '{"name":"John","role":"admin"}'; + expect(sanitizeForLog(safe)).toBe(safe); + }); + + it('should not modify safe strings containing JWT-like patterns (inline tokens not masked by sanitizeForLog)', () => { + const msg = 'Processing token for user 123'; + expect(sanitizeForLog(msg)).toBe(msg); + }); +}); + +describe('sanitizeTokenForLog', () => { + it('should mask JWT token preserving header and first 8 chars of payload', () => { + const token = 'eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8'; + const result = sanitizeTokenForLog(token); + expect(result).toBe('eyJhbGciOiJIUzI1NiJ9.eyJzdWIi...'); + }); + + it('should return masked string for non-JWT input', () => { + expect(sanitizeTokenForLog('not-a-token')).toBe(''); + }); +}); diff --git a/packages/sveltekit/src/adapters/HTTPAdapter.ts b/packages/sveltekit/src/adapters/HTTPAdapter.ts new file mode 100644 index 0000000..3bfb8d3 --- /dev/null +++ b/packages/sveltekit/src/adapters/HTTPAdapter.ts @@ -0,0 +1,43 @@ +export interface HTTPResponse { + statusCode: number; + headers: Record; + body: string; +} + +export interface HTTPAdapter { + request(method: string, url: string, headers?: Record, body?: unknown): Promise; +} + +export class DefaultHTTPAdapter implements HTTPAdapter { + async request( + method: string, + url: string, + headers?: Record, + body?: unknown, + ): Promise { + const init: RequestInit = { + method, + headers: { + 'Content-Type': 'application/json', + ...headers, + }, + }; + + if (body !== undefined) { + init.body = typeof body === 'string' ? body : JSON.stringify(body); + } + + const response: Response = await fetch(url, init); + + const responseHeaders: Record = {}; + response.headers.forEach((value: string, key: string) => { + responseHeaders[key] = value; + }); + + return { + body: await response.text(), + headers: responseHeaders, + statusCode: response.status, + }; + } +} diff --git a/packages/svelte/src/server/getClient.ts b/packages/sveltekit/src/adapters/StorageAdapter.ts similarity index 50% rename from packages/svelte/src/server/getClient.ts rename to packages/sveltekit/src/adapters/StorageAdapter.ts index 42ee5f0..9053211 100644 --- a/packages/svelte/src/server/getClient.ts +++ b/packages/sveltekit/src/adapters/StorageAdapter.ts @@ -16,22 +16,29 @@ * under the License. */ -import type {ThunderIDSvelteConfig} from '../models/config'; -import ThunderIDSvelteClient from '../ThunderIDSvelteClient'; +export interface StorageAdapter { + getData(key: string): Promise; + setData(key: string, value: string): Promise; + removeData(key: string): Promise; + clear(): Promise; +} -let clientInitialized = false; +export class DefaultStorage implements StorageAdapter { + private store: Map = new Map(); -export async function getClient(config: ThunderIDSvelteConfig): Promise { - const client: ThunderIDSvelteClient = ThunderIDSvelteClient.getInstance(); + async getData(key: string): Promise { + return this.store.get(key) ?? null; + } - if (!clientInitialized) { - await client.initialize(config); - clientInitialized = true; + async setData(key: string, value: string): Promise { + this.store.set(key, value); } - return client; -} + async removeData(key: string): Promise { + this.store.delete(key); + } -export function resetClient(): void { - clientInitialized = false; + async clear(): Promise { + this.store.clear(); + } } diff --git a/packages/svelte/src/server/routes/index.ts b/packages/sveltekit/src/adapters/index.ts similarity index 75% rename from packages/svelte/src/server/routes/index.ts rename to packages/sveltekit/src/adapters/index.ts index 2d0dece..90a03b1 100644 --- a/packages/svelte/src/server/routes/index.ts +++ b/packages/sveltekit/src/adapters/index.ts @@ -16,7 +16,7 @@ * under the License. */ -export {createSignInHandler} from './signin'; -export {createCallbackHandler} from './callback'; -export {createSignOutHandler} from './signout'; -export {createOrgSwitchHandler} from './orgSwitch'; +export type {StorageAdapter} from './StorageAdapter'; +export {DefaultStorage} from './StorageAdapter'; +export type {HTTPAdapter, HTTPResponse} from './HTTPAdapter'; +export {DefaultHTTPAdapter} from './HTTPAdapter'; diff --git a/packages/sveltekit/src/api/getSchemas.ts b/packages/sveltekit/src/api/getSchemas.ts new file mode 100644 index 0000000..1728e76 --- /dev/null +++ b/packages/sveltekit/src/api/getSchemas.ts @@ -0,0 +1,30 @@ +import { + type Schema, + getSchemas as baseGetSchemas, + type GetSchemasConfig as BaseGetSchemasConfig, +} from '@thunderid/node'; + +export interface GetSchemasConfig extends Omit { + fetcher?: (url: string, config: RequestInit) => Promise; +} + +const getSchemas = async ({fetcher, ...requestConfig}: GetSchemasConfig): Promise => { + const defaultFetcher = async (url: string, config: RequestInit): Promise => { + const response: globalThis.Response = await fetch(url, config); + const data: unknown = await response.json(); + return { + json: () => Promise.resolve(data), + ok: response.ok, + status: response.status, + statusText: response.statusText, + text: () => Promise.resolve(typeof data === 'string' ? data : JSON.stringify(data)), + } as Response; + }; + + return baseGetSchemas({ + ...requestConfig, + fetcher: fetcher || defaultFetcher, + }); +}; + +export default getSchemas; diff --git a/packages/sveltekit/src/api/getScim2Me.ts b/packages/sveltekit/src/api/getScim2Me.ts new file mode 100644 index 0000000..7359624 --- /dev/null +++ b/packages/sveltekit/src/api/getScim2Me.ts @@ -0,0 +1,30 @@ +import { + type User, + getScim2Me as baseGetScim2Me, + type GetScim2MeConfig as BaseGetScim2MeConfig, +} from '@thunderid/node'; + +export interface GetScim2MeConfig extends Omit { + fetcher?: (url: string, config: RequestInit) => Promise; +} + +const getScim2Me = async ({fetcher, ...requestConfig}: GetScim2MeConfig): Promise => { + const defaultFetcher = async (url: string, config: RequestInit): Promise => { + const response: globalThis.Response = await fetch(url, config); + const data: unknown = await response.json(); + return { + json: () => Promise.resolve(data), + ok: response.ok, + status: response.status, + statusText: response.statusText, + text: () => Promise.resolve(typeof data === 'string' ? data : JSON.stringify(data)), + } as Response; + }; + + return baseGetScim2Me({ + ...requestConfig, + fetcher: fetcher || defaultFetcher, + }); +}; + +export default getScim2Me; diff --git a/packages/sveltekit/src/api/index.ts b/packages/sveltekit/src/api/index.ts new file mode 100644 index 0000000..ba2fe36 --- /dev/null +++ b/packages/sveltekit/src/api/index.ts @@ -0,0 +1,2 @@ +export {default as getSchemas} from './getSchemas'; +export {default as getScim2Me} from './getScim2Me'; diff --git a/packages/sveltekit/src/components/actions/BaseSignInButton.svelte b/packages/sveltekit/src/components/actions/BaseSignInButton.svelte new file mode 100644 index 0000000..fb4c655 --- /dev/null +++ b/packages/sveltekit/src/components/actions/BaseSignInButton.svelte @@ -0,0 +1,46 @@ + + +{@render children({loading, handleClick})} diff --git a/packages/sveltekit/src/components/actions/BaseSignOutButton.svelte b/packages/sveltekit/src/components/actions/BaseSignOutButton.svelte new file mode 100644 index 0000000..97372b1 --- /dev/null +++ b/packages/sveltekit/src/components/actions/BaseSignOutButton.svelte @@ -0,0 +1,45 @@ + + +{@render children({loading, handleClick})} diff --git a/packages/sveltekit/src/components/actions/BaseSignUpButton.svelte b/packages/sveltekit/src/components/actions/BaseSignUpButton.svelte new file mode 100644 index 0000000..be895d4 --- /dev/null +++ b/packages/sveltekit/src/components/actions/BaseSignUpButton.svelte @@ -0,0 +1,46 @@ + + +{@render children({loading, handleClick})} diff --git a/packages/svelte/src/components/actions/SignInButton.svelte b/packages/sveltekit/src/components/actions/SignInButton.svelte similarity index 70% rename from packages/svelte/src/components/actions/SignInButton.svelte rename to packages/sveltekit/src/components/actions/SignInButton.svelte index 6e65c7e..fd9e139 100644 --- a/packages/svelte/src/components/actions/SignInButton.svelte +++ b/packages/sveltekit/src/components/actions/SignInButton.svelte @@ -17,7 +17,7 @@ * under the License. */ - import {ThunderIDRuntimeError} from '@thunderid/browser'; + import {IAMError, ErrorCode} from '../../errors/IAMError'; import {useThunderID} from '../../composables/useThunderID'; interface Props { @@ -27,31 +27,29 @@ let {children, signInOptions = undefined}: Props = $props(); - const {signIn, signInUrl} = useThunderID(); + const tid = useThunderID(); let loading = $state(false); async function handleClick(): Promise { try { loading = true; - await signIn(signInOptions); + await tid.signIn(signInOptions); } catch (error) { - throw new ThunderIDRuntimeError( - `Sign in failed: ${error instanceof Error ? error.message : String(error)}`, - 'SignInButton-handleSignIn-RuntimeError-001', - 'svelte', - 'Something went wrong while trying to sign in. Please try again later.', - ); + throw new IAMError({ + code: ErrorCode.UNKNOWN_ERROR, + message: `Sign in failed: ${error instanceof Error ? error.message : String(error)}`, + }); } finally { loading = false; } } - diff --git a/packages/svelte/src/components/actions/SignOutButton.svelte b/packages/sveltekit/src/components/actions/SignOutButton.svelte similarity index 70% rename from packages/svelte/src/components/actions/SignOutButton.svelte rename to packages/sveltekit/src/components/actions/SignOutButton.svelte index 97dc91a..eb8c5c2 100644 --- a/packages/svelte/src/components/actions/SignOutButton.svelte +++ b/packages/sveltekit/src/components/actions/SignOutButton.svelte @@ -17,7 +17,7 @@ * under the License. */ - import {ThunderIDRuntimeError} from '@thunderid/browser'; + import {IAMError, ErrorCode} from '../../errors/IAMError'; import {useThunderID} from '../../composables/useThunderID'; interface Props { @@ -26,31 +26,29 @@ let {children}: Props = $props(); - const {signOut} = useThunderID(); + const tid = useThunderID(); let loading = $state(false); async function handleClick(): Promise { try { loading = true; - await signOut(); + await tid.signOut(); } catch (error) { - throw new ThunderIDRuntimeError( - `Sign out failed: ${error instanceof Error ? error.message : String(error)}`, - 'SignOutButton-handleSignOut-RuntimeError-001', - 'svelte', - 'Something went wrong while trying to sign out. Please try again later.', - ); + throw new IAMError({ + code: ErrorCode.UNKNOWN_ERROR, + message: `Sign out failed: ${error instanceof Error ? error.message : String(error)}`, + }); } finally { loading = false; } } - diff --git a/packages/svelte/src/components/actions/SignUpButton.svelte b/packages/sveltekit/src/components/actions/SignUpButton.svelte similarity index 70% rename from packages/svelte/src/components/actions/SignUpButton.svelte rename to packages/sveltekit/src/components/actions/SignUpButton.svelte index 0f17713..1be2b5c 100644 --- a/packages/svelte/src/components/actions/SignUpButton.svelte +++ b/packages/sveltekit/src/components/actions/SignUpButton.svelte @@ -17,7 +17,7 @@ * under the License. */ - import {ThunderIDRuntimeError} from '@thunderid/browser'; + import {IAMError, ErrorCode} from '../../errors/IAMError'; import {useThunderID} from '../../composables/useThunderID'; interface Props { @@ -26,31 +26,29 @@ let {children}: Props = $props(); - const {signUp} = useThunderID(); + const tid = useThunderID(); let loading = $state(false); async function handleClick(): Promise { try { loading = true; - await signUp(); + await tid.signUp(); } catch (error) { - throw new ThunderIDRuntimeError( - `Sign up failed: ${error instanceof Error ? error.message : String(error)}`, - 'SignUpButton-handleSignUp-RuntimeError-001', - 'svelte', - 'Something went wrong while trying to sign up. Please try again later.', - ); + throw new IAMError({ + code: ErrorCode.UNKNOWN_ERROR, + message: `Sign up failed: ${error instanceof Error ? error.message : String(error)}`, + }); } finally { loading = false; } } - diff --git a/packages/svelte/src/components/auth/Callback.svelte b/packages/sveltekit/src/components/auth/Callback.svelte similarity index 61% rename from packages/svelte/src/components/auth/Callback.svelte rename to packages/sveltekit/src/components/auth/Callback.svelte index 383153c..ecd93a5 100644 --- a/packages/svelte/src/components/auth/Callback.svelte +++ b/packages/sveltekit/src/components/auth/Callback.svelte @@ -1,6 +1,8 @@ {#if status === 'loading'} - {#if loadingComponent} - {@render loadingComponent()} - {:else if children} - {@render children()} - {:else} -
Signing you in...
- {/if} +
+ {#if loadingComponent} + {@render loadingComponent()} + {:else if children} + {@render children()} + {:else} + {tid.t('callback.signingIn')} + {/if} +
{:else if status === 'error'} - {#if errorComponent} - {@render errorComponent({error: error ?? ''})} - {:else} -
Sign-in failed: {error}
- {/if} +
+ {#if errorComponent} + {@render errorComponent({error: error ?? ''})} + {:else} + {tid.t('callback.signInFailed', {error: error ?? ''})} + {/if} +
{:else if status === 'success'} - {#if children} - {@render children()} - {:else} -
Signed in successfully! Redirecting...
- {/if} +
+ {#if children} + {@render children()} + {:else} + {tid.t('callback.signedIn')} + {/if} +
{/if} diff --git a/packages/svelte/src/components/control/Loading.svelte b/packages/sveltekit/src/components/control/Loading.svelte similarity index 100% rename from packages/svelte/src/components/control/Loading.svelte rename to packages/sveltekit/src/components/control/Loading.svelte diff --git a/packages/svelte/src/components/control/SignedIn.svelte b/packages/sveltekit/src/components/control/SignedIn.svelte similarity index 100% rename from packages/svelte/src/components/control/SignedIn.svelte rename to packages/sveltekit/src/components/control/SignedIn.svelte diff --git a/packages/svelte/src/components/control/SignedOut.svelte b/packages/sveltekit/src/components/control/SignedOut.svelte similarity index 100% rename from packages/svelte/src/components/control/SignedOut.svelte rename to packages/sveltekit/src/components/control/SignedOut.svelte diff --git a/packages/sveltekit/src/components/presentation/AcceptInvite.svelte b/packages/sveltekit/src/components/presentation/AcceptInvite.svelte new file mode 100644 index 0000000..9f5952d --- /dev/null +++ b/packages/sveltekit/src/components/presentation/AcceptInvite.svelte @@ -0,0 +1,31 @@ + + + + {#snippet children({acceptInvite, loading, error})} + {#if customChildren} + {@render customChildren({acceptInvite, loading, error})} + {:else} +
+

{tid.t('acceptInvite.title')}

+ {#if error} + + {/if} + +
+ {/if} + {/snippet} +
diff --git a/packages/sveltekit/src/components/presentation/BaseAcceptInvite.svelte b/packages/sveltekit/src/components/presentation/BaseAcceptInvite.svelte new file mode 100644 index 0000000..c0c1a83 --- /dev/null +++ b/packages/sveltekit/src/components/presentation/BaseAcceptInvite.svelte @@ -0,0 +1,25 @@ + + +{@render children({acceptInvite, loading, error})} diff --git a/packages/sveltekit/src/components/presentation/BaseInviteUser.svelte b/packages/sveltekit/src/components/presentation/BaseInviteUser.svelte new file mode 100644 index 0000000..d4bf5de --- /dev/null +++ b/packages/sveltekit/src/components/presentation/BaseInviteUser.svelte @@ -0,0 +1,24 @@ + + +{@render children({inviteUser, loading, error})} diff --git a/packages/sveltekit/src/components/presentation/BaseLanguageSwitcher.svelte b/packages/sveltekit/src/components/presentation/BaseLanguageSwitcher.svelte new file mode 100644 index 0000000..2a9cfba --- /dev/null +++ b/packages/sveltekit/src/components/presentation/BaseLanguageSwitcher.svelte @@ -0,0 +1,16 @@ + + +{@render children({currentLanguage, switchLanguage, languages})} diff --git a/packages/sveltekit/src/components/presentation/BaseSignIn.svelte b/packages/sveltekit/src/components/presentation/BaseSignIn.svelte new file mode 100644 index 0000000..7b15524 --- /dev/null +++ b/packages/sveltekit/src/components/presentation/BaseSignIn.svelte @@ -0,0 +1,29 @@ + + +{@render children({signIn: handleSignIn, loading, error})} diff --git a/packages/sveltekit/src/components/presentation/BaseSignUp.svelte b/packages/sveltekit/src/components/presentation/BaseSignUp.svelte new file mode 100644 index 0000000..7abf5de --- /dev/null +++ b/packages/sveltekit/src/components/presentation/BaseSignUp.svelte @@ -0,0 +1,29 @@ + + +{@render children({signUp: handleSignUp, loading, error})} diff --git a/packages/sveltekit/src/components/presentation/BaseUser.svelte b/packages/sveltekit/src/components/presentation/BaseUser.svelte new file mode 100644 index 0000000..a18bed2 --- /dev/null +++ b/packages/sveltekit/src/components/presentation/BaseUser.svelte @@ -0,0 +1,16 @@ + + +{@render children({user: userData, isSignedIn: signedIn})} diff --git a/packages/sveltekit/src/components/presentation/BaseUserDropdown.svelte b/packages/sveltekit/src/components/presentation/BaseUserDropdown.svelte new file mode 100644 index 0000000..878d5ad --- /dev/null +++ b/packages/sveltekit/src/components/presentation/BaseUserDropdown.svelte @@ -0,0 +1,56 @@ + + +{@render children({user: userData, isSignedIn: tid.isSignedIn, open, toggle, close, triggerRef, menuRef, triggerId, menuId, signOut: tid.signOut, handleTriggerKeydown, handleMenuKeydown})} diff --git a/packages/sveltekit/src/components/presentation/BaseUserProfile.svelte b/packages/sveltekit/src/components/presentation/BaseUserProfile.svelte new file mode 100644 index 0000000..0ea1cf7 --- /dev/null +++ b/packages/sveltekit/src/components/presentation/BaseUserProfile.svelte @@ -0,0 +1,13 @@ + + +{@render children({profile: tid.userProfile, user: tid.user})} diff --git a/packages/sveltekit/src/components/presentation/InviteUser.svelte b/packages/sveltekit/src/components/presentation/InviteUser.svelte new file mode 100644 index 0000000..0240694 --- /dev/null +++ b/packages/sveltekit/src/components/presentation/InviteUser.svelte @@ -0,0 +1,43 @@ + + + + {#snippet children({inviteUser, loading, error})} + {#if customChildren} + {@render customChildren({inviteUser, loading, error})} + {:else} +
+

{tid.t('inviteUser.title')}

+ {#if error} + + {/if} + + +
+ {/if} + {/snippet} +
diff --git a/packages/sveltekit/src/components/presentation/LanguageSwitcher.svelte b/packages/sveltekit/src/components/presentation/LanguageSwitcher.svelte new file mode 100644 index 0000000..8ebdb23 --- /dev/null +++ b/packages/sveltekit/src/components/presentation/LanguageSwitcher.svelte @@ -0,0 +1,27 @@ + + + + {#snippet children({currentLanguage, switchLanguage, languages})} + {#if customChildren} + {@render customChildren({currentLanguage, switchLanguage, languages})} + {:else} + + {/if} + {/snippet} + diff --git a/packages/sveltekit/src/components/presentation/SignIn.svelte b/packages/sveltekit/src/components/presentation/SignIn.svelte new file mode 100644 index 0000000..8c5bcf9 --- /dev/null +++ b/packages/sveltekit/src/components/presentation/SignIn.svelte @@ -0,0 +1,59 @@ + + +{#if children} + {@render children({signIn: handleSignIn, loading})} +{:else} + +{/if} diff --git a/packages/sveltekit/src/components/presentation/SignUp.svelte b/packages/sveltekit/src/components/presentation/SignUp.svelte new file mode 100644 index 0000000..b8df963 --- /dev/null +++ b/packages/sveltekit/src/components/presentation/SignUp.svelte @@ -0,0 +1,59 @@ + + +{#if children} + {@render children({signUp: handleSignUp, loading})} +{:else} + +{/if} diff --git a/packages/sveltekit/src/components/presentation/User.svelte b/packages/sveltekit/src/components/presentation/User.svelte new file mode 100644 index 0000000..cffb361 --- /dev/null +++ b/packages/sveltekit/src/components/presentation/User.svelte @@ -0,0 +1,42 @@ + + +{#if children} + {@render children({user: userData, isSignedIn: signedIn})} +{:else if signedIn && userData} +
+ {userData['displayName'] ?? userData['userName'] ?? tid.t('user.displayName')} +
+{:else} +
{tid.t('user.notSignedIn')}
+{/if} diff --git a/packages/sveltekit/src/components/presentation/UserDropdown.svelte b/packages/sveltekit/src/components/presentation/UserDropdown.svelte new file mode 100644 index 0000000..09b9c6c --- /dev/null +++ b/packages/sveltekit/src/components/presentation/UserDropdown.svelte @@ -0,0 +1,52 @@ + + + + {#snippet children({user, isSignedIn, open, toggle, close, triggerRef, menuRef, triggerId, menuId, signOut, handleTriggerKeydown, handleMenuKeydown})} + {#if customChildren} + {@render customChildren({user, isSignedIn, open, toggle, close, triggerRef, menuRef, triggerId, menuId, signOut, handleTriggerKeydown, handleMenuKeydown})} + {:else if isSignedIn} +
+ + {#if open} + + {/if} +
+ {/if} + {/snippet} +
diff --git a/packages/sveltekit/src/components/presentation/UserProfile.svelte b/packages/sveltekit/src/components/presentation/UserProfile.svelte new file mode 100644 index 0000000..00d058d --- /dev/null +++ b/packages/sveltekit/src/components/presentation/UserProfile.svelte @@ -0,0 +1,44 @@ + + +{#if children} + {@render children({profile: tid.userProfile, user: tid.user})} +{:else} + +{/if} diff --git a/packages/svelte/src/composables/useThunderID.ts b/packages/sveltekit/src/composables/useThunderID.ts similarity index 91% rename from packages/svelte/src/composables/useThunderID.ts rename to packages/sveltekit/src/composables/useThunderID.ts index bf1fdf1..343f956 100644 --- a/packages/svelte/src/composables/useThunderID.ts +++ b/packages/sveltekit/src/composables/useThunderID.ts @@ -43,14 +43,12 @@ export function useThunderID(): ThunderIDContext { get userProfile() { return authState.userProfile; }, - get organization() { - return authState.organization; - }, - get myOrganizations() { - return authState.myOrganizations; - }, + get resolvedBaseUrl() { return authState.resolvedBaseUrl; }, + get locale() { + return authState.locale; + }, }; } diff --git a/packages/svelte/src/composables/useUser.ts b/packages/sveltekit/src/composables/useUser.ts similarity index 100% rename from packages/svelte/src/composables/useUser.ts rename to packages/sveltekit/src/composables/useUser.ts diff --git a/packages/svelte/src/context.ts b/packages/sveltekit/src/context.ts similarity index 100% rename from packages/svelte/src/context.ts rename to packages/sveltekit/src/context.ts diff --git a/packages/svelte/src/errors/IAMError.ts b/packages/sveltekit/src/errors/IAMError.ts similarity index 100% rename from packages/svelte/src/errors/IAMError.ts rename to packages/sveltekit/src/errors/IAMError.ts diff --git a/packages/svelte/src/errors/index.ts b/packages/sveltekit/src/errors/index.ts similarity index 100% rename from packages/svelte/src/errors/index.ts rename to packages/sveltekit/src/errors/index.ts diff --git a/packages/sveltekit/src/events/EventBus.ts b/packages/sveltekit/src/events/EventBus.ts new file mode 100644 index 0000000..f77e7ee --- /dev/null +++ b/packages/sveltekit/src/events/EventBus.ts @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {getLogger} from '../logger/LoggerAdapter'; + +export enum SDKEvent { + SIGN_IN = 'signIn', + SIGN_IN_FAILED = 'signInFailed', + SIGN_OUT = 'signOut', + SIGN_UP = 'signUp', + SESSION_EXPIRED = 'sessionExpired', + TOKEN_REFRESHED = 'tokenRefreshed', + TOKEN_REFRESH_FAILED = 'tokenRefreshFailed', + MFA_STEP_REQUIRED = 'mfaStepRequired', + + INITIALIZED = 'initialized', + ERROR = 'error', +} + +export type SDKEventListener = (data?: unknown) => void; + +type EventHandlers = Map>; + +let _handlers: EventHandlers = new Map(); + +export function on(event: SDKEvent | string, listener: SDKEventListener): void { + const key: string = typeof event === 'string' ? event : event; + if (!_handlers.has(key)) { + _handlers.set(key, new Set()); + } + _handlers.get(key)!.add(listener); +} + +export function off(event: SDKEvent | string, listener: SDKEventListener): void { + const key: string = typeof event === 'string' ? event : event; + _handlers.get(key)?.delete(listener); +} + +export function emit(event: SDKEvent | string, data?: unknown): void { + const key: string = typeof event === 'string' ? event : event; + const logger = getLogger(); + _handlers.get(key)?.forEach((listener: SDKEventListener) => { + try { + listener(data); + } catch (error: unknown) { + logger.warn(`[EventBus] Listener error for event "${key}": ${(error as any)?.message ?? String(error)}`); + } + }); +} + +export function clearListeners(event?: SDKEvent | string): void { + if (event) { + _handlers.delete(typeof event === 'string' ? event : event); + } else { + _handlers.clear(); + } +} diff --git a/packages/svelte/prettier.config.js b/packages/sveltekit/src/events/index.ts similarity index 84% rename from packages/svelte/prettier.config.js rename to packages/sveltekit/src/events/index.ts index 7e978e9..05e30a2 100644 --- a/packages/svelte/prettier.config.js +++ b/packages/sveltekit/src/events/index.ts @@ -16,6 +16,5 @@ * under the License. */ -import config from '@thunderid/prettier-config'; - -export default config; +export {SDKEvent, on, off, emit, clearListeners} from './EventBus'; +export type {SDKEventListener} from './EventBus'; diff --git a/packages/sveltekit/src/i18n/index.ts b/packages/sveltekit/src/i18n/index.ts new file mode 100644 index 0000000..c67b9ea --- /dev/null +++ b/packages/sveltekit/src/i18n/index.ts @@ -0,0 +1,2 @@ +export {createTranslator, DEFAULT_BUNDLES} from './translations'; +export type {TranslationBundle, TranslationBundles} from './translations'; diff --git a/packages/sveltekit/src/i18n/translations.ts b/packages/sveltekit/src/i18n/translations.ts new file mode 100644 index 0000000..7e58c0a --- /dev/null +++ b/packages/sveltekit/src/i18n/translations.ts @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import type {I18nPreferences} from '../models/config'; + +export type TranslationBundle = Record; + +export interface TranslationBundles { + [locale: string]: TranslationBundle; +} + +export const DEFAULT_BUNDLES: TranslationBundles = { + en: { + 'signIn.button': 'Sign In', + 'signIn.loading': 'Signing in...', + 'signIn.title': 'Sign In', + 'signIn.error': 'Sign in failed: {{error}}', + 'signOut.button': 'Sign Out', + 'signOut.loading': 'Signing out...', + 'signUp.button': 'Sign Up', + 'signUp.loading': 'Redirecting...', + 'signUp.title': 'Create Account', + 'signUp.error': 'Sign up failed: {{error}}', + 'user.notSignedIn': 'Not signed in', + 'user.displayName': 'User', + 'userProfile.empty': 'No user profile available.', + 'callback.signingIn': 'Signing you in...', + 'callback.signedIn': 'Signed in successfully! Redirecting...', + 'callback.signInFailed': 'Sign-in failed: {{error}}', + 'callback.noCode': 'No authorization code received.', + 'acceptInvite.title': 'Accept Invitation', + 'acceptInvite.button': 'Accept Invitation', + 'acceptInvite.loading': 'Accepting...', + 'inviteUser.title': 'Invite User', + 'inviteUser.button': 'Send Invitation', + 'inviteUser.loading': 'Sending...', + 'inviteUser.emailPlaceholder': 'email@example.com', + 'userDropdown.signOut': 'Sign Out', + }, +}; + +export function createTranslator(preferences?: I18nPreferences): (key: string, params?: Record) => string { + const lang = preferences?.language ?? 'en'; + const fallback = preferences?.fallbackLanguage ?? 'en'; + const userBundles = preferences?.bundles ?? {}; + const mergedBundles: TranslationBundles = {}; + + for (const locale of new Set([...Object.keys(DEFAULT_BUNDLES), ...Object.keys(userBundles)])) { + mergedBundles[locale] = {...DEFAULT_BUNDLES[locale], ...userBundles[locale]}; + } + + return (key: string, params?: Record): string => { + let text = mergedBundles[lang]?.[key] ?? mergedBundles[fallback]?.[key] ?? key; + if (params) { + for (const [k, v] of Object.entries(params)) { + text = text.replace(`{{${k}}}`, String(v)); + } + } + return text; + }; +} diff --git a/packages/sveltekit/src/index.ts b/packages/sveltekit/src/index.ts new file mode 100644 index 0000000..5bb06eb --- /dev/null +++ b/packages/sveltekit/src/index.ts @@ -0,0 +1,114 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +// ── Components ── +export {default as ThunderID} from './ThunderID.svelte'; +export {default as SignedIn} from './components/control/SignedIn.svelte'; +export {default as SignedOut} from './components/control/SignedOut.svelte'; +export {default as Loading} from './components/control/Loading.svelte'; +export {default as SignInButton} from './components/actions/SignInButton.svelte'; +export {default as SignOutButton} from './components/actions/SignOutButton.svelte'; +export {default as SignUpButton} from './components/actions/SignUpButton.svelte'; +export {default as Callback} from './components/auth/Callback.svelte'; +export {default as BaseSignInButton} from './components/actions/BaseSignInButton.svelte'; +export {default as BaseSignOutButton} from './components/actions/BaseSignOutButton.svelte'; +export {default as BaseSignUpButton} from './components/actions/BaseSignUpButton.svelte'; + +// ── Presentation Components ── +export {default as UserProfile} from './components/presentation/UserProfile.svelte'; +export {default as BaseUserProfile} from './components/presentation/BaseUserProfile.svelte'; +export {default as User} from './components/presentation/User.svelte'; +export {default as BaseUser} from './components/presentation/BaseUser.svelte'; +export {default as SignIn} from './components/presentation/SignIn.svelte'; +export {default as BaseSignIn} from './components/presentation/BaseSignIn.svelte'; +export {default as SignUp} from './components/presentation/SignUp.svelte'; +export {default as BaseSignUp} from './components/presentation/BaseSignUp.svelte'; +export {default as AcceptInvite} from './components/presentation/AcceptInvite.svelte'; +export {default as BaseAcceptInvite} from './components/presentation/BaseAcceptInvite.svelte'; +export {default as InviteUser} from './components/presentation/InviteUser.svelte'; +export {default as BaseInviteUser} from './components/presentation/BaseInviteUser.svelte'; +export {default as UserDropdown} from './components/presentation/UserDropdown.svelte'; +export {default as BaseUserDropdown} from './components/presentation/BaseUserDropdown.svelte'; +export {default as LanguageSwitcher} from './components/presentation/LanguageSwitcher.svelte'; +export {default as BaseLanguageSwitcher} from './components/presentation/BaseLanguageSwitcher.svelte'; + +// ── Client ── +export {default as ThunderIDSvelteClient} from './ThunderIDSvelteClient'; + +// ── Context ── +export {THUNDERID_KEY, USER_KEY, setThunderIDContext, setUserContext} from './context'; + +// ── Composables ── +export {useThunderID} from './composables/useThunderID'; +export {useUser} from './composables/useUser'; + +// ── State ── +export {authState} from './state.svelte'; + +// ── Models / Types ── +export type {ThunderIDSvelteKitConfig, ThunderIDSveltePreferences, ThemePreferences, I18nPreferences} from './models/config'; +export type {ThunderIDSSRData, ThunderIDSessionPayload} from './models/session'; +export type {BrandingPreference} from '@thunderid/browser'; +export type {ThunderIDContext, UserContextValue, NavigateFn} from './models/contexts'; + +// ── Errors ── +export {IAMError, ErrorCode} from './errors/IAMError'; + +// ── Logger ── +export {DefaultLogger, setLogger, getLogger} from './logger/LoggerAdapter'; +export type {LoggerAdapter} from './logger/LoggerAdapter'; +export {sanitizeForLog, sanitizeTokenForLog} from './logger/sanitizer'; + +// ── Adapters ── +export type {StorageAdapter} from './adapters/StorageAdapter'; +export {DefaultStorage} from './adapters/StorageAdapter'; +export type {HTTPAdapter, HTTPResponse} from './adapters/HTTPAdapter'; +export {DefaultHTTPAdapter} from './adapters/HTTPAdapter'; + +// ── Events ── +export {SDKEvent, on, off, emit, clearListeners} from './events/EventBus'; +export type {SDKEventListener} from './events/EventBus'; + +// ── Validation ── +export {verifyIdToken, validateIdTokenClaims, clearJWKSCache} from './validation/IdTokenValidator'; +export type {IdTokenValidationResult} from './validation/IdTokenValidator'; + +// ── i18n ── +export {createTranslator, DEFAULT_BUNDLES} from './i18n/translations'; +export type {TranslationBundle, TranslationBundles} from './i18n/translations'; + +// ── Server ── +export {createThunderIDHandle, loadThunderID} from './server/hooks'; +export {resolveConfig} from './server/config'; +export {requireServerSession, isGuardRedirect, GuardRedirect} from './server/guard'; +export {getClient, resetClient} from './server/getClient'; +export {getValidAccessToken, maybeRefreshToken} from './server/refresh'; +export {createSignInHandler} from './server/routes/signin'; +export {createCallbackHandler} from './server/routes/callback'; +export {createSignOutHandler} from './server/routes/signout'; +export { + createSessionToken, + createTempSessionToken, + verifySessionToken, + verifyTempSessionToken, + getSessionCookieName, + getTempSessionCookieName, + getSessionCookieOptions, + getTempSessionCookieOptions, + issueSessionCookie, +} from './server/session'; diff --git a/packages/svelte/src/logger/LoggerAdapter.ts b/packages/sveltekit/src/logger/LoggerAdapter.ts similarity index 100% rename from packages/svelte/src/logger/LoggerAdapter.ts rename to packages/sveltekit/src/logger/LoggerAdapter.ts diff --git a/packages/svelte/src/logger/index.ts b/packages/sveltekit/src/logger/index.ts similarity index 100% rename from packages/svelte/src/logger/index.ts rename to packages/sveltekit/src/logger/index.ts diff --git a/packages/svelte/src/logger/sanitizer.ts b/packages/sveltekit/src/logger/sanitizer.ts similarity index 81% rename from packages/svelte/src/logger/sanitizer.ts rename to packages/sveltekit/src/logger/sanitizer.ts index 7a53a90..b308b4c 100644 --- a/packages/svelte/src/logger/sanitizer.ts +++ b/packages/sveltekit/src/logger/sanitizer.ts @@ -19,6 +19,8 @@ const TOKEN_PATTERN = /^[A-Za-z0-9\-_.]+\.[A-Za-z0-9\-_.]+\.[A-Za-z0-9\-_.]+$/; const EMAIL_PATTERN = /([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g; const PHONE_PATTERN = /(\+?\d[\d\s\-().]{6,}\d)/g; +const SENSITIVE_JSON_FIELD_PATTERN = /"(password|newPassword|currentPassword|secret|otp|pin|verificationCode|mfaToken)"\s*:\s*"[^"]+"/gi; +const SENSITIVE_PARAM_PATTERN = /(password|secret|otp|pin|code)=[^&\s]+/gi; export function sanitizeForLog(input: string): string { let result: string = input; @@ -40,6 +42,14 @@ export function sanitizeForLog(input: string): string { return prefix + '*'.repeat(digits.length - 4 - (prefix ? 1 : 0)) + last4; }); + result = result.replace(SENSITIVE_JSON_FIELD_PATTERN, (_match: string, key: string) => { + return `"${key}":"***"`; + }); + + result = result.replace(SENSITIVE_PARAM_PATTERN, (match: string, key: string) => { + return `${key}=***`; + }); + return result; } diff --git a/packages/sveltekit/src/models/config.ts b/packages/sveltekit/src/models/config.ts new file mode 100644 index 0000000..d6d9d80 --- /dev/null +++ b/packages/sveltekit/src/models/config.ts @@ -0,0 +1,111 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import type {OAuthResponseMode} from '@thunderid/browser'; +import type {SessionCookieConfig} from '@thunderid/node'; +import type {HTTPAdapter} from '../adapters/HTTPAdapter'; +export interface ThemePreferences { + mode?: string; + direction?: 'ltr' | 'rtl'; + inheritFromBranding?: boolean; + overrides?: Record; +} + +export interface I18nPreferences { + language?: string; + fallbackLanguage?: string; + bundles?: Record; + storageStrategy?: 'cookie' | 'localStorage' | 'none'; + storageKey?: string; + cookieDomain?: string; + urlParam?: string | false; +} + +export interface ThunderIDSveltePreferences { + theme?: ThemePreferences; + i18n?: I18nPreferences; + resolveFromMeta?: boolean; + user?: { + fetchUserProfile?: boolean; + }; +} + +export interface ThunderIDSvelteKitConfig { + afterSignInUrl?: string; + afterSignOutUrl?: string; + allowedExternalUrls?: string[]; + applicationId?: string; + organizationHandle?: string; + baseUrl?: string; + clientId?: string; + clientSecret?: string; + discovery?: { + wellKnown?: { + enabled?: boolean; + }; + }; + endpoints?: { + authorization?: string; + endSession?: string; + introspection?: string; + jwks?: string; + token?: string; + userInfo?: string; + wellKnown?: string; + }; + instanceId?: number; + mode?: 'redirect' | 'embedded'; + + platform?: string; + preferences?: ThunderIDSveltePreferences; + prompt?: string; + responseMode?: OAuthResponseMode; + scopes?: string | string[]; + sendCookiesInRequests?: boolean; + sendIdTokenInLogoutRequest?: boolean; + sessionCookie?: SessionCookieConfig; + sessionSecret?: string; + signInOptions?: Record; + signInUrl?: string; + signOutOptions?: Record; + signUpOptions?: Record; + signUpUrl?: string; + forgotPasswordUrl?: string; + forgotUsernameUrl?: string; + changePasswordUrl?: string; + /** Custom storage backend. Pass a StorageAdapter implementation. */ + storage?: unknown; + httpAdapter?: HTTPAdapter; + syncSession?: boolean; + tokenLifecycle?: { + refreshToken?: { + autoRefresh?: boolean; + }; + }; + tokenRequest?: { + authMethod?: 'client_secret_basic' | 'client_secret_post' | 'private_key_jwt' | 'none'; + params?: Record; + }; + tokenValidation?: { + idToken?: { + clockTolerance?: number; + validate?: boolean; + validateIssuer?: boolean; + }; + }; +} diff --git a/packages/sveltekit/src/models/contexts.ts b/packages/sveltekit/src/models/contexts.ts new file mode 100644 index 0000000..f4e98ab --- /dev/null +++ b/packages/sveltekit/src/models/contexts.ts @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import type { + BrandingPreference, + GetBrandingPreferenceConfig, + IdToken, + SPATokenExchangeConfig, + TokenResponse, + User, + UserProfile, +} from '@thunderid/browser'; +import type {IdTokenValidationResult} from '../validation/IdTokenValidator'; +import type {ThunderIDSvelteKitConfig} from './config'; + +export type NavigateFn = (url: string) => void; +export type TranslateFn = (key: string, params?: Record) => string; + +export interface ThunderIDContext { + afterSignInUrl?: string; + afterSignOutUrl?: string; + applicationId?: string; + baseUrl?: string; + brandingPreference: BrandingPreference | null; + clientId?: string; + isInitialized: boolean; + isLoading: boolean; + isSignedIn: boolean; + + resolvedBaseUrl: string; + scopes?: string | string[]; + signInUrl?: string; + signUpUrl?: string; + user: User | null; + userProfile: UserProfile | null; + locale: string; + + t: TranslateFn; + + signIn: (options?: Record) => Promise; + signOut: (options?: Record) => Promise; + signUp: (options?: Record) => Promise; + + getAccessToken: (sessionId?: string) => Promise; + getIdToken: (sessionId?: string) => Promise; + getDecodedIdToken: (sessionId?: string, idToken?: string) => Promise; + + getUser: (sessionId?: string) => Promise; + getUserProfile: (sessionId?: string) => Promise; + updateUserProfile: (payload: Record, userId?: string) => Promise; + getUserInfo: (sessionId?: string) => Promise; + verifyIdToken: ( + idToken: string, + options?: { + clientId?: string; + issuer?: string; + clockTolerance?: number; + validateIssuer?: boolean; + nonce?: string; + }, + ) => Promise; + getBrandingPreference: (config: GetBrandingPreferenceConfig) => Promise; + revokeAccessToken: (userId?: string) => Promise; + + exchangeToken: (config: SPATokenExchangeConfig) => Promise; + signInSilently: (options?: Record) => Promise; + reInitialize: (config: Partial) => Promise; + reset: () => Promise; + getConfiguration: () => ThunderIDSvelteKitConfig; +} + +export interface UserContextValue { + flattenedProfile: User | null; + profile: UserProfile | null; + schemas: any[] | null; +} diff --git a/packages/svelte/src/models/session.ts b/packages/sveltekit/src/models/session.ts similarity index 88% rename from packages/svelte/src/models/session.ts rename to packages/sveltekit/src/models/session.ts index 7835bbb..770c4e8 100644 --- a/packages/svelte/src/models/session.ts +++ b/packages/sveltekit/src/models/session.ts @@ -16,7 +16,7 @@ * under the License. */ -import type {BrandingPreference, Organization, User, UserProfile} from '@thunderid/node'; +import type {BrandingPreference, User, UserProfile} from '@thunderid/browser'; import type {JWTPayload} from 'jose'; /** @@ -28,7 +28,7 @@ export interface ThunderIDSessionPayload extends JWTPayload { exp: number; iat: number; idToken?: string; - organizationId?: string; + refreshToken?: string; scopes: string; sessionId: string; @@ -42,8 +42,6 @@ export interface ThunderIDSessionPayload extends JWTPayload { export interface ThunderIDSSRData { brandingPreference: BrandingPreference | null; isSignedIn: boolean; - myOrganizations: Organization[]; - organization: Organization | null; resolvedBaseUrl: string | null; session: ThunderIDSessionPayload | null; user: User | null; diff --git a/packages/sveltekit/src/server/__tests__/guard.test.ts b/packages/sveltekit/src/server/__tests__/guard.test.ts new file mode 100644 index 0000000..fd79673 --- /dev/null +++ b/packages/sveltekit/src/server/__tests__/guard.test.ts @@ -0,0 +1,139 @@ +import type {RequestEvent} from '@sveltejs/kit'; +import {describe, it, expect} from 'vitest'; +import type {ThunderIDSSRData} from '../../models/session'; + +const {requireServerSession, GuardRedirect, isGuardRedirect} = await import('../guard'); + +function createMockEvent(ssrData: ThunderIDSSRData | undefined, path = '/'): RequestEvent { + return { + locals: {thunderid: ssrData}, + url: {pathname: path, search: ''}, + } as unknown as RequestEvent; +} + +describe('requireServerSession', () => { + it('should return SSR data when signed in', () => { + const ssrData: ThunderIDSSRData = { + brandingPreference: null, + isSignedIn: true, + resolvedBaseUrl: null, + session: null, + user: {id: 'u1'} as any, + userProfile: null, + }; + + const result = requireServerSession(createMockEvent(ssrData)); + expect(result).toBe(ssrData); + }); + + it('should throw GuardRedirect when not signed in', () => { + const ssrData: ThunderIDSSRData = { + brandingPreference: null, + isSignedIn: false, + resolvedBaseUrl: null, + session: null, + user: null, + userProfile: null, + }; + + expect(() => requireServerSession(createMockEvent(ssrData))).toThrow(GuardRedirect); + }); + + it('should append returnTo with custom redirectTo path', () => { + const ssrData: ThunderIDSSRData = { + brandingPreference: null, + isSignedIn: false, + resolvedBaseUrl: null, + session: null, + user: null, + userProfile: null, + }; + + let err: unknown; + try { + requireServerSession(createMockEvent(ssrData, '/protected'), '/custom/signin'); + } catch (e) { + err = e; + } + expect(isGuardRedirect(err)).toBe(true); + if (isGuardRedirect(err)) { + expect(err.location).toBe('/custom/signin?returnTo=%2Fprotected'); + expect(err.status).toBe(307); + } + }); + + it('should append returnTo with default sign-in path', () => { + const ssrData: ThunderIDSSRData = { + brandingPreference: null, + isSignedIn: false, + resolvedBaseUrl: null, + session: null, + user: null, + userProfile: null, + }; + + let err: unknown; + try { + requireServerSession(createMockEvent(ssrData, '/dashboard')); + } catch (e) { + err = e; + } + expect(isGuardRedirect(err)).toBe(true); + if (isGuardRedirect(err)) { + expect(err.location).toBe('/api/auth/signin?returnTo=%2Fdashboard'); + expect(err.status).toBe(307); + } + }); + + it('should include query string in returnTo', () => { + const ssrData: ThunderIDSSRData = { + brandingPreference: null, + isSignedIn: false, + resolvedBaseUrl: null, + session: null, + user: null, + userProfile: null, + }; + + const event = { + locals: {thunderid: ssrData}, + url: {pathname: '/search', search: '?q=test'}, + } as unknown as RequestEvent; + + let err: unknown; + try { + requireServerSession(event); + } catch (e) { + err = e; + } + expect(isGuardRedirect(err)).toBe(true); + if (isGuardRedirect(err)) { + expect(err.location).toBe('/api/auth/signin?returnTo=%2Fsearch%3Fq%3Dtest'); + expect(err.status).toBe(307); + } + }); + + it('should throw GuardRedirect when not signed in (custom path)', () => { + const ssrData: ThunderIDSSRData = { + brandingPreference: null, + isSignedIn: false, + resolvedBaseUrl: null, + session: null, + user: null, + userProfile: null, + }; + + let err: unknown; + try { + requireServerSession(createMockEvent(ssrData), '/custom/signin'); + } catch (e) { + err = e; + } + expect(isGuardRedirect(err)).toBe(true); + }); + + it('should throw IAMError when SSR data is undefined', () => { + const event = {locals: {}} as RequestEvent; + expect(() => requireServerSession(event)).toThrow('ThunderID SSR data not found'); + }); +}); diff --git a/packages/svelte/src/server/__tests__/session.test.ts b/packages/sveltekit/src/server/__tests__/session.test.ts similarity index 85% rename from packages/svelte/src/server/__tests__/session.test.ts rename to packages/sveltekit/src/server/__tests__/session.test.ts index f1766af..2ce2f37 100644 --- a/packages/svelte/src/server/__tests__/session.test.ts +++ b/packages/sveltekit/src/server/__tests__/session.test.ts @@ -1,21 +1,3 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - import {describe, it, expect, beforeAll} from 'vitest'; import { createSessionToken, @@ -113,11 +95,24 @@ describe('Session token utilities', () => { expect(payload.returnTo).toBe('/dashboard'); }); - it('should reject an expired temp token', async () => { - // Can't easily test without overriding Date, but at least verify type checks - const token = await createTempSessionToken('sess-temp-003'); + it('should store and return nonce when provided', async () => { + const nonce: string = crypto.randomUUID(); + const token = await createTempSessionToken('sess-temp-003', undefined, undefined, nonce); const payload = await verifyTempSessionToken(token); expect(payload.sessionId).toBe('sess-temp-003'); + expect(payload.nonce).toBe(nonce); + }); + + it('should omit nonce from payload when not provided', async () => { + const token = await createTempSessionToken('sess-temp-004'); + const payload = await verifyTempSessionToken(token); + expect(payload.nonce).toBeUndefined(); + }); + + it('should reject an expired temp token', async () => { + const token = await createTempSessionToken('sess-temp-005'); + const payload = await verifyTempSessionToken(token); + expect(payload.sessionId).toBe('sess-temp-005'); }); }); diff --git a/packages/sveltekit/src/server/app.d.ts b/packages/sveltekit/src/server/app.d.ts new file mode 100644 index 0000000..00e4cb2 --- /dev/null +++ b/packages/sveltekit/src/server/app.d.ts @@ -0,0 +1,9 @@ +import type {ThunderIDSSRData} from '../models/session'; + +declare global { + namespace App { + interface Locals { + thunderid: ThunderIDSSRData; + } + } +} diff --git a/packages/svelte/src/server/config.ts b/packages/sveltekit/src/server/config.ts similarity index 50% rename from packages/svelte/src/server/config.ts rename to packages/sveltekit/src/server/config.ts index ec7eea4..742e729 100644 --- a/packages/svelte/src/server/config.ts +++ b/packages/sveltekit/src/server/config.ts @@ -1,39 +1,46 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - import {IAMError, ErrorCode} from '../errors/IAMError'; -import type {ThunderIDSvelteConfig} from '../models/config'; +import type {ThunderIDSvelteKitConfig} from '../models/config'; + +export type {ThunderIDSvelteKitConfig} from '../models/config'; + +export function resolveConfig(config?: ThunderIDSvelteKitConfig): ThunderIDSvelteKitConfig { + const baseUrl: string = config?.baseUrl || process.env['THUNDERID_BASE_URL'] || ''; -export function resolveConfig(config?: ThunderIDSvelteConfig): ThunderIDSvelteConfig { - const resolved: ThunderIDSvelteConfig = { + const resolved: ThunderIDSvelteKitConfig = { afterSignInUrl: config?.afterSignInUrl ?? '/', afterSignOutUrl: config?.afterSignOutUrl ?? '/', - baseUrl: config?.baseUrl || process.env['THUNDERID_BASE_URL'], + allowedExternalUrls: config?.allowedExternalUrls, + applicationId: config?.applicationId, + baseUrl, clientId: config?.clientId || process.env['THUNDERID_CLIENT_ID'], clientSecret: config?.clientSecret || process.env['THUNDERID_CLIENT_SECRET'], - enablePKCE: true, - scopes: config?.scopes ?? ['openid', 'profile'], + discovery: config?.discovery, + endpoints: { + wellKnown: `${baseUrl}/.well-known/openid-configuration`, + ...config?.endpoints, + } as any, + instanceId: config?.instanceId, + mode: config?.mode, + + preferences: config?.preferences, + prompt: config?.prompt, + responseMode: config?.responseMode, + scopes: config?.scopes ?? ['openid'], + sendCookiesInRequests: config?.sendCookiesInRequests, + sendIdTokenInLogoutRequest: config?.sendIdTokenInLogoutRequest, + sessionCookie: config?.sessionCookie, sessionSecret: config?.sessionSecret || process.env['THUNDERID_SESSION_SECRET'], - tokenRequest: config?.tokenRequest ?? {authMethod: 'client_secret_post'}, + signInOptions: config?.signInOptions, signInUrl: config?.signInUrl, + signOutOptions: config?.signOutOptions, + signUpOptions: config?.signUpOptions, signUpUrl: config?.signUpUrl, - applicationId: config?.applicationId, - preferences: config?.preferences, + storage: config?.storage, + httpAdapter: config?.httpAdapter, + syncSession: config?.syncSession, + tokenLifecycle: config?.tokenLifecycle, + tokenRequest: config?.tokenRequest ?? {authMethod: 'client_secret_post'}, + tokenValidation: config?.tokenValidation, }; if (!resolved.baseUrl) { diff --git a/packages/sveltekit/src/server/getClient.ts b/packages/sveltekit/src/server/getClient.ts new file mode 100644 index 0000000..f4a2962 --- /dev/null +++ b/packages/sveltekit/src/server/getClient.ts @@ -0,0 +1,19 @@ +import {ThunderIDJavaScriptClient} from '@thunderid/node'; +import type {ThunderIDSvelteKitConfig} from './config'; + +let cachedClient: ThunderIDJavaScriptClient | null = null; + +export async function getClient(config: ThunderIDSvelteKitConfig): Promise { + if (cachedClient) { + return cachedClient; + } + + const client = new ThunderIDJavaScriptClient(); + await client.initialize(config as any); + cachedClient = client; + return cachedClient; +} + +export function resetClient(): void { + cachedClient = null; +} diff --git a/packages/sveltekit/src/server/guard.ts b/packages/sveltekit/src/server/guard.ts new file mode 100644 index 0000000..687faa2 --- /dev/null +++ b/packages/sveltekit/src/server/guard.ts @@ -0,0 +1,44 @@ +import type {RequestEvent} from '@sveltejs/kit'; +import {IAMError, ErrorCode} from '../errors/IAMError'; +import type {ThunderIDSSRData} from '../models/session'; + +export class GuardRedirect extends Error { + status: number; + location: string; + + constructor(status: number, location: string) { + super(); + this.name = 'GuardRedirect'; + this.status = status; + this.location = location; + } +} + +export function isGuardRedirect(err: unknown): err is GuardRedirect { + return err instanceof GuardRedirect; +} + +export function requireServerSession( + event: RequestEvent, + redirectTo?: string, +): ThunderIDSSRData { + const ssrData: ThunderIDSSRData | undefined = event.locals.thunderid; + + if (!ssrData) { + throw new IAMError({ + code: ErrorCode.SDK_NOT_INITIALIZED, + message: + 'ThunderID SSR data not found in event.locals. Ensure createThunderIDHandle() is configured in hooks.server.ts.', + }); + } + + if (!ssrData.isSignedIn) { + const signInUrl: string = redirectTo || '/api/auth/signin'; + const returnTo: string = encodeURIComponent(event.url.pathname + event.url.search); + const separator: string = signInUrl.includes('?') ? '&' : '?'; + + throw new GuardRedirect(307, `${signInUrl}${separator}returnTo=${returnTo}`); + } + + return ssrData; +} diff --git a/packages/sveltekit/src/server/hooks.ts b/packages/sveltekit/src/server/hooks.ts new file mode 100644 index 0000000..725c414 --- /dev/null +++ b/packages/sveltekit/src/server/hooks.ts @@ -0,0 +1,112 @@ +import type {Handle, RequestEvent} from '@sveltejs/kit'; +import type {BrandingPreference} from '@thunderid/node'; +import {getBrandingPreference} from '@thunderid/node'; +import {resolveConfig} from './config'; +import type {ThunderIDSvelteKitConfig} from './config'; +import {maybeRefreshToken} from './refresh'; +import {verifySessionToken, getSessionCookieName} from './session'; +import {getLogger} from '../logger/LoggerAdapter'; +import {DefaultHTTPAdapter, type HTTPAdapter, type HTTPResponse} from '../adapters/HTTPAdapter'; +import type {ThunderIDSSRData, ThunderIDSessionPayload} from '../models/session'; + +export function createThunderIDHandle(config?: ThunderIDSvelteKitConfig): Handle { + const resolvedConfig: ThunderIDSvelteKitConfig = resolveConfig(config); + + return async ({event, resolve}) => { + const sessionCookie: string | undefined = event.cookies.get(getSessionCookieName()); + let session: ThunderIDSessionPayload | null = null; + + if (sessionCookie) { + try { + session = await verifySessionToken(sessionCookie, resolvedConfig.sessionSecret); + + session = await maybeRefreshToken(session, resolvedConfig, event); + + if (session) { + event.locals.thunderid = { + isSignedIn: true, + session, + user: null, + userProfile: null, + brandingPreference: null, + resolvedBaseUrl: resolvedConfig.baseUrl ?? null, + } as ThunderIDSSRData; + } + } catch { + event.cookies.delete(getSessionCookieName(), {path: '/'}); + session = null; + } + } + + const isSignedIn: boolean = session !== null; + + const shouldFetchBranding: boolean = resolvedConfig.preferences?.theme?.inheritFromBranding !== false; + + let ssrData: ThunderIDSSRData; + + if (isSignedIn && session) { + const accessToken: string = session.accessToken; + const baseUrl: string = resolvedConfig.baseUrl!; + + const adapter: HTTPAdapter = resolvedConfig.httpAdapter ?? new DefaultHTTPAdapter(); + + const [userResponse, branding] = await Promise.all([ + adapter.request('GET', `${baseUrl}/oauth2/userinfo`, {Authorization: `Bearer ${accessToken}`}).then((r: HTTPResponse) => { + if (r.statusCode !== 200) { + getLogger().error( + `[hooks] userinfo fetch failed (${r.statusCode})`, + undefined, + {requestId: r.headers['x-request-id'] || r.headers['correlation-id'] || undefined, statusCode: r.statusCode}, + ); + return null; + } + return JSON.parse(r.body) as any; + }), + shouldFetchBranding + ? getBrandingPreference({baseUrl} as any).catch(() => null) + : Promise.resolve(null), + ]); + + const user = userResponse; + const userProfile = user ? {flattenedProfile: user, profile: user, schemas: [] as string[]} : null; + + ssrData = { + brandingPreference: (branding!) ?? null, + isSignedIn: true, + resolvedBaseUrl: resolvedConfig.baseUrl ?? null, + session, + user: user as any, + userProfile: userProfile as any, + }; + } else { + let branding: BrandingPreference | null = null; + + if (shouldFetchBranding) { + try { + branding = await getBrandingPreference({baseUrl: resolvedConfig.baseUrl!} as any); + } catch { + // branding fetch failed — continue without + } + } + + ssrData = { + brandingPreference: branding, + isSignedIn: false, + resolvedBaseUrl: null, + session: null, + user: null, + userProfile: null, + }; + } + + event.locals.thunderid = ssrData; + + return resolve(event); + }; +} + +export function loadThunderID(event: RequestEvent): ThunderIDSSRData { + return event.locals.thunderid; +} + + diff --git a/packages/sveltekit/src/server/index.ts b/packages/sveltekit/src/server/index.ts new file mode 100644 index 0000000..1a5e0b7 --- /dev/null +++ b/packages/sveltekit/src/server/index.ts @@ -0,0 +1,18 @@ +export {resolveConfig} from './config'; +export {createThunderIDHandle} from './hooks'; +export {loadThunderID} from './load'; +export {getClient, resetClient} from './getClient'; +export { + createSessionToken, + createTempSessionToken, + verifySessionToken, + verifyTempSessionToken, + issueSessionCookie, + getSessionCookieName, + getTempSessionCookieName, + getSessionCookieOptions, + getTempSessionCookieOptions, +} from './session'; +export {maybeRefreshToken, getValidAccessToken} from './refresh'; +export {requireServerSession, GuardRedirect, isGuardRedirect} from './guard'; +export {createSignInHandler, createCallbackHandler, createSignOutHandler} from './routes'; diff --git a/packages/sveltekit/src/server/load.ts b/packages/sveltekit/src/server/load.ts new file mode 100644 index 0000000..cbcd4d8 --- /dev/null +++ b/packages/sveltekit/src/server/load.ts @@ -0,0 +1,6 @@ +import type {RequestEvent} from '@sveltejs/kit'; +import type {ThunderIDSSRData} from '../models/session'; + +export function loadThunderID(event: RequestEvent): ThunderIDSSRData { + return event.locals.thunderid; +} diff --git a/packages/sveltekit/src/server/refresh.ts b/packages/sveltekit/src/server/refresh.ts new file mode 100644 index 0000000..5e303f6 --- /dev/null +++ b/packages/sveltekit/src/server/refresh.ts @@ -0,0 +1,150 @@ +import type {RequestEvent} from '@sveltejs/kit'; +import type {ThunderIDSvelteKitConfig} from './config'; +import {createSessionToken, getSessionCookieName, getSessionCookieOptions} from './session'; +import {emit, SDKEvent} from '../events/EventBus'; +import {getLogger} from '../logger/LoggerAdapter'; +import {DefaultHTTPAdapter, type HTTPAdapter, type HTTPResponse} from '../adapters/HTTPAdapter'; +import type {ThunderIDSessionPayload} from '../models/session'; + +const REFRESH_SKEW_SECONDS = 60; + +interface OIDCTokenRefreshResponse { + access_token: string; + expires_in?: number; + id_token?: string; + refresh_token?: string; + scope?: string; + token_type?: string; +} + +const inFlightRefreshes = new Map>(); + +export async function maybeRefreshToken( + session: ThunderIDSessionPayload, + config: ThunderIDSvelteKitConfig, + event: Pick, +): Promise { + const now: number = Math.floor(Date.now() / 1000); + const logger = getLogger(); + + if (!session.accessTokenExpiresAt || session.accessTokenExpiresAt - REFRESH_SKEW_SECONDS > now) { + return session; + } + + if (!session.refreshToken) { + return null; + } + + const dedupKey: string = session.sessionId || session.sub || 'default'; + + const existing = inFlightRefreshes.get(dedupKey); + if (existing) { + return existing; + } + + const promise = doRefresh(session, config, event, logger); + inFlightRefreshes.set(dedupKey, promise); + + try { + return await promise; + } finally { + inFlightRefreshes.delete(dedupKey); + } +} + +async function doRefresh( + session: ThunderIDSessionPayload, + config: ThunderIDSvelteKitConfig, + event: Pick, + logger: ReturnType, +): Promise { + const now: number = Math.floor(Date.now() / 1000); + const tokenEndpoint = `${config.baseUrl}/oauth2/token`; + + const body: string = new URLSearchParams({ + client_id: config.clientId!, + grant_type: 'refresh_token', + refresh_token: session.refreshToken ?? '', + ...(config.clientSecret ? {client_secret: config.clientSecret} : {}), + }).toString(); + + let refreshed: OIDCTokenRefreshResponse; + try { + const adapter: HTTPAdapter = config.httpAdapter ?? new DefaultHTTPAdapter(); + const res: HTTPResponse = await adapter.request('POST', tokenEndpoint, { + 'Content-Type': 'application/x-www-form-urlencoded', + }, body); + + const requestId: string | undefined = res.headers['x-request-id'] || res.headers['correlation-id'] || undefined; + + if (res.statusCode !== 200) { + logger.error(`Token refresh failed (${res.statusCode})`, undefined, { + statusCode: res.statusCode, + requestId, + sessionId: session.sessionId, + }); + emit(SDKEvent.TOKEN_REFRESH_FAILED, { + statusCode: res.statusCode, + requestId, + sessionId: session.sessionId, + }); + return null; + } + + refreshed = JSON.parse(res.body) as OIDCTokenRefreshResponse; + } catch (err: unknown) { + logger.error('Token refresh network error', err instanceof Error ? err : new Error(String(err)), { + sessionId: session.sessionId, + }); + emit(SDKEvent.TOKEN_REFRESH_FAILED, { + error: err instanceof Error ? err.message : String(err), + sessionId: session.sessionId, + }); + return null; + } + + const newSessionToken: string = await createSessionToken( + { + accessToken: refreshed.access_token, + accessTokenExpiresAt: now + (refreshed.expires_in ?? 3600), + idToken: refreshed.id_token ?? session.idToken, + refreshToken: refreshed.refresh_token ?? session.refreshToken, + scopes: refreshed.scope ?? session.scopes, + sessionId: session.sessionId, + userId: session.sub, + }, + config.sessionSecret, + ); + + event.cookies.set(getSessionCookieName(), newSessionToken, getSessionCookieOptions()); + emit(SDKEvent.TOKEN_REFRESHED, {sessionId: session.sessionId}); + + return { + ...session, + accessToken: refreshed.access_token, + accessTokenExpiresAt: now + (refreshed.expires_in ?? 3600), + idToken: refreshed.id_token ?? session.idToken, + refreshToken: refreshed.refresh_token ?? session.refreshToken, + scopes: refreshed.scope ?? session.scopes, + }; +} + +export async function getValidAccessToken( + event: RequestEvent, + config: ThunderIDSvelteKitConfig, +): Promise { + const {loadThunderID} = await import('./load'); + const ssrData = loadThunderID(event); + + if (!ssrData.session) { + throw new Error('Not authenticated'); + } + + const refreshed = await maybeRefreshToken(ssrData.session, config, event); + + if (!refreshed) { + throw new Error('Session expired'); + } + + return refreshed.accessToken; +} diff --git a/packages/sveltekit/src/server/routes/callback.ts b/packages/sveltekit/src/server/routes/callback.ts new file mode 100644 index 0000000..20016b2 --- /dev/null +++ b/packages/sveltekit/src/server/routes/callback.ts @@ -0,0 +1,111 @@ +import type {TokenResponse} from '@thunderid/node'; +import {decodeJwt} from 'jose'; +import {getLogger} from '../../logger/LoggerAdapter'; +import {resolveConfig} from '../config'; +import type {ThunderIDSvelteKitConfig} from '../config'; +import {getClient} from '../getClient'; +import {issueSessionCookie, verifyTempSessionToken, getTempSessionCookieName, getSessionCookieName} from '../session'; + +export function createCallbackHandler(config?: ThunderIDSvelteKitConfig): (event: {url: URL; cookies: any}) => Promise { + const resolvedConfig: ThunderIDSvelteKitConfig = resolveConfig(config); + const logger = getLogger(); + + return async (event) => { + try { + const client = await getClient(resolvedConfig); + + const tempCookie: string | undefined = event.cookies.get(getTempSessionCookieName()); + + if (!tempCookie) { + return new Response(null, {status: 302, headers: {Location: resolvedConfig.afterSignInUrl || '/'}}); + } + + let sessionId: string; + let returnTo: string | undefined; + let storedNonce: string | undefined; + + try { + const tempPayload = await verifyTempSessionToken(tempCookie, resolvedConfig.sessionSecret); + sessionId = tempPayload.sessionId; + returnTo = tempPayload.returnTo; + storedNonce = tempPayload.nonce; + } catch { + event.cookies.delete(getTempSessionCookieName(), {path: '/'}); + return new Response(null, {status: 302, headers: {Location: resolvedConfig.afterSignInUrl || '/'}}); + } + + const code: string | null = event.url.searchParams.get('code'); + const state: string | null = event.url.searchParams.get('state'); + const sessionState: string | null = event.url.searchParams.get('session_state'); + + if (code) { + let tokenResponse: TokenResponse; + + try { + tokenResponse = await (client as any).requestAccessToken( + code, + sessionState ?? '', + state ?? '', + sessionId, + ); + } catch (err: unknown) { + const message: string = (err as any)?.message ?? String(err); + logger.error('callback requestAccessToken failed', err instanceof Error ? err : new Error(message)); + return new Response(JSON.stringify({error: message, details: (err as any)?.name ?? ''}), { + status: 500, + headers: {'content-type': 'application/json'}, + }); + } + + if (tokenResponse.accessToken) { + const nonceFromQuery: string | null = event.url.searchParams.get('nonce'); + const nonceToValidate: string | undefined = nonceFromQuery ?? storedNonce; + + if (nonceToValidate && tokenResponse.idToken) { + try { + const idTokenPayload = decodeJwt(tokenResponse.idToken); + if (idTokenPayload['nonce'] !== nonceToValidate) { + const errMsg = `ID token nonce mismatch: expected ${nonceToValidate}, got ${idTokenPayload['nonce']}`; + logger.error(errMsg); + return new Response(JSON.stringify({error: errMsg}), { + status: 401, + headers: {'content-type': 'application/json'}, + }); + } + } catch (decodeErr: unknown) { + const errMsg = `Failed to decode ID token for nonce validation: ${(decodeErr as any)?.message ?? String(decodeErr)}`; + logger.error(errMsg); + return new Response(JSON.stringify({error: errMsg}), { + status: 401, + headers: {'content-type': 'application/json'}, + }); + } + } + + const sessionPayload = await issueSessionCookie(event, sessionId, tokenResponse, resolvedConfig.sessionSecret); + + event.cookies.delete(getTempSessionCookieName(), {path: '/'}); + + return new Response(null, { + status: 302, + headers: {Location: returnTo || resolvedConfig.afterSignInUrl || '/'}, + }); + } + } + + const error: string | null = event.url.searchParams.get('error'); + if (error) { + return new Response(null, {status: 302, headers: {Location: `${resolvedConfig.afterSignInUrl || '/'}?error=${error}`}}); + } + + return new Response(null, {status: 302, headers: {Location: resolvedConfig.afterSignInUrl || '/'}}); + } catch (err: unknown) { + const message: string = (err as any)?.message ?? String(err); + logger.error('callback unhandled error', err instanceof Error ? err : new Error(message)); + return new Response(JSON.stringify({error: message, details: (err as any)?.name ?? ''}), { + status: 500, + headers: {'content-type': 'application/json'}, + }); + } + }; +} diff --git a/packages/sveltekit/src/server/routes/index.ts b/packages/sveltekit/src/server/routes/index.ts new file mode 100644 index 0000000..0a1c0e3 --- /dev/null +++ b/packages/sveltekit/src/server/routes/index.ts @@ -0,0 +1,3 @@ +export {createSignInHandler} from './signin'; +export {createCallbackHandler} from './callback'; +export {createSignOutHandler} from './signout'; diff --git a/packages/sveltekit/src/server/routes/signin.ts b/packages/sveltekit/src/server/routes/signin.ts new file mode 100644 index 0000000..9fe7dba --- /dev/null +++ b/packages/sveltekit/src/server/routes/signin.ts @@ -0,0 +1,40 @@ +import {generateSessionId} from '@thunderid/node'; +import {getLogger} from '../../logger/LoggerAdapter'; +import {resolveConfig} from '../config'; +import type {ThunderIDSvelteKitConfig} from '../config'; +import {getClient} from '../getClient'; +import {createTempSessionToken, getTempSessionCookieName, getTempSessionCookieOptions} from '../session'; + +export function createSignInHandler(config?: ThunderIDSvelteKitConfig): (event: {url: URL; cookies: any}) => Promise { + const resolvedConfig: ThunderIDSvelteKitConfig = resolveConfig(config); + const logger = getLogger(); + + return async (event) => { + try { + const client = await getClient(resolvedConfig); + const sessionId: string = generateSessionId(); + + const returnTo: string = event.url.searchParams.get('returnTo') || resolvedConfig.afterSignInUrl || '/'; + + const nonce: string = crypto.randomUUID(); + + const authorizeUrl: string = await (client as any).getSignInUrl({nonce}, sessionId); + + const tempToken: string = await createTempSessionToken(sessionId, resolvedConfig.sessionSecret, returnTo, nonce); + + event.cookies.set(getTempSessionCookieName(), tempToken, getTempSessionCookieOptions()); + + return new Response(null, { + status: 302, + headers: {Location: authorizeUrl}, + }); + } catch (err: unknown) { + const message: string = (err as any)?.message ?? String(err); + logger.error('sign-in failed', err instanceof Error ? err : new Error(message)); + return new Response(JSON.stringify({error: message}), { + status: 500, + headers: {'content-type': 'application/json'}, + }); + } + }; +} diff --git a/packages/sveltekit/src/server/routes/signout.ts b/packages/sveltekit/src/server/routes/signout.ts new file mode 100644 index 0000000..1c2bd03 --- /dev/null +++ b/packages/sveltekit/src/server/routes/signout.ts @@ -0,0 +1,34 @@ +import {getLogger} from '../../logger/LoggerAdapter'; +import {resolveConfig} from '../config'; +import type {ThunderIDSvelteKitConfig} from '../config'; +import {getClient} from '../getClient'; +import {getSessionCookieName, verifySessionToken} from '../session'; + +export function createSignOutHandler(config?: ThunderIDSvelteKitConfig): (event: {cookies: any}) => Promise { + const resolvedConfig: ThunderIDSvelteKitConfig = resolveConfig(config); + const logger = getLogger(); + + return async (event) => { + const redirectUrl: string = resolvedConfig.afterSignOutUrl || '/'; + + const sessionCookie: string | undefined = event.cookies.get(getSessionCookieName()); + + if (sessionCookie) { + try { + const client = await getClient(resolvedConfig); + const session = await verifySessionToken(sessionCookie, resolvedConfig.sessionSecret); + await (client as any).revokeAccessToken(session.sessionId); + await client.signOut({sessionId: session.sessionId} as any); + } catch (err: unknown) { + logger.warn('sign-out encountered an issue', {error: (err as any)?.message ?? String(err)}); + } + } + + event.cookies.delete(getSessionCookieName(), {path: '/'}); + + return new Response(null, { + status: 302, + headers: {Location: redirectUrl}, + }); + }; +} diff --git a/packages/svelte/src/server/session.ts b/packages/sveltekit/src/server/session.ts similarity index 80% rename from packages/svelte/src/server/session.ts rename to packages/sveltekit/src/server/session.ts index 23e730a..d594d49 100644 --- a/packages/svelte/src/server/session.ts +++ b/packages/sveltekit/src/server/session.ts @@ -1,24 +1,6 @@ -/** - * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). - * - * WSO2 LLC. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - import {CookieConfig} from '@thunderid/node'; import type {IdToken, TokenResponse} from '@thunderid/node'; -import {SignJWT, jwtVerify} from 'jose'; +import {SignJWT, jwtVerify, decodeJwt} from 'jose'; import {getLogger} from '../logger/LoggerAdapter'; import type {ThunderIDSessionPayload} from '../models/session'; @@ -48,7 +30,6 @@ export async function createSessionToken( accessTokenExpiresAt?: number; expirySeconds?: number; idToken?: string; - organizationId?: string; refreshToken?: string; scopes: string; sessionId: string; @@ -62,7 +43,6 @@ export async function createSessionToken( accessToken: params.accessToken, accessTokenExpiresAt: params.accessTokenExpiresAt, idToken: params.idToken, - organizationId: params.organizationId, refreshToken: params.refreshToken, scopes: params.scopes, sessionId: params.sessionId, @@ -79,6 +59,7 @@ export async function createTempSessionToken( sessionId: string, sessionSecret?: string, returnTo?: string, + nonce?: string, ): Promise { const secret: Uint8Array = getSecret(sessionSecret); @@ -91,6 +72,10 @@ export async function createTempSessionToken( payload['returnTo'] = returnTo; } + if (nonce) { + payload['nonce'] = nonce; + } + return new SignJWT(payload).setProtectedHeader({alg: 'HS256'}).setIssuedAt().setExpirationTime('15m').sign(secret); } @@ -106,7 +91,7 @@ export async function verifySessionToken( export async function verifyTempSessionToken( token: string, sessionSecret?: string, -): Promise<{returnTo?: string; sessionId: string}> { +): Promise<{returnTo?: string; sessionId: string; nonce?: string}> { const secret: Uint8Array = getSecret(sessionSecret); const {payload} = await jwtVerify(token, secret); @@ -117,6 +102,7 @@ export async function verifyTempSessionToken( return { returnTo: payload['returnTo'] as string | undefined, sessionId: payload['sessionId'] as string, + nonce: payload['nonce'] as string | undefined, }; } @@ -166,13 +152,9 @@ export async function issueSessionCookie( tokenResponse: TokenResponse, sessionSecret?: string, ): Promise { - const {default: ThunderIDSvelteClient} = await import('../ThunderIDSvelteClient'); - const client = ThunderIDSvelteClient.getInstance(); - - const idToken: IdToken = await client.getDecodedIdToken(sessionId, tokenResponse.idToken); + const idToken: IdToken = decodeJwt(tokenResponse.idToken) as IdToken; const userId: string = idToken.sub || sessionId; - const organizationId: string | undefined = (idToken['user_org'] || idToken['organization_id']) as string | undefined; const expiresInSeconds: number = parseInt(tokenResponse.expiresIn ?? '3600', 10); const accessTokenExpiresAt: number = Math.floor(Date.now() / 1000) + (Number.isFinite(expiresInSeconds) ? expiresInSeconds : 3600); @@ -182,7 +164,6 @@ export async function issueSessionCookie( accessToken: tokenResponse.accessToken, accessTokenExpiresAt, idToken: tokenResponse.idToken || undefined, - organizationId, refreshToken: tokenResponse.refreshToken || undefined, scopes: tokenResponse.scope || '', sessionId, @@ -199,7 +180,6 @@ export async function issueSessionCookie( exp: Math.floor(Date.now() / 1000) + expiresInSeconds, iat: Math.floor(Date.now() / 1000), idToken: tokenResponse.idToken || undefined, - organizationId, refreshToken: tokenResponse.refreshToken || undefined, scopes: tokenResponse.scope || '', sessionId, diff --git a/packages/svelte/src/state.svelte.ts b/packages/sveltekit/src/state.svelte.ts similarity index 84% rename from packages/svelte/src/state.svelte.ts rename to packages/sveltekit/src/state.svelte.ts index 658e7e4..67f01d7 100644 --- a/packages/svelte/src/state.svelte.ts +++ b/packages/sveltekit/src/state.svelte.ts @@ -16,7 +16,7 @@ * under the License. */ -import type {Organization, User, UserProfile} from '@thunderid/node'; +import type {User, UserProfile} from '@thunderid/browser'; class AuthState { isSignedIn = $state(false); @@ -24,9 +24,9 @@ class AuthState { isInitialized = $state(false); user: User | null = $state(null); userProfile: UserProfile | null = $state(null); - organization: Organization | null = $state(null); - myOrganizations: Organization[] = $state([]); + resolvedBaseUrl = $state(''); + locale = $state('en'); } export const authState = new AuthState(); diff --git a/packages/sveltekit/src/validation/IdTokenValidator.ts b/packages/sveltekit/src/validation/IdTokenValidator.ts new file mode 100644 index 0000000..d026fae --- /dev/null +++ b/packages/sveltekit/src/validation/IdTokenValidator.ts @@ -0,0 +1,141 @@ +/** + * Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import {createRemoteJWKSet, jwtVerify, type JWTPayload} from 'jose'; +import type {JWK} from 'jose'; +import {IAMError, ErrorCode} from '../errors/IAMError'; +import type {ThunderIDSvelteKitConfig} from '../models/config'; + +export interface IdTokenValidationResult { + valid: boolean; + payload: JWTPayload | null; + error?: string; +} + +let _jwksCache: ReturnType | null = null; +let _jwksUrl: string | null = null; + +export async function verifyIdToken( + idToken: string, + config: ThunderIDSvelteKitConfig, + options?: { + clientId?: string; + issuer?: string; + clockTolerance?: number; + validateIssuer?: boolean; + nonce?: string; + }, +): Promise { + try { + const jwksUrl: string | undefined = config.endpoints?.jwks; + + if (!jwksUrl) { + return {valid: false, payload: null, error: 'JWKS endpoint not configured'}; + } + + if (_jwksUrl !== jwksUrl || !_jwksCache) { + _jwksUrl = jwksUrl; + _jwksCache = createRemoteJWKSet(new URL(jwksUrl)); + } + + let payload: JWTPayload; + + try { + const result = await jwtVerify(idToken, _jwksCache, { + issuer: options?.validateIssuer !== false ? options?.issuer : undefined, + audience: options?.clientId, + clockTolerance: options?.clockTolerance, + }); + payload = result.payload; + } catch { + clearJWKSCache(); + _jwksUrl = jwksUrl; + _jwksCache = createRemoteJWKSet(new URL(jwksUrl)); + const result = await jwtVerify(idToken, _jwksCache, { + issuer: options?.validateIssuer !== false ? options?.issuer : undefined, + audience: options?.clientId, + clockTolerance: options?.clockTolerance, + }); + payload = result.payload; + } + + if (options?.nonce !== undefined && payload['nonce'] !== options.nonce) { + return { + valid: false, + payload, + error: `ID token nonce mismatch: expected ${options.nonce}, got ${payload['nonce']}`, + }; + } + + return {valid: true, payload}; + } catch (error: unknown) { + return { + valid: false, + payload: null, + error: (error as any)?.message ?? String(error), + }; + } +} + +export function clearJWKSCache(): void { + _jwksCache = null; + _jwksUrl = null; +} + +/** + * Validates the basic structural claims of an ID token (exp, iat, iss, aud) + * without requiring a JWKS fetch. Useful for quick client-side checks. + */ +export function validateIdTokenClaims( + payload: JWTPayload, + options?: { + clientId?: string; + issuer?: string; + clockTolerance?: number; + nonce?: string; + }, +): IdTokenValidationResult { + const now: number = Math.floor(Date.now() / 1000); + const tolerance: number = options?.clockTolerance ?? 0; + + if (payload.exp && payload.exp + tolerance < now) { + return {valid: false, payload, error: 'ID token has expired'}; + } + + if (payload.nbf && payload.nbf - tolerance > now) { + return {valid: false, payload, error: 'ID token is not yet valid (nbf)'}; + } + + if (options?.issuer && payload.iss !== options.issuer) { + return {valid: false, payload, error: `ID token issuer mismatch: expected ${options.issuer}, got ${payload.iss}`}; + } + + if (options?.clientId && payload.aud !== options.clientId && !Array.isArray(payload.aud)) { + return {valid: false, payload, error: `ID token audience mismatch: expected ${options.clientId}, got ${payload.aud}`}; + } + + if (Array.isArray(payload.aud) && options?.clientId && !payload.aud.includes(options.clientId)) { + return {valid: false, payload, error: `ID token audience mismatch: ${options.clientId} not in ${payload.aud}`}; + } + + if (options?.nonce !== undefined && payload['nonce'] !== options.nonce) { + return {valid: false, payload, error: `ID token nonce mismatch: expected ${options.nonce}, got ${payload['nonce']}`}; + } + + return {valid: true, payload}; +} diff --git a/packages/svelte/vitest.config.ts b/packages/sveltekit/src/validation/index.ts similarity index 81% rename from packages/svelte/vitest.config.ts rename to packages/sveltekit/src/validation/index.ts index bac935f..082620a 100644 --- a/packages/svelte/vitest.config.ts +++ b/packages/sveltekit/src/validation/index.ts @@ -16,10 +16,5 @@ * under the License. */ -import {defineConfig} from 'vitest/config'; - -export default defineConfig({ - test: { - passWithNoTests: true, - }, -}); +export {verifyIdToken, validateIdTokenClaims, clearJWKSCache} from './IdTokenValidator'; +export type {IdTokenValidationResult} from './IdTokenValidator'; diff --git a/packages/svelte/tsconfig.json b/packages/sveltekit/tsconfig.json similarity index 97% rename from packages/svelte/tsconfig.json rename to packages/sveltekit/tsconfig.json index 311d020..ba6587f 100644 --- a/packages/svelte/tsconfig.json +++ b/packages/sveltekit/tsconfig.json @@ -6,7 +6,6 @@ "esModuleInterop": true, "experimentalDecorators": true, "importHelpers": true, - "jsx": "preserve", "lib": ["ESNext", "DOM"], "module": "ESNext", "moduleResolution": "bundler", diff --git a/packages/svelte/tsconfig.lib.json b/packages/sveltekit/tsconfig.lib.json similarity index 100% rename from packages/svelte/tsconfig.lib.json rename to packages/sveltekit/tsconfig.lib.json diff --git a/packages/sveltekit/vitest.config.ts b/packages/sveltekit/vitest.config.ts new file mode 100644 index 0000000..4011268 --- /dev/null +++ b/packages/sveltekit/vitest.config.ts @@ -0,0 +1,10 @@ +import {defineConfig} from 'vitest/config'; +import {svelte} from '@sveltejs/vite-plugin-svelte'; + +export default defineConfig({ + plugins: [svelte({hot: false}) as any], + test: { + passWithNoTests: true, + exclude: ['dist/**', 'node_modules/**'], + }, +}); From 819daf6774f93c7de1edf89f943d111158cb888f Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Thu, 16 Jul 2026 11:55:13 +0530 Subject: [PATCH 21/31] chore: regenerate lockfile after merge --- pnpm-lock.yaml | 1611 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 1180 insertions(+), 431 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ef86b2d..176f1ef 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,202 +1,3 @@ ---- -lockfileVersion: '9.0' - -importers: - - .: - configDependencies: {} - packageManagerDependencies: - '@pnpm/exe': - specifier: 11.9.0 - version: 11.9.0 - pnpm: - specifier: 11.9.0 - version: 11.9.0 - -packages: - - '@pnpm/exe@11.9.0': - resolution: {integrity: sha512-pPPOpR79qW3nsNhlyDIdfstli4Bi78mk8r22ySxpFRwMbO8KXSjGrVzGmJBsVX39NnJTh7/WADj527nZhG9H9g==} - hasBin: true - - '@pnpm/linux-arm64@11.9.0': - resolution: {integrity: sha512-XYmY2qadHauBA3QaHi2R7fI6kt5Flje0WHz9MVrbH0kVH/XLpfOLnwPeE1+EX6K/nDa2CBvzp35VjYCNGFJa9A==} - cpu: [arm64] - os: [linux] - - '@pnpm/linux-x64@11.9.0': - resolution: {integrity: sha512-fl7W5imnSmmgXIqMQFZ/rPaVvk9OkKF8/anqHZE3XEDfWcn3BlWGndyOEas/JN7u2BXWYjs63DJZ3rnG6WOhLA==} - cpu: [x64] - os: [linux] - - '@pnpm/linuxstatic-arm64@11.9.0': - resolution: {integrity: sha512-fif8xbnzVEAIlvaU4yIgWKXeXYb4Kj6WMEl/KvM2x1Rp3AKAjBW/53SGzxO4cZP9doAqUzOIpMRGFVbHGeMDRw==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@pnpm/linuxstatic-x64@11.9.0': - resolution: {integrity: sha512-9dKu3QdShqOpnWrjW9owARpIJeP0ul8UgIIbBUv8VDGEYibt+g49zQsNaD7SdMD3WeRSjExPQ1zIhplzr5cwvQ==} - cpu: [x64] - os: [linux] - libc: [musl] - - '@pnpm/macos-arm64@11.9.0': - resolution: {integrity: sha512-MWzBTgeI5p3odjdVltYvFXaSWAjF2Xk5YaxiP/u2RmW8N6PHsLyIyj37Ds992CrXgFM8fO1RpvsEirozhsD6KA==} - cpu: [arm64] - os: [darwin] - - '@pnpm/win-arm64@11.9.0': - resolution: {integrity: sha512-u/QxEcbKJZxC1t3zUYCZiHzu7TaZ/iXc6EGZoQjkeVT0LXmEVR6ypcK3ByjhIqbxQ3HGX75gnpIpR+VjRjubfw==} - cpu: [arm64] - os: [win32] - - '@pnpm/win-x64@11.9.0': - resolution: {integrity: sha512-HqJVHmZG5UKfLi38AjMl2azVmq87TlWRhwSW+f4q9LLaZeAkFyIZ7LY/pN6mh4VlH9yWj90C+tsb1yKiy7OKnw==} - cpu: [x64] - os: [win32] - - '@reflink/reflink-darwin-arm64@0.1.19': - resolution: {integrity: sha512-ruy44Lpepdk1FqDz38vExBY/PVUsjxZA+chd9wozjUH9JjuDT/HEaQYA6wYN9mf041l0yLVar6BCZuWABJvHSA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [darwin] - - '@reflink/reflink-darwin-x64@0.1.19': - resolution: {integrity: sha512-By85MSWrMZa+c26TcnAy8SDk0sTUkYlNnwknSchkhHpGXOtjNDUOxJE9oByBnGbeuIE1PiQsxDG3Ud+IVV9yuA==} - engines: {node: '>= 10'} - cpu: [x64] - os: [darwin] - - '@reflink/reflink-linux-arm64-gnu@0.1.19': - resolution: {integrity: sha512-7P+er8+rP9iNeN+bfmccM4hTAaLP6PQJPKWSA4iSk2bNvo6KU6RyPgYeHxXmzNKzPVRcypZQTpFgstHam6maVg==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@reflink/reflink-linux-arm64-musl@0.1.19': - resolution: {integrity: sha512-37iO/Dp6m5DDaC2sf3zPtx/hl9FV3Xze4xoYidrxxS9bgP3S8ALroxRK6xBG/1TtfXKTvolvp+IjrUU6ujIGmA==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@reflink/reflink-linux-x64-gnu@0.1.19': - resolution: {integrity: sha512-jbI8jvuYCaA3MVUdu8vLoLAFqC+iNMpiSuLbxlAgg7x3K5bsS8nOpTRnkLF7vISJ+rVR8W+7ThXlXlUQ93ulkw==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@reflink/reflink-linux-x64-musl@0.1.19': - resolution: {integrity: sha512-e9FBWDe+lv7QKAwtKOt6A2W/fyy/aEEfr0g6j/hWzvQcrzHCsz07BNQYlNOjTfeytrtLU7k449H1PI95jA4OjQ==} - engines: {node: '>= 10'} - cpu: [x64] - os: [linux] - libc: [musl] - - '@reflink/reflink-win32-arm64-msvc@0.1.19': - resolution: {integrity: sha512-09PxnVIQcd+UOn4WAW73WU6PXL7DwGS6wPlkMhMg2zlHHG65F3vHepOw06HFCq+N42qkaNAc8AKIabWvtk6cIQ==} - engines: {node: '>= 10'} - cpu: [arm64] - os: [win32] - - '@reflink/reflink-win32-x64-msvc@0.1.19': - resolution: {integrity: sha512-E//yT4ni2SyhwP8JRjVGWr3cbnhWDiPLgnQ66qqaanjjnMiu3O/2tjCPQXlcGc/DEYofpDc9fvhv6tALQsMV9w==} - engines: {node: '>= 10'} - cpu: [x64] - os: [win32] - - '@reflink/reflink@0.1.19': - resolution: {integrity: sha512-DmCG8GzysnCZ15bres3N5AHCmwBwYgp0As6xjhQ47rAUTUXxJiK+lLUxaGsX3hd/30qUpVElh05PbGuxRPgJwA==} - engines: {node: '>= 10'} - - detect-libc@2.1.2: - resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} - engines: {node: '>=8'} - - pnpm@11.9.0: - resolution: {integrity: sha512-vWgtXQP+Ul73yf1ngMaITR51asTJyf4AxTh4KCQxDc+Q493E9Tg18G3669UIXkGFXgvLs7YN4qxburieUDbwOw==} - engines: {node: '>=22.13'} - hasBin: true - -snapshots: - - '@pnpm/exe@11.9.0': - dependencies: - '@reflink/reflink': 0.1.19 - detect-libc: 2.1.2 - optionalDependencies: - '@pnpm/linux-arm64': 11.9.0 - '@pnpm/linux-x64': 11.9.0 - '@pnpm/linuxstatic-arm64': 11.9.0 - '@pnpm/linuxstatic-x64': 11.9.0 - '@pnpm/macos-arm64': 11.9.0 - '@pnpm/win-arm64': 11.9.0 - '@pnpm/win-x64': 11.9.0 - - '@pnpm/linux-arm64@11.9.0': - optional: true - - '@pnpm/linux-x64@11.9.0': - optional: true - - '@pnpm/linuxstatic-arm64@11.9.0': - optional: true - - '@pnpm/linuxstatic-x64@11.9.0': - optional: true - - '@pnpm/macos-arm64@11.9.0': - optional: true - - '@pnpm/win-arm64@11.9.0': - optional: true - - '@pnpm/win-x64@11.9.0': - optional: true - - '@reflink/reflink-darwin-arm64@0.1.19': - optional: true - - '@reflink/reflink-darwin-x64@0.1.19': - optional: true - - '@reflink/reflink-linux-arm64-gnu@0.1.19': - optional: true - - '@reflink/reflink-linux-arm64-musl@0.1.19': - optional: true - - '@reflink/reflink-linux-x64-gnu@0.1.19': - optional: true - - '@reflink/reflink-linux-x64-musl@0.1.19': - optional: true - - '@reflink/reflink-win32-arm64-msvc@0.1.19': - optional: true - - '@reflink/reflink-win32-x64-msvc@0.1.19': - optional: true - - '@reflink/reflink@0.1.19': - optionalDependencies: - '@reflink/reflink-darwin-arm64': 0.1.19 - '@reflink/reflink-darwin-x64': 0.1.19 - '@reflink/reflink-linux-arm64-gnu': 0.1.19 - '@reflink/reflink-linux-arm64-musl': 0.1.19 - '@reflink/reflink-linux-x64-gnu': 0.1.19 - '@reflink/reflink-linux-x64-musl': 0.1.19 - '@reflink/reflink-win32-arm64-msvc': 0.1.19 - '@reflink/reflink-win32-x64-msvc': 0.1.19 - - detect-libc@2.1.2: {} - - pnpm@11.9.0: {} - ---- lockfileVersion: '9.0' settings: @@ -214,6 +15,15 @@ catalogs: '@playwright/test': specifier: 1.60.0 version: 1.60.0 + '@sveltejs/kit': + specifier: 2.68.0 + version: 2.68.0 + '@sveltejs/package': + specifier: 2.5.8 + version: 2.5.8 + '@sveltejs/vite-plugin-svelte': + specifier: 6.2.4 + version: 6.2.4 '@testing-library/react': specifier: 16.3.0 version: 16.3.0 @@ -298,6 +108,12 @@ catalogs: stream-browserify: specifier: 3.0.0 version: 3.0.0 + svelte: + specifier: 5.56.4 + version: 5.56.4 + svelte-check: + specifier: 4.7.1 + version: 4.7.1 tslib: specifier: 2.8.1 version: 2.8.1 @@ -381,7 +197,7 @@ importers: version: 24.7.2 '@vitest/browser-playwright': specifier: 'catalog:' - version: 4.1.8(playwright@1.60.0)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) + version: 4.1.8(playwright@1.60.0)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) eslint: specifier: 'catalog:' version: 9.39.4(jiti@2.7.0) @@ -399,13 +215,13 @@ importers: version: 6.1.3 rolldown: specifier: 'catalog:' - version: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) + version: 1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) packages/express: dependencies: @@ -445,13 +261,13 @@ importers: version: 6.1.3 rolldown: specifier: 'catalog:' - version: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) + version: 1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@22.15.3)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@22.15.3)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) packages/javascript: dependencies: @@ -482,13 +298,13 @@ importers: version: 6.1.3 rolldown: specifier: 'catalog:' - version: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) + version: 1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) packages/nextjs: dependencies: @@ -534,13 +350,13 @@ importers: version: 6.1.3 rolldown: specifier: 'catalog:' - version: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) + version: 1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) packages/node: dependencies: @@ -592,13 +408,13 @@ importers: version: 6.1.3 rolldown: specifier: 'catalog:' - version: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) + version: 1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) packages/nuxt: dependencies: @@ -626,7 +442,7 @@ importers: devDependencies: '@nuxt/devtools': specifier: 2.6.4 - version: 2.6.4(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)) + version: 2.6.4(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)) '@nuxt/module-builder': specifier: 1.0.1 version: 1.0.1(@nuxt/cli@3.36.0(@nuxt/schema@3.21.7)(cac@6.7.14)(magicast@0.5.3))(@vue/compiler-core@3.5.38)(esbuild@0.28.1)(typescript@5.9.3)(vue@3.5.30(typescript@5.9.3)) @@ -635,7 +451,7 @@ importers: version: 3.21.7 '@nuxt/test-utils': specifier: 3.17.2 - version: 3.17.2(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(jsdom@27.0.1)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0) + version: 3.17.2(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(jsdom@27.0.1)(lightningcss@1.32.0)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0) '@thunderid/eslint-plugin': specifier: 'catalog:' version: 0.0.2(@typescript-eslint/utils@8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)(vitest@4.1.8)(vue-eslint-parser@10.4.1(eslint@9.39.4(jiti@2.7.0))) @@ -653,13 +469,13 @@ importers: version: 1.15.11 nuxt: specifier: 3.21.7 - version: 3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) + version: 3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) packages/react: dependencies: @@ -699,7 +515,7 @@ importers: version: 19.2.3(@types/react@19.2.14) '@vitest/browser-playwright': specifier: 'catalog:' - version: 4.1.8(playwright@1.60.0)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) + version: 4.1.8(playwright@1.60.0)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) eslint: specifier: 'catalog:' version: 9.39.4(jiti@2.7.0) @@ -717,13 +533,13 @@ importers: version: 6.1.3 rolldown: specifier: 'catalog:' - version: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) + version: 1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) packages/react-router: dependencies: @@ -751,7 +567,7 @@ importers: version: 19.2.14 '@vitest/browser-playwright': specifier: 'catalog:' - version: 4.1.8(playwright@1.60.0)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) + version: 4.1.8(playwright@1.60.0)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) eslint: specifier: 'catalog:' version: 9.39.4(jiti@2.7.0) @@ -772,13 +588,68 @@ importers: version: 6.1.3 rolldown: specifier: 'catalog:' - version: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) + version: 1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + + packages/sveltekit: + dependencies: + '@thunderid/browser': + specifier: workspace:^ + version: link:../browser + '@thunderid/node': + specifier: workspace:^ + version: link:../node + jose: + specifier: 'catalog:' + version: 5.2.0 + tslib: + specifier: 'catalog:' + version: 2.8.1 + devDependencies: + '@sveltejs/kit': + specifier: 'catalog:' + version: 2.68.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@sveltejs/package': + specifier: 'catalog:' + version: 2.5.8(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3) + '@sveltejs/vite-plugin-svelte': + specifier: 'catalog:' + version: 6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@thunderid/eslint-plugin': + specifier: 'catalog:' + version: 0.0.2(@typescript-eslint/utils@8.61.1(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)(vitest@4.1.8)(vue-eslint-parser@10.4.1(eslint@9.39.4(jiti@2.7.0))) + '@thunderid/prettier-config': + specifier: 'catalog:' + version: 0.0.2 + '@types/node': + specifier: 'catalog:' + version: 24.7.2 + eslint: + specifier: 'catalog:' + version: 9.39.4(jiti@2.7.0) + prettier: + specifier: 'catalog:' + version: 3.6.2 + rimraf: + specifier: 'catalog:' + version: 6.1.3 + svelte: + specifier: 'catalog:' + version: 5.56.4(@typescript-eslint/types@8.61.1) + svelte-check: + specifier: 'catalog:' + version: 4.7.1(picomatch@4.0.4)(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3) + typescript: + specifier: 'catalog:' + version: 5.9.3 + vitest: + specifier: 'catalog:' + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) packages/tanstack-router: dependencies: @@ -821,13 +692,13 @@ importers: version: 6.1.3 rolldown: specifier: 'catalog:' - version: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) + version: 1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) packages/vue: dependencies: @@ -842,7 +713,7 @@ importers: version: 2.8.1 vue-router: specifier: '>=4.0.0' - version: 5.1.0(@vue/compiler-sfc@3.5.38)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)) + version: 5.1.0(@vue/compiler-sfc@3.5.38)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)) devDependencies: '@thunderid/eslint-plugin': specifier: 'catalog:' @@ -867,17 +738,51 @@ importers: version: 6.1.3 rolldown: specifier: 'catalog:' - version: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) + version: 1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) vue: specifier: 3.5.30 version: 3.5.30(typescript@5.9.3) + samples/apps/sveltekit-b2c: + dependencies: + '@thunderid/sveltekit': + specifier: workspace:^ + version: link:../../../packages/sveltekit + devDependencies: + '@sveltejs/adapter-auto': + specifier: ^7.0.1 + version: 7.0.1(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))) + '@sveltejs/kit': + specifier: ^2.63.0 + version: 2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@sveltejs/vite-plugin-svelte': + specifier: ^7.1.2 + version: 7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + prettier: + specifier: ^3.8.3 + version: 3.9.4 + prettier-plugin-svelte: + specifier: ^4.1.0 + version: 4.1.1(prettier@3.9.4)(svelte@5.56.4(@typescript-eslint/types@8.61.1)) + svelte: + specifier: ^5.56.1 + version: 5.56.4(@typescript-eslint/types@8.61.1) + svelte-check: + specifier: ^4.6.0 + version: 4.7.1(picomatch@4.0.4)(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@6.0.3) + typescript: + specifier: ^6.0.3 + version: 6.0.3 + vite: + specifier: ^8.0.16 + version: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + samples/browser/quickstart: dependencies: '@thunderid/browser': @@ -886,7 +791,7 @@ importers: devDependencies: vite: specifier: ^6.3.5 - version: 6.4.3(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + version: 6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) samples/express/quickstart: dependencies: @@ -935,10 +840,10 @@ importers: version: link:../../../packages/nuxt nuxt: specifier: ^4.4.8 - version: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) + version: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) vue: specifier: ^3.5.13 - version: 3.5.30(typescript@5.9.3) + version: 3.5.30(typescript@6.0.3) samples/react/quickstart: dependencies: @@ -960,10 +865,10 @@ importers: devDependencies: '@vitejs/plugin-react': specifier: ^4.5.2 - version: 4.7.0(vite@6.4.3(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + version: 4.7.0(vite@6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0)) vite: specifier: ^6.3.5 - version: 6.4.3(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + version: 6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) samples/vue/quickstart: dependencies: @@ -972,14 +877,14 @@ importers: version: link:../../../packages/vue vue: specifier: ^3.5.13 - version: 3.5.30(typescript@5.9.3) + version: 3.5.30(typescript@6.0.3) devDependencies: '@vitejs/plugin-vue': specifier: ^5.2.3 - version: 5.2.4(vite@6.4.3(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)) + version: 5.2.4(vite@6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@6.0.3)) vite: specifier: ^6.3.5 - version: 6.4.3(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + version: 6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) packages: @@ -1229,6 +1134,9 @@ packages: '@emnapi/core@1.10.0': resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + '@emnapi/core@1.11.1': + resolution: {integrity: sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==} + '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} @@ -1238,6 +1146,9 @@ packages: '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + '@emnapi/wasi-threads@1.2.2': + resolution: {integrity: sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==} + '@emotion/babel-plugin@11.13.5': resolution: {integrity: sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==} @@ -2015,6 +1926,12 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 + '@napi-rs/wasm-runtime@1.1.6': + resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + '@next/env@15.5.18': resolution: {integrity: sha512-hAV85Ckd9QR6RvH04MEKwsfLTksvFpO47j9xwtoIuvuPnlwecpSi+uZTtm8HirVbtlI2Fnz//xpcSTjFdyJk+g==} @@ -2769,6 +2686,9 @@ packages: '@oxc-project/types@0.133.0': resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==} + '@oxc-project/types@0.137.0': + resolution: {integrity: sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==} + '@oxc-project/types@0.95.0': resolution: {integrity: sha512-vACy7vhpMPhjEJhULNxrdR0D943TkA/MigMpJCHmBHvMXxRStRi/dPtTlfQ3uDwWSzRpT8z+7ImjZVf8JWBocQ==} @@ -3150,30 +3070,60 @@ packages: cpu: [arm64] os: [android] + '@rolldown/binding-android-arm64@1.1.3': + resolution: {integrity: sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + '@rolldown/binding-darwin-arm64@1.0.0-beta.45': resolution: {integrity: sha512-xjCv4CRVsSnnIxTuyH1RDJl5OEQ1c9JYOwfDAHddjJDxCw46ZX9q80+xq7Eok7KC4bRSZudMJllkvOKv0T9SeA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] + '@rolldown/binding-darwin-arm64@1.1.3': + resolution: {integrity: sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + '@rolldown/binding-darwin-x64@1.0.0-beta.45': resolution: {integrity: sha512-ddcO9TD3D/CLUa/l8GO8LHzBOaZqWg5ClMy3jICoxwCuoz47h9dtqPsIeTiB6yR501LQTeDsjA4lIFd7u3Ljfw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] + '@rolldown/binding-darwin-x64@1.1.3': + resolution: {integrity: sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + '@rolldown/binding-freebsd-x64@1.0.0-beta.45': resolution: {integrity: sha512-MBTWdrzW9w+UMYDUvnEuh0pQvLENkl2Sis15fHTfHVW7ClbGuez+RWopZudIDEGkpZXdeI4CkRXk+vdIIebrmg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] + '@rolldown/binding-freebsd-x64@1.1.3': + resolution: {integrity: sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.45': resolution: {integrity: sha512-4YgoCFiki1HR6oSg+GxxfzfnVCesQxLF1LEnw9uXS/MpBmuog0EOO2rYfy69rWP4tFZL9IWp6KEfGZLrZ7aUog==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] + '@rolldown/binding-linux-arm-gnueabihf@1.1.3': + resolution: {integrity: sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.45': resolution: {integrity: sha512-LE1gjAwQRrbCOorJJ7LFr10s5vqYf5a00V5Ea9wXcT2+56n5YosJkcp8eQ12FxRBv2YX8dsdQJb+ZTtYJwb6XQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3181,6 +3131,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-arm64-gnu@1.1.3': + resolution: {integrity: sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.45': resolution: {integrity: sha512-tdy8ThO/fPp40B81v0YK3QC+KODOmzJzSUOO37DinQxzlTJ026gqUSOM8tzlVixRbQJltgVDCTYF8HNPRErQTA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3188,6 +3145,27 @@ packages: os: [linux] libc: [musl] + '@rolldown/binding-linux-arm64-musl@1.1.3': + resolution: {integrity: sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.1.3': + resolution: {integrity: sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.1.3': + resolution: {integrity: sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.45': resolution: {integrity: sha512-lS082ROBWdmOyVY/0YB3JmsiClaWoxvC+dA8/rbhyB9VLkvVEaihLEOr4CYmrMse151C4+S6hCw6oa1iewox7g==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3195,6 +3173,13 @@ packages: os: [linux] libc: [glibc] + '@rolldown/binding-linux-x64-gnu@1.1.3': + resolution: {integrity: sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + '@rolldown/binding-linux-x64-musl@1.0.0-beta.45': resolution: {integrity: sha512-Hi73aYY0cBkr1/SvNQqH8Cd+rSV6S9RB5izCv0ySBcRnd/Wfn5plguUoGYwBnhHgFbh6cPw9m2dUVBR6BG1gxA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3202,23 +3187,47 @@ packages: os: [linux] libc: [musl] + '@rolldown/binding-linux-x64-musl@1.1.3': + resolution: {integrity: sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + '@rolldown/binding-openharmony-arm64@1.0.0-beta.45': resolution: {integrity: sha512-fljEqbO7RHHogNDxYtTzr+GNjlfOx21RUyGmF+NrkebZ8emYYiIqzPxsaMZuRx0rgZmVmliOzEp86/CQFDKhJQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] + '@rolldown/binding-openharmony-arm64@1.1.3': + resolution: {integrity: sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + '@rolldown/binding-wasm32-wasi@1.0.0-beta.45': resolution: {integrity: sha512-ZJDB7lkuZE9XUnWQSYrBObZxczut+8FZ5pdanm8nNS1DAo8zsrPuvGwn+U3fwU98WaiFsNrA4XHngesCGr8tEQ==} engines: {node: '>=14.0.0'} cpu: [wasm32] + '@rolldown/binding-wasm32-wasi@1.1.3': + resolution: {integrity: sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [wasm32] + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.45': resolution: {integrity: sha512-zyzAjItHPUmxg6Z8SyRhLdXlJn3/D9KL5b9mObUrBHhWS/GwRH4665xCiFqeuktAhhWutqfc+rOV2LjK4VYQGQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] + '@rolldown/binding-win32-arm64-msvc@1.1.3': + resolution: {integrity: sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.45': resolution: {integrity: sha512-wODcGzlfxqS6D7BR0srkJk3drPwXYLu7jPHN27ce2c4PUnVVmJnp9mJzUQGT4LpmHmmVdMZ+P6hKvyTGBzc1CA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3231,6 +3240,12 @@ packages: cpu: [x64] os: [win32] + '@rolldown/binding-win32-x64-msvc@1.1.3': + resolution: {integrity: sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + '@rolldown/pluginutils@1.0.0-beta.27': resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} @@ -3488,6 +3503,65 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@sveltejs/acorn-typescript@1.0.10': + resolution: {integrity: sha512-4WfKk68eTih+MiJD4fSbxN7E8kVBmTMPWHUPYjvl2N0rMs53YLTT8/YjKU5Dtnz5LqDjl7LEw4U7lXR2W3J5WA==} + peerDependencies: + acorn: ^8.9.0 + + '@sveltejs/adapter-auto@7.0.1': + resolution: {integrity: sha512-dvuPm1E7M9NI/+canIQ6KKQDU2AkEefEZ2Dp7cY6uKoPq9Z/PhOXABe526UdW2mN986gjVkuSLkOYIBnS/M2LQ==} + peerDependencies: + '@sveltejs/kit': ^2.0.0 + + '@sveltejs/kit@2.68.0': + resolution: {integrity: sha512-PdKiWsqinAoubVsSiRgVFkg3MHzGhQPnwQ8VxnGQKpZYijpapZ3UHHBje0GeByt2TvfjHPw+kxV+dNK2RIZg9g==} + engines: {node: '>=18.13'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.0.0 + '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: ^5.3.3 || ^6.0.0 + vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + typescript: + optional: true + + '@sveltejs/load-config@0.2.0': + resolution: {integrity: sha512-1LgZ/qUqSoq+QorD83lk2hka79Px0wXNW2q5V1nZlxGhQgw1jrsIbVz5YiCeucVLo4XvFLjXukUaQjIiqowkcg==} + engines: {node: '>= 18.0.0'} + + '@sveltejs/package@2.5.8': + resolution: {integrity: sha512-zeBbsXYvHiBu56v4gJaGQoEHzg96w0E1j3dOMX8vo56s6vI5eQ57ZEZhudjwjnegnVitRRu5MrmhO0eNvaonIw==} + engines: {node: ^16.14 || >=18} + hasBin: true + peerDependencies: + svelte: ^3.44.0 || ^4.0.0 || ^5.0.0-next.1 + + '@sveltejs/vite-plugin-svelte-inspector@5.0.2': + resolution: {integrity: sha512-TZzRTcEtZffICSAoZGkPSl6Etsj2torOVrx6Uw0KpXxrec9Gg6jFWQ60Q3+LmNGfZSxHRCZL7vXVZIWmuV50Ig==} + engines: {node: ^20.19 || ^22.12 || >=24} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^6.0.0-next.0 + svelte: ^5.0.0 + vite: ^6.3.0 || ^7.0.0 + + '@sveltejs/vite-plugin-svelte@6.2.4': + resolution: {integrity: sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA==} + engines: {node: ^20.19 || ^22.12 || >=24} + peerDependencies: + svelte: ^5.0.0 + vite: ^6.3.0 || ^7.0.0 + + '@sveltejs/vite-plugin-svelte@7.1.2': + resolution: {integrity: sha512-DrUBA2UXRfDmUX/ZTiEopd3X40yavsJF1FX2RygcuIScHL7o5YX1fMvoYnDhjeJQC4weCOklirpNWlcb2NiSeA==} + engines: {node: ^20.19 || ^22.12 || >=24} + peerDependencies: + svelte: ^5.46.4 + vite: ^8.0.0-beta.7 || ^8.0.0 + '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} @@ -3579,6 +3653,9 @@ packages: '@tybys/wasm-util@0.10.2': resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + '@tybys/wasm-util@0.10.3': + resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} + '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} @@ -3608,6 +3685,9 @@ packages: peerDependencies: '@types/express': '*' + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} @@ -4186,6 +4266,10 @@ packages: aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + aria-query@5.3.1: + resolution: {integrity: sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==} + engines: {node: '>= 0.4'} + aria-query@5.3.2: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} @@ -4353,8 +4437,8 @@ packages: birpc@4.0.0: resolution: {integrity: sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==} - body-parser@1.20.5: - resolution: {integrity: sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==} + body-parser@1.20.6: + resolution: {integrity: sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} body-parser@2.3.0: @@ -4471,6 +4555,10 @@ packages: resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} engines: {node: '>=20'} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + cluster-key-slot@1.1.1: resolution: {integrity: sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==} engines: {node: '>=0.10.0'} @@ -4568,6 +4656,10 @@ packages: resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} engines: {node: '>=6.6.0'} + cookie@0.6.0: + resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} + engines: {node: '>= 0.6'} + cookie@0.7.2: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} @@ -4756,6 +4848,9 @@ packages: decimal.js@10.6.0: resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + dedent-js@1.0.1: + resolution: {integrity: sha512-OUepMozQULMLUmhxS95Vudo0jb0UchLimi3+pQ2plj61Fcy8axbP9hbiD4Sz6DPqn6XG3kfmziVfQ1rSys5AJQ==} + deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -5099,6 +5194,9 @@ packages: jiti: optional: true + esm-env@1.2.2: + resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==} + espree@10.4.0: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -5111,6 +5209,14 @@ packages: resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} + esrap@2.2.13: + resolution: {integrity: sha512-m8jH5hZgJE2RRUK/jjkGPcJEDAV+dYnZYFkosQaPTcE+Yw4xynXHOo6FUdwaWBtdR3b1MMa7wEDTSHeR2VWsGA==} + peerDependencies: + '@typescript-eslint/types': ^8.2.0 + peerDependenciesMeta: + '@typescript-eslint/types': + optional: true + esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} @@ -5651,6 +5757,9 @@ packages: is-reference@1.2.1: resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + is-reference@3.0.3: + resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -5831,6 +5940,80 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + lilconfig@3.1.3: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} @@ -5846,6 +6029,9 @@ packages: resolution: {integrity: sha512-++gUqRDEvcnN6Zhqrr+y/CkVEHhlrR96vZn3nZZPYzMcBUyBtTKzB9NadClFIsIVSsu+3i9tfk/erqy9kAmt7Q==} engines: {node: '>=14'} + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -6028,6 +6214,10 @@ packages: mocked-exports@0.1.1: resolution: {integrity: sha512-aF7yRQr/Q0O2/4pIXm6PZ5G+jAd7QS4Yu8m+WEeEHGnbo+7mE36CbLSDQiXYV8bVL3NfmdeqPJct0tUlnjVSnA==} + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + mrmime@2.0.1: resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} @@ -6750,11 +6940,23 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prettier-plugin-svelte@4.1.1: + resolution: {integrity: sha512-wXvbXMjSvb4C9ENWTHXyd+ihakKCsJ6rJhLP6/8HFNj4GkZr48jqL9PoKsl2sk7SyCZRTnJ7O2TTowUpOxP/KA==} + engines: {node: '>=20'} + peerDependencies: + prettier: ^3.0.0 + svelte: ^5.0.0 + prettier@3.6.2: resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} engines: {node: '>=14'} hasBin: true + prettier@3.9.4: + resolution: {integrity: sha512-yWG/o/4oJfo036EKAfK6ACAoDOfHeRHx4tuxkfBZiauURiaSmYwlpOr5LQqKtIkRD2z1PLteme2WoxEnj4tHTg==} + engines: {node: '>=14'} + hasBin: true + pretty-bytes@7.1.0: resolution: {integrity: sha512-nODzvTiYVRGRqAOvE84Vk5JDPyyxsVk0/fbA/bq7RqlnhksGpset09XTxbpvLTIjoaF7K8Z8DG8yHtKGTPSYRw==} engines: {node: '>=20'} @@ -6939,6 +7141,11 @@ packages: engines: {node: ^20.19.0 || >=22.12.0} hasBin: true + rolldown@1.1.3: + resolution: {integrity: sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + rollup-plugin-dts@6.4.1: resolution: {integrity: sha512-l//F3Zf7ID5GoOfLfD8kroBjQKEKpy1qfhtAdnpibFZMffPaylrg1CoDC2vGkPeTeyxUe4bVFCln2EFuL7IGGg==} engines: {node: '>=20'} @@ -6981,6 +7188,10 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + safe-array-concat@1.1.4: resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} engines: {node: '>=0.4'} @@ -7069,6 +7280,9 @@ packages: set-cookie-parser@2.7.2: resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + set-cookie-parser@3.1.1: + resolution: {integrity: sha512-vM9SUhjsUYs6UeJUmygc5Ofm5eQGe85riob5ju6XCgFGJI5PLV4nrDAQpQjd+LkFBpAkADn5BQQpZ9EUNkyLuA==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -7312,6 +7526,24 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + svelte-check@4.7.1: + resolution: {integrity: sha512-FGUOmAqxXdN/H9Zm8slrqO7SLtFisXRB7rfOsHNJ3MLTD2po/+Stg8XyErkpumPHbuUiYTcqrEIzxpVWKTLqtg==} + engines: {node: '>= 18.0.0'} + hasBin: true + peerDependencies: + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: '>=5.0.0' + + svelte2tsx@0.7.57: + resolution: {integrity: sha512-nQo0xEfUpSVjfkxan2UmC6Wl9UZVe9+/pglUiyBNMJ7KDQ9DDooucmXdToBOvzSfgYjj/kMYwf6CTTDz/z048w==} + peerDependencies: + svelte: ^3.55 || ^4.0.0-next.0 || ^4.0 || ^5.0.0-next.0 + typescript: ^4.9.4 || ^5.0.0 || ^6.0.0 + + svelte@5.56.4: + resolution: {integrity: sha512-/d0QHehmRuJW8gVz395MTkPcPozxzdjBMBE8oEYGz8O3b9KTMzzQ9ZHJQLuFKOHOPQbU6kx/X4iid/EBBzH7iw==} + engines: {node: '>=18'} + svgo@4.0.1: resolution: {integrity: sha512-XDpWUOPC6FEibaLzjfe0ucaV0YrOjYotGJO1WpF0Zd+n6ZGEQUsSugaoLq9QkEZtAfQIxT42UChcssDVPP3+/w==} engines: {node: '>=16'} @@ -7478,6 +7710,11 @@ packages: engines: {node: '>=14.17'} hasBin: true + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} + engines: {node: '>=14.17'} + hasBin: true + ufo@1.6.4: resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} @@ -7855,6 +8092,57 @@ packages: yaml: optional: true + vite@8.1.0: + resolution: {integrity: sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.3.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitefu@1.1.3: + resolution: {integrity: sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + vite: + optional: true + vitest-environment-nuxt@1.0.1: resolution: {integrity: sha512-eBCwtIQriXW5/M49FjqNKfnlJYlG2LWMSNFsRVKomc8CaMqmhQPBS5LZ9DlgYL9T8xIVsiA6RZn2lk7vxov3Ow==} @@ -8118,6 +8406,9 @@ packages: youch@4.1.1: resolution: {integrity: sha512-mxW3qiSnl+GRxXsaUMzv2Mbada1Y8CDltET9UxejDQe6DBYlSekghl5U5K0ReAikcHDi0G1vKZEmmo/NWAGKLA==} + zimmerframe@1.1.4: + resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} + zip-stream@6.0.1: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} engines: {node: '>= 14'} @@ -8411,6 +8702,18 @@ snapshots: transitivePeerDependencies: - magicast + '@dxup/nuxt@0.4.1(magicast@0.5.3)(typescript@6.0.3)': + dependencies: + '@dxup/unimport': 0.1.2 + '@nuxt/kit': 4.4.8(magicast@0.5.3) + chokidar: 5.0.0 + pathe: 2.0.3 + tinyglobby: 0.2.17 + optionalDependencies: + typescript: 6.0.3 + transitivePeerDependencies: + - magicast + '@dxup/unimport@0.1.2': {} '@emnapi/core@1.10.0': @@ -8419,6 +8722,12 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/core@1.11.1': + dependencies: + '@emnapi/wasi-threads': 1.2.2 + tslib: 2.8.1 + optional: true + '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 @@ -8434,6 +8743,11 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/wasi-threads@1.2.2': + dependencies: + tslib: 2.8.1 + optional: true + '@emotion/babel-plugin@11.13.5': dependencies: '@babel/helper-module-imports': 7.29.7 @@ -8973,13 +9287,27 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true - '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1)': + '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: - '@emnapi/core': 1.10.0 + '@emnapi/core': 1.11.1 '@emnapi/runtime': 1.11.1 '@tybys/wasm-util': 0.10.2 optional: true + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.3 + optional: true + + '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': + dependencies: + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@tybys/wasm-util': 0.10.3 + optional: true + '@next/env@15.5.18': {} '@next/swc-darwin-arm64@15.5.18': @@ -9096,19 +9424,19 @@ snapshots: '@nuxt/devalue@2.0.2': {} - '@nuxt/devtools-kit@2.6.4(magicast@0.3.5)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + '@nuxt/devtools-kit@2.6.4(magicast@0.3.5)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': dependencies: '@nuxt/kit': 3.21.7(magicast@0.3.5) execa: 8.0.1 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) transitivePeerDependencies: - magicast - '@nuxt/devtools-kit@3.2.4(magicast@0.5.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + '@nuxt/devtools-kit@3.2.4(magicast@0.5.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': dependencies: '@nuxt/kit': 4.4.8(magicast@0.5.3) execa: 8.0.1 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) transitivePeerDependencies: - magicast @@ -9134,12 +9462,12 @@ snapshots: pkg-types: 2.3.1 semver: 7.8.5 - '@nuxt/devtools@2.6.4(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3))': + '@nuxt/devtools@2.6.4(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3))': dependencies: - '@nuxt/devtools-kit': 2.6.4(magicast@0.3.5)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@nuxt/devtools-kit': 2.6.4(magicast@0.3.5)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@nuxt/devtools-wizard': 2.6.4 '@nuxt/kit': 3.21.7(magicast@0.3.5) - '@vue/devtools-core': 7.7.9(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)) + '@vue/devtools-core': 7.7.9(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)) '@vue/devtools-kit': 7.7.9 birpc: 2.9.0 consola: 3.4.2 @@ -9164,9 +9492,9 @@ snapshots: sirv: 3.0.2 structured-clone-es: 1.0.0 tinyglobby: 0.2.17 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vite-plugin-inspect: 11.4.1(@nuxt/kit@3.21.7(magicast@0.5.3))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) - vite-plugin-vue-tracer: 1.4.0(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite-plugin-inspect: 11.4.1(@nuxt/kit@3.21.7(magicast@0.5.3))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vite-plugin-vue-tracer: 1.4.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)) which: 5.0.0 ws: 8.21.0 transitivePeerDependencies: @@ -9175,9 +9503,9 @@ snapshots: - utf-8-validate - vue - '@nuxt/devtools@3.2.4(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3))': + '@nuxt/devtools@3.2.4(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3))': dependencies: - '@nuxt/devtools-kit': 3.2.4(magicast@0.5.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@nuxt/devtools-kit': 3.2.4(magicast@0.5.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@nuxt/devtools-wizard': 3.2.4 '@nuxt/kit': 4.4.8(magicast@0.5.3) '@vue/devtools-core': 8.1.3(vue@3.5.38(typescript@5.9.3)) @@ -9205,9 +9533,50 @@ snapshots: sirv: 3.0.2 structured-clone-es: 2.0.0 tinyglobby: 0.2.17 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vite-plugin-inspect: 11.4.1(@nuxt/kit@4.4.8(magicast@0.5.3))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) - vite-plugin-vue-tracer: 1.4.0(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite-plugin-inspect: 11.4.1(@nuxt/kit@4.4.8(magicast@0.5.3))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vite-plugin-vue-tracer: 1.4.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) + which: 6.0.1 + ws: 8.21.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + - vue + + '@nuxt/devtools@3.2.4(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@nuxt/devtools-kit': 3.2.4(magicast@0.5.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@nuxt/devtools-wizard': 3.2.4 + '@nuxt/kit': 4.4.8(magicast@0.5.3) + '@vue/devtools-core': 8.1.3(vue@3.5.38(typescript@6.0.3)) + '@vue/devtools-kit': 8.1.3 + birpc: 4.0.0 + consola: 3.4.2 + destr: 2.0.5 + error-stack-parser-es: 1.0.5 + execa: 8.0.1 + fast-npm-meta: 1.5.1 + get-port-please: 3.2.0 + hookable: 6.1.1 + image-meta: 0.2.2 + is-installed-globally: 1.0.0 + launch-editor: 2.14.1 + local-pkg: 1.2.1 + magicast: 0.5.3 + nypm: 0.6.7 + ohash: 2.0.11 + pathe: 2.0.3 + perfect-debounce: 2.1.0 + pkg-types: 2.3.1 + semver: 7.8.5 + simple-git: 3.36.0 + sirv: 3.0.2 + structured-clone-es: 2.0.0 + tinyglobby: 0.2.17 + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite-plugin-inspect: 11.4.1(@nuxt/kit@4.4.8(magicast@0.5.3))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vite-plugin-vue-tracer: 1.4.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) which: 6.0.1 ws: 8.21.0 transitivePeerDependencies: @@ -9316,7 +9685,7 @@ snapshots: - vue - vue-tsc - '@nuxt/nitro-server@3.21.7(db0@0.3.4)(ioredis@5.11.1)(magicast@0.5.3)(nuxt@3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(oxc-parser@0.132.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(srvx@0.11.17)(typescript@5.9.3)': + '@nuxt/nitro-server@3.21.7(db0@0.3.4)(ioredis@5.11.1)(magicast@0.5.3)(nuxt@3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(oxc-parser@0.132.0)(rolldown@1.1.3)(srvx@0.11.17)(typescript@5.9.3)': dependencies: '@nuxt/devalue': 2.0.2 '@nuxt/kit': 3.21.7(magicast@0.5.3) @@ -9333,8 +9702,8 @@ snapshots: impound: 1.1.5 klona: 2.0.6 mocked-exports: 0.1.1 - nitropack: 2.13.4(oxc-parser@0.132.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(srvx@0.11.17) - nuxt: 3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) + nitropack: 2.13.4(oxc-parser@0.132.0)(rolldown@1.1.3)(srvx@0.11.17) + nuxt: 3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) ohash: 2.0.11 pathe: 2.0.3 pkg-types: 2.3.1 @@ -9384,11 +9753,11 @@ snapshots: - uploadthing - xml2js - '@nuxt/nitro-server@4.4.8(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(db0@0.3.4)(ioredis@5.11.1)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(oxc-parser@0.133.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(typescript@5.9.3)': + '@nuxt/nitro-server@4.4.8(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(db0@0.3.4)(ioredis@5.11.1)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(oxc-parser@0.133.0)(rolldown@1.1.3)(typescript@6.0.3)': dependencies: '@nuxt/devalue': 2.0.2 '@nuxt/kit': 4.4.8(magicast@0.5.3) - '@unhead/vue': 2.1.15(vue@3.5.38(typescript@5.9.3)) + '@unhead/vue': 2.1.15(vue@3.5.38(typescript@6.0.3)) '@vue/shared': 3.5.38 consola: 3.4.2 defu: 6.1.7 @@ -9401,8 +9770,8 @@ snapshots: impound: 1.1.5 klona: 2.0.6 mocked-exports: 0.1.1 - nitropack: 2.13.4(oxc-parser@0.133.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)) - nuxt: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) + nitropack: 2.13.4(oxc-parser@0.133.0)(rolldown@1.1.3) + nuxt: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) nypm: 0.6.7 ohash: 2.0.11 pathe: 2.0.3 @@ -9411,7 +9780,7 @@ snapshots: ufo: 1.6.4 unctx: 2.5.0 unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1) - vue: 3.5.38(typescript@5.9.3) + vue: 3.5.38(typescript@6.0.3) vue-bundle-renderer: 2.2.0 vue-devtools-stub: 0.1.0 optionalDependencies: @@ -9488,7 +9857,7 @@ snapshots: rc9: 3.0.1 std-env: 4.1.0 - '@nuxt/test-utils@3.17.2(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(jsdom@27.0.1)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0)': + '@nuxt/test-utils@3.17.2(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(jsdom@27.0.1)(lightningcss@1.32.0)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0)': dependencies: '@nuxt/kit': 3.21.7(magicast@0.5.3) '@nuxt/schema': 3.21.7 @@ -9513,15 +9882,15 @@ snapshots: tinyexec: 0.3.2 ufo: 1.6.4 unplugin: 2.3.11 - vite: 6.4.3(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vitest-environment-nuxt: 1.0.1(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(jsdom@27.0.1)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0) + vite: 6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) + vitest-environment-nuxt: 1.0.1(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(jsdom@27.0.1)(lightningcss@1.32.0)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0) vue: 3.5.30(typescript@5.9.3) optionalDependencies: '@playwright/test': 1.60.0 '@vue/test-utils': 2.4.6 jsdom: 27.0.1 playwright-core: 1.60.0 - vitest: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vitest: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) transitivePeerDependencies: - '@types/node' - jiti @@ -9537,12 +9906,12 @@ snapshots: - typescript - yaml - '@nuxt/vite-builder@3.21.7(@types/node@24.7.2)(eslint@9.39.4(jiti@2.7.0))(magicast@0.5.3)(nuxt@3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3))(yaml@2.9.0)': + '@nuxt/vite-builder@3.21.7(@types/node@24.7.2)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.32.0)(magicast@0.5.3)(nuxt@3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3))(yaml@2.9.0)': dependencies: '@nuxt/kit': 3.21.7(magicast@0.5.3) '@rollup/plugin-replace': 6.0.3(rollup@4.62.2) - '@vitejs/plugin-vue': 6.0.7(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) - '@vitejs/plugin-vue-jsx': 5.1.5(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) + '@vitejs/plugin-vue': 6.0.7(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) + '@vitejs/plugin-vue-jsx': 5.1.5(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) autoprefixer: 10.5.0(postcss@8.5.15) consola: 3.4.2 cssnano: 7.1.9(postcss@8.5.15) @@ -9556,7 +9925,7 @@ snapshots: magic-string: 0.30.21 mlly: 1.8.2 mocked-exports: 0.1.1 - nuxt: 3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) + nuxt: 3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) nypm: 0.6.7 ohash: 2.0.11 pathe: 2.0.3 @@ -9567,14 +9936,14 @@ snapshots: std-env: 4.1.0 ufo: 1.6.4 unenv: 2.0.0-rc.24 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vite-node: 5.3.0(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vite-plugin-checker: 0.13.0(eslint@9.39.4(jiti@2.7.0))(optionator@0.9.4)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) + vite-node: 5.3.0(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) + vite-plugin-checker: 0.13.0(eslint@9.39.4(jiti@2.7.0))(optionator@0.9.4)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0)) vue: 3.5.38(typescript@5.9.3) vue-bundle-renderer: 2.2.0 optionalDependencies: - rolldown: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - rollup-plugin-visualizer: 7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2) + rolldown: 1.1.3 + rollup-plugin-visualizer: 7.0.1(rolldown@1.1.3)(rollup@4.62.2) transitivePeerDependencies: - '@biomejs/biome' - '@types/node' @@ -9600,12 +9969,12 @@ snapshots: - vue-tsc - yaml - '@nuxt/vite-builder@4.4.8(5e770e72ca055b8ebf51d504a3c88ac6)': + '@nuxt/vite-builder@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@types/node@24.7.2)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.32.0)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3))(yaml@2.9.0)': dependencies: '@nuxt/kit': 4.4.8(magicast@0.5.3) '@rollup/plugin-replace': 6.0.3(rollup@4.62.2) - '@vitejs/plugin-vue': 6.0.7(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) - '@vitejs/plugin-vue-jsx': 5.1.5(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) + '@vitejs/plugin-vue': 6.0.7(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) + '@vitejs/plugin-vue-jsx': 5.1.5(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) autoprefixer: 10.5.0(postcss@8.5.15) consola: 3.4.2 cssnano: 8.0.2(postcss@8.5.15) @@ -9618,7 +9987,7 @@ snapshots: magic-string: 0.30.21 mlly: 1.8.2 mocked-exports: 0.1.1 - nuxt: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) + nuxt: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) nypm: 0.6.7 pathe: 2.0.3 pkg-types: 2.3.1 @@ -9627,15 +9996,15 @@ snapshots: std-env: 4.1.0 ufo: 1.6.4 unenv: 2.0.0-rc.24 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vite-node: 5.3.0(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vite-plugin-checker: 0.14.4(eslint@9.39.4(jiti@2.7.0))(optionator@0.9.4)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) - vue: 3.5.38(typescript@5.9.3) + vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) + vite-node: 5.3.0(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) + vite-plugin-checker: 0.14.4(eslint@9.39.4(jiti@2.7.0))(optionator@0.9.4)(typescript@6.0.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0)) + vue: 3.5.38(typescript@6.0.3) vue-bundle-renderer: 2.2.0 optionalDependencies: '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) - rolldown: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - rollup-plugin-visualizer: 7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2) + rolldown: 1.1.3 + rollup-plugin-visualizer: 7.0.1(rolldown@1.1.3)(rollup@4.62.2) transitivePeerDependencies: - '@biomejs/biome' - '@types/node' @@ -9768,7 +10137,7 @@ snapshots: dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true '@oxc-minify/binding-win32-arm64-msvc@0.132.0': @@ -9896,7 +10265,7 @@ snapshots: dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true '@oxc-parser/binding-win32-arm64-msvc@0.132.0': @@ -9921,6 +10290,8 @@ snapshots: '@oxc-project/types@0.133.0': {} + '@oxc-project/types@0.137.0': {} + '@oxc-project/types@0.95.0': {} '@oxc-transform/binding-android-arm-eabi@0.132.0': @@ -10030,7 +10401,7 @@ snapshots: dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true '@oxc-transform/binding-win32-arm64-msvc@0.132.0': @@ -10142,58 +10513,99 @@ snapshots: '@rolldown/binding-android-arm64@1.0.0-beta.45': optional: true + '@rolldown/binding-android-arm64@1.1.3': + optional: true + '@rolldown/binding-darwin-arm64@1.0.0-beta.45': optional: true + '@rolldown/binding-darwin-arm64@1.1.3': + optional: true + '@rolldown/binding-darwin-x64@1.0.0-beta.45': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-beta.45': + '@rolldown/binding-darwin-x64@1.1.3': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.0-beta.45': + optional: true + + '@rolldown/binding-freebsd-x64@1.1.3': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.45': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.1.3': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.45': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.1.3': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.45': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.45': + '@rolldown/binding-linux-arm64-musl@1.1.3': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.45': + '@rolldown/binding-linux-ppc64-gnu@1.1.3': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.45': + '@rolldown/binding-linux-s390x-gnu@1.1.3': optional: true '@rolldown/binding-linux-x64-gnu@1.0.0-beta.45': optional: true + '@rolldown/binding-linux-x64-gnu@1.1.3': + optional: true + '@rolldown/binding-linux-x64-musl@1.0.0-beta.45': optional: true + '@rolldown/binding-linux-x64-musl@1.1.3': + optional: true + '@rolldown/binding-openharmony-arm64@1.0.0-beta.45': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@rolldown/binding-openharmony-arm64@1.1.3': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: - '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1)': + '@rolldown/binding-wasm32-wasi@1.1.3': dependencies: - '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' + '@emnapi/core': 1.11.1 + '@emnapi/runtime': 1.11.1 + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) optional: true '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.45': optional: true + '@rolldown/binding-win32-arm64-msvc@1.1.3': + optional: true + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.45': optional: true '@rolldown/binding-win32-x64-msvc@1.0.0-beta.45': optional: true + '@rolldown/binding-win32-x64-msvc@1.1.3': + optional: true + '@rolldown/pluginutils@1.0.0-beta.27': {} '@rolldown/pluginutils@1.0.0-beta.45': {} @@ -10368,6 +10780,93 @@ snapshots: '@standard-schema/spec@1.1.0': {} + '@sveltejs/acorn-typescript@1.0.10(acorn@8.17.0)': + dependencies: + acorn: 8.17.0 + + '@sveltejs/adapter-auto@7.0.1(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))': + dependencies: + '@sveltejs/kit': 2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + + '@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + dependencies: + '@standard-schema/spec': 1.1.0 + '@sveltejs/acorn-typescript': 1.0.10(acorn@8.17.0) + '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@types/cookie': 0.6.0 + acorn: 8.17.0 + cookie: 0.6.0 + devalue: 5.8.1 + esm-env: 1.2.2 + kleur: 4.1.5 + magic-string: 0.30.21 + mrmime: 2.0.1 + set-cookie-parser: 3.1.1 + sirv: 3.0.2 + svelte: 5.56.4(@typescript-eslint/types@8.61.1) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + optionalDependencies: + typescript: 5.9.3 + + '@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + dependencies: + '@standard-schema/spec': 1.1.0 + '@sveltejs/acorn-typescript': 1.0.10(acorn@8.17.0) + '@sveltejs/vite-plugin-svelte': 7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@types/cookie': 0.6.0 + acorn: 8.17.0 + cookie: 0.6.0 + devalue: 5.8.1 + esm-env: 1.2.2 + kleur: 4.1.5 + magic-string: 0.30.21 + mrmime: 2.0.1 + set-cookie-parser: 3.1.1 + sirv: 3.0.2 + svelte: 5.56.4(@typescript-eslint/types@8.61.1) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + optionalDependencies: + typescript: 6.0.3 + + '@sveltejs/load-config@0.2.0': {} + + '@sveltejs/package@2.5.8(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3)': + dependencies: + chokidar: 5.0.0 + kleur: 4.1.5 + sade: 1.8.1 + semver: 7.8.5 + svelte: 5.56.4(@typescript-eslint/types@8.61.1) + svelte2tsx: 0.7.57(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3) + transitivePeerDependencies: + - typescript + + '@sveltejs/vite-plugin-svelte-inspector@5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + dependencies: + '@sveltejs/vite-plugin-svelte': 6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + obug: 2.1.3 + svelte: 5.56.4(@typescript-eslint/types@8.61.1) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + + '@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 5.0.2(@sveltejs/vite-plugin-svelte@6.2.4(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + deepmerge: 4.3.1 + magic-string: 0.30.21 + obug: 2.1.3 + svelte: 5.56.4(@typescript-eslint/types@8.61.1) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vitefu: 1.1.3(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + + '@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.61.1))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + dependencies: + deepmerge: 4.3.1 + magic-string: 0.30.21 + obug: 2.1.3 + svelte: 5.56.4(@typescript-eslint/types@8.61.1) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vitefu: 1.1.3(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 @@ -10482,6 +10981,11 @@ snapshots: tslib: 2.8.1 optional: true + '@tybys/wasm-util@0.10.3': + dependencies: + tslib: 2.8.1 + optional: true + '@types/aria-query@5.0.4': {} '@types/babel__core@7.20.5': @@ -10523,6 +11027,8 @@ snapshots: dependencies: '@types/express': 5.0.6 + '@types/cookie@0.6.0': {} + '@types/deep-eql@4.0.2': {} '@types/esrecurse@4.3.1': {} @@ -10581,8 +11087,7 @@ snapshots: '@types/http-errors': 2.0.5 '@types/node': 24.7.2 - '@types/trusted-types@2.0.7': - optional: true + '@types/trusted-types@2.0.7': {} '@typescript-eslint/eslint-plugin@8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': dependencies: @@ -10732,6 +11237,12 @@ snapshots: unhead: 2.1.15 vue: 3.5.38(typescript@5.9.3) + '@unhead/vue@2.1.15(vue@3.5.38(typescript@6.0.3))': + dependencies: + hookable: 6.1.1 + unhead: 2.1.15 + vue: 3.5.38(typescript@6.0.3) + '@unrs/resolver-binding-android-arm-eabi@1.12.2': optional: true @@ -10790,7 +11301,7 @@ snapshots: dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': @@ -10821,7 +11332,7 @@ snapshots: - rollup - supports-color - '@vitejs/plugin-react@4.7.0(vite@6.4.3(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + '@vitejs/plugin-react@4.7.0(vite@6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.7 '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7) @@ -10829,40 +11340,58 @@ snapshots: '@rolldown/pluginutils': 1.0.0-beta.27 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 - vite: 6.4.3(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue-jsx@5.1.5(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3))': + '@vitejs/plugin-vue-jsx@5.1.5(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3))': dependencies: '@babel/core': 7.29.7 '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) '@rolldown/pluginutils': 1.0.1 '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.7) - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) vue: 3.5.38(typescript@5.9.3) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@5.2.4(vite@6.4.3(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3))': + '@vitejs/plugin-vue-jsx@5.1.5(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3))': dependencies: - vite: 6.4.3(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vue: 3.5.30(typescript@5.9.3) + '@babel/core': 7.29.7 + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) + '@rolldown/pluginutils': 1.0.1 + '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.7) + vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) + vue: 3.5.38(typescript@6.0.3) + transitivePeerDependencies: + - supports-color + + '@vitejs/plugin-vue@5.2.4(vite@6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@6.0.3))': + dependencies: + vite: 6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) + vue: 3.5.30(typescript@6.0.3) - '@vitejs/plugin-vue@6.0.7(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.7(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) vue: 3.5.38(typescript@5.9.3) - '@vitest/browser-playwright@4.1.8(playwright@1.60.0)(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8)': + '@vitejs/plugin-vue@6.0.7(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@rolldown/pluginutils': 1.0.1 + vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) + vue: 3.5.38(typescript@6.0.3) + + '@vitest/browser-playwright@4.1.8(playwright@1.60.0)(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8)': dependencies: - '@vitest/browser': 4.1.8(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) - '@vitest/mocker': 4.1.8(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@vitest/browser': 4.1.8(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) + '@vitest/mocker': 4.1.8(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) playwright: 1.60.0 tinyrainbow: 3.1.0 - vitest: 4.1.8(@types/node@22.15.3)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vitest: 4.1.8(@types/node@22.15.3)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw @@ -10870,29 +11399,29 @@ snapshots: - vite optional: true - '@vitest/browser-playwright@4.1.8(playwright@1.60.0)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8)': + '@vitest/browser-playwright@4.1.8(playwright@1.60.0)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8)': dependencies: - '@vitest/browser': 4.1.8(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) - '@vitest/mocker': 4.1.8(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@vitest/browser': 4.1.8(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) + '@vitest/mocker': 4.1.8(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) playwright: 1.60.0 tinyrainbow: 3.1.0 - vitest: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vitest: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser@4.1.8(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8)': + '@vitest/browser@4.1.8(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.8(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@vitest/mocker': 4.1.8(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@vitest/utils': 4.1.8 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.8(@types/node@22.15.3)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vitest: 4.1.8(@types/node@22.15.3)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) ws: 8.21.0 transitivePeerDependencies: - bufferutil @@ -10901,16 +11430,16 @@ snapshots: - vite optional: true - '@vitest/browser@4.1.8(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8)': + '@vitest/browser@4.1.8(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8)': dependencies: '@blazediff/core': 1.9.1 - '@vitest/mocker': 4.1.8(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@vitest/mocker': 4.1.8(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@vitest/utils': 4.1.8 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 - vitest: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vitest: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) ws: 8.21.0 transitivePeerDependencies: - bufferutil @@ -10926,7 +11455,7 @@ snapshots: optionalDependencies: '@typescript-eslint/eslint-plugin': 8.57.2(@typescript-eslint/parser@8.57.2(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) typescript: 5.9.3 - vitest: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vitest: 4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) transitivePeerDependencies: - supports-color @@ -10939,21 +11468,21 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.8(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + '@vitest/mocker@4.1.8(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.8 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - '@vitest/mocker@4.1.8(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': + '@vitest/mocker@4.1.8(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.8 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) '@vitest/pretty-format@4.1.8': dependencies: @@ -11005,6 +11534,16 @@ snapshots: optionalDependencies: vue: 3.5.38(typescript@5.9.3) + '@vue-macros/common@3.1.2(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@vue/compiler-sfc': 3.5.38 + ast-kit: 2.2.0 + local-pkg: 1.2.1 + magic-string-ast: 1.0.3 + unplugin-utils: 0.3.1 + optionalDependencies: + vue: 3.5.38(typescript@6.0.3) + '@vue/babel-helper-vue-transform-on@2.0.1': {} '@vue/babel-plugin-jsx@2.0.1(@babel/core@7.29.7)': @@ -11100,14 +11639,14 @@ snapshots: dependencies: '@vue/devtools-kit': 8.1.3 - '@vue/devtools-core@7.7.9(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3))': + '@vue/devtools-core@7.7.9(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3))': dependencies: '@vue/devtools-kit': 7.7.9 '@vue/devtools-shared': 7.7.9 mitt: 3.0.1 nanoid: 5.1.15 pathe: 2.0.3 - vite-hot-client: 2.2.0(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vite-hot-client: 2.2.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) vue: 3.5.30(typescript@5.9.3) transitivePeerDependencies: - vite @@ -11118,6 +11657,12 @@ snapshots: '@vue/devtools-shared': 8.1.3 vue: 3.5.38(typescript@5.9.3) + '@vue/devtools-core@8.1.3(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@vue/devtools-kit': 8.1.3 + '@vue/devtools-shared': 8.1.3 + vue: 3.5.38(typescript@6.0.3) + '@vue/devtools-kit@7.7.9': dependencies: '@vue/devtools-shared': 7.7.9 @@ -11189,12 +11734,24 @@ snapshots: '@vue/shared': 3.5.30 vue: 3.5.30(typescript@5.9.3) + '@vue/server-renderer@3.5.30(vue@3.5.30(typescript@6.0.3))': + dependencies: + '@vue/compiler-ssr': 3.5.30 + '@vue/shared': 3.5.30 + vue: 3.5.30(typescript@6.0.3) + '@vue/server-renderer@3.5.38(vue@3.5.38(typescript@5.9.3))': dependencies: '@vue/compiler-ssr': 3.5.38 '@vue/shared': 3.5.38 vue: 3.5.38(typescript@5.9.3) + '@vue/server-renderer@3.5.38(vue@3.5.38(typescript@6.0.3))': + dependencies: + '@vue/compiler-ssr': 3.5.38 + '@vue/shared': 3.5.38 + vue: 3.5.38(typescript@6.0.3) + '@vue/shared@3.5.30': {} '@vue/shared@3.5.38': {} @@ -11292,6 +11849,8 @@ snapshots: dependencies: dequal: 2.0.3 + aria-query@5.3.1: {} + aria-query@5.3.2: {} array-buffer-byte-length@1.0.2: @@ -11459,7 +12018,7 @@ snapshots: birpc@4.0.0: {} - body-parser@1.20.5: + body-parser@1.20.6: dependencies: bytes: 3.1.2 content-type: 1.0.5 @@ -11632,6 +12191,8 @@ snapshots: strip-ansi: 7.2.0 wrap-ansi: 9.0.2 + clsx@2.1.1: {} + cluster-key-slot@1.1.1: {} color-convert@2.0.1: @@ -11704,6 +12265,8 @@ snapshots: cookie-signature@1.2.2: {} + cookie@0.6.0: {} + cookie@0.7.2: {} cookie@1.1.1: {} @@ -11916,6 +12479,8 @@ snapshots: decimal.js@10.6.0: {} + dedent-js@1.0.1: {} + deep-is@0.1.4: {} deepmerge@4.3.1: {} @@ -12433,6 +12998,8 @@ snapshots: transitivePeerDependencies: - supports-color + esm-env@1.2.2: {} + espree@10.4.0: dependencies: acorn: 8.17.0 @@ -12449,6 +13016,12 @@ snapshots: dependencies: estraverse: 5.3.0 + esrap@2.2.13(@typescript-eslint/types@8.61.1): + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + optionalDependencies: + '@typescript-eslint/types': 8.61.1 + esrecurse@4.3.0: dependencies: estraverse: 5.3.0 @@ -12493,7 +13066,7 @@ snapshots: dependencies: accepts: 1.3.8 array-flatten: 1.1.1 - body-parser: 1.20.5 + body-parser: 1.20.6 content-disposition: 0.5.4 content-type: 1.0.5 cookie: 0.7.2 @@ -13060,6 +13633,10 @@ snapshots: dependencies: '@types/estree': 1.0.9 + is-reference@3.0.3: + dependencies: + '@types/estree': 1.0.9 + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -13238,6 +13815,55 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} @@ -13271,6 +13897,8 @@ snapshots: pkg-types: 2.3.1 quansync: 0.2.11 + locate-character@3.0.0: {} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -13436,6 +14064,8 @@ snapshots: mocked-exports@0.1.1: {} + mri@1.2.0: {} + mrmime@2.0.1: {} ms@2.0.0: {} @@ -13482,7 +14112,7 @@ snapshots: - '@babel/core' - babel-plugin-macros - nitropack@2.13.4(oxc-parser@0.132.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(srvx@0.11.17): + nitropack@2.13.4(oxc-parser@0.132.0)(rolldown@1.1.3)(srvx@0.11.17): dependencies: '@cloudflare/kv-asset-handler': 0.4.2 '@rollup/plugin-alias': 6.0.0(rollup@4.62.2) @@ -13535,7 +14165,7 @@ snapshots: pretty-bytes: 7.1.0 radix3: 1.1.2 rollup: 4.62.2 - rollup-plugin-visualizer: 7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2) + rollup-plugin-visualizer: 7.0.1(rolldown@1.1.3)(rollup@4.62.2) scule: 1.3.0 semver: 7.8.5 serve-placeholder: 2.0.2 @@ -13547,7 +14177,7 @@ snapshots: uncrypto: 0.1.3 unctx: 2.5.0 unenv: 2.0.0-rc.24 - unimport: 6.3.0(oxc-parser@0.132.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)) + unimport: 6.3.0(oxc-parser@0.132.0)(rolldown@1.1.3) unplugin-utils: 0.3.1 unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1) untyped: 2.0.0 @@ -13587,7 +14217,7 @@ snapshots: - supports-color - uploadthing - nitropack@2.13.4(oxc-parser@0.133.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)): + nitropack@2.13.4(oxc-parser@0.133.0)(rolldown@1.1.3): dependencies: '@cloudflare/kv-asset-handler': 0.4.2 '@rollup/plugin-alias': 6.0.0(rollup@4.62.2) @@ -13640,7 +14270,7 @@ snapshots: pretty-bytes: 7.1.0 radix3: 1.1.2 rollup: 4.62.2 - rollup-plugin-visualizer: 7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2) + rollup-plugin-visualizer: 7.0.1(rolldown@1.1.3)(rollup@4.62.2) scule: 1.3.0 semver: 7.8.5 serve-placeholder: 2.0.2 @@ -13652,7 +14282,7 @@ snapshots: uncrypto: 0.1.3 unctx: 2.5.0 unenv: 2.0.0-rc.24 - unimport: 6.3.0(oxc-parser@0.133.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)) + unimport: 6.3.0(oxc-parser@0.133.0)(rolldown@1.1.3) unplugin-utils: 0.3.1 unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1) untyped: 2.0.0 @@ -13738,16 +14368,16 @@ snapshots: dependencies: boolbase: 1.0.0 - nuxt@3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0): + nuxt@3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0): dependencies: '@dxup/nuxt': 0.4.1(magicast@0.5.3)(typescript@5.9.3) '@nuxt/cli': 3.36.0(@nuxt/schema@3.21.7)(cac@6.7.14)(magicast@0.5.3) - '@nuxt/devtools': 3.2.4(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) + '@nuxt/devtools': 3.2.4(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) '@nuxt/kit': 3.21.7(magicast@0.5.3) - '@nuxt/nitro-server': 3.21.7(db0@0.3.4)(ioredis@5.11.1)(magicast@0.5.3)(nuxt@3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(oxc-parser@0.132.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(srvx@0.11.17)(typescript@5.9.3) + '@nuxt/nitro-server': 3.21.7(db0@0.3.4)(ioredis@5.11.1)(magicast@0.5.3)(nuxt@3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(oxc-parser@0.132.0)(rolldown@1.1.3)(srvx@0.11.17)(typescript@5.9.3) '@nuxt/schema': 3.21.7 '@nuxt/telemetry': 2.8.0(@nuxt/kit@3.21.7(magicast@0.5.3)) - '@nuxt/vite-builder': 3.21.7(@types/node@24.7.2)(eslint@9.39.4(jiti@2.7.0))(magicast@0.5.3)(nuxt@3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3))(yaml@2.9.0) + '@nuxt/vite-builder': 3.21.7(@types/node@24.7.2)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.32.0)(magicast@0.5.3)(nuxt@3.21.7(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(srvx@0.11.17)(terser@5.48.0)(typescript@5.9.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@5.9.3)(vue@3.5.38(typescript@5.9.3))(yaml@2.9.0) '@unhead/vue': 2.1.15(vue@3.5.38(typescript@5.9.3)) '@vue/shared': 3.5.38 c12: 3.3.4(magicast@0.5.3) @@ -13778,7 +14408,7 @@ snapshots: oxc-minify: 0.132.0 oxc-parser: 0.132.0 oxc-transform: 0.132.0 - oxc-walker: 1.0.0(oxc-parser@0.132.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)) + oxc-walker: 1.0.0(oxc-parser@0.132.0)(rolldown@1.1.3) pathe: 2.0.3 perfect-debounce: 2.1.0 pkg-types: 2.3.1 @@ -13791,7 +14421,7 @@ snapshots: ultrahtml: 1.6.0 uncrypto: 0.1.3 unctx: 2.5.0 - unimport: 6.3.0(oxc-parser@0.132.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)) + unimport: 6.3.0(oxc-parser@0.132.0)(rolldown@1.1.3) unplugin: 3.0.0 unplugin-vue-router: 0.19.2(@vue/compiler-sfc@3.5.38)(vue-router@4.6.4(vue@3.5.38(typescript@5.9.3)))(vue@3.5.38(typescript@5.9.3)) untyped: 2.0.0 @@ -13864,17 +14494,17 @@ snapshots: - xml2js - yaml - nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0): + nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0): dependencies: - '@dxup/nuxt': 0.4.1(magicast@0.5.3)(typescript@5.9.3) + '@dxup/nuxt': 0.4.1(magicast@0.5.3)(typescript@6.0.3) '@nuxt/cli': 3.36.0(@nuxt/schema@4.4.8)(cac@6.7.14)(magicast@0.5.3) - '@nuxt/devtools': 3.2.4(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) + '@nuxt/devtools': 3.2.4(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) '@nuxt/kit': 4.4.8(magicast@0.5.3) - '@nuxt/nitro-server': 4.4.8(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(db0@0.3.4)(ioredis@5.11.1)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(oxc-parser@0.133.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(typescript@5.9.3) + '@nuxt/nitro-server': 4.4.8(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(db0@0.3.4)(ioredis@5.11.1)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(oxc-parser@0.133.0)(rolldown@1.1.3)(typescript@6.0.3) '@nuxt/schema': 4.4.8 '@nuxt/telemetry': 2.8.0(@nuxt/kit@4.4.8(magicast@0.5.3)) - '@nuxt/vite-builder': 4.4.8(5e770e72ca055b8ebf51d504a3c88ac6) - '@unhead/vue': 2.1.15(vue@3.5.38(typescript@5.9.3)) + '@nuxt/vite-builder': 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@types/node@24.7.2)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.32.0)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3))(yaml@2.9.0) + '@unhead/vue': 2.1.15(vue@3.5.38(typescript@6.0.3)) '@vue/shared': 3.5.38 chokidar: 5.0.0 compatx: 0.2.0 @@ -13901,7 +14531,7 @@ snapshots: oxc-minify: 0.133.0 oxc-parser: 0.133.0 oxc-transform: 0.133.0 - oxc-walker: 1.0.0(oxc-parser@0.133.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)) + oxc-walker: 1.0.0(oxc-parser@0.133.0)(rolldown@1.1.3) pathe: 2.0.3 perfect-debounce: 2.1.0 picomatch: 4.0.4 @@ -13916,12 +14546,12 @@ snapshots: uncrypto: 0.1.3 unctx: 2.5.0 unhead: 2.1.15 - unimport: 6.3.0(oxc-parser@0.133.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)) + unimport: 6.3.0(oxc-parser@0.133.0)(rolldown@1.1.3) unplugin: 3.0.0 unrouting: 0.1.7 untyped: 2.0.0 - vue: 3.5.38(typescript@5.9.3) - vue-router: 5.1.0(@vue/compiler-sfc@3.5.38)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)) + vue: 3.5.38(typescript@6.0.3) + vue-router: 5.1.0(@vue/compiler-sfc@3.5.38)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) optionalDependencies: '@parcel/watcher': 2.5.6 '@types/node': 24.7.2 @@ -14227,19 +14857,19 @@ snapshots: '@oxc-transform/binding-win32-ia32-msvc': 0.133.0 '@oxc-transform/binding-win32-x64-msvc': 0.133.0 - oxc-walker@1.0.0(oxc-parser@0.132.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)): + oxc-walker@1.0.0(oxc-parser@0.132.0)(rolldown@1.1.3): dependencies: magic-regexp: 0.11.0 optionalDependencies: oxc-parser: 0.132.0 - rolldown: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + rolldown: 1.1.3 - oxc-walker@1.0.0(oxc-parser@0.133.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)): + oxc-walker@1.0.0(oxc-parser@0.133.0)(rolldown@1.1.3): dependencies: magic-regexp: 0.11.0 optionalDependencies: oxc-parser: 0.133.0 - rolldown: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + rolldown: 1.1.3 p-limit@3.1.0: dependencies: @@ -14656,8 +15286,15 @@ snapshots: prelude-ls@1.2.1: {} + prettier-plugin-svelte@4.1.1(prettier@3.9.4)(svelte@5.56.4(@typescript-eslint/types@8.61.1)): + dependencies: + prettier: 3.9.4 + svelte: 5.56.4(@typescript-eslint/types@8.61.1) + prettier@3.6.2: {} + prettier@3.9.4: {} + pretty-bytes@7.1.0: {} pretty-format@27.5.1: @@ -14847,7 +15484,7 @@ snapshots: glob: 13.0.6 package-json-from-dist: 1.0.1 - rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): + rolldown@1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1): dependencies: '@oxc-project/types': 0.95.0 '@rolldown/pluginutils': 1.0.0-beta.45 @@ -14862,37 +15499,34 @@ snapshots: '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.45 '@rolldown/binding-linux-x64-musl': 1.0.0-beta.45 '@rolldown/binding-openharmony-arm64': 1.0.0-beta.45 - '@rolldown/binding-wasm32-wasi': 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.45(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.45 '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.45 '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.45 transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' - optional: true - rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1): + rolldown@1.1.3: dependencies: - '@oxc-project/types': 0.95.0 - '@rolldown/pluginutils': 1.0.0-beta.45 + '@oxc-project/types': 0.137.0 + '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-beta.45 - '@rolldown/binding-darwin-arm64': 1.0.0-beta.45 - '@rolldown/binding-darwin-x64': 1.0.0-beta.45 - '@rolldown/binding-freebsd-x64': 1.0.0-beta.45 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.45 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.45 - '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.45 - '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.45 - '@rolldown/binding-linux-x64-musl': 1.0.0-beta.45 - '@rolldown/binding-openharmony-arm64': 1.0.0-beta.45 - '@rolldown/binding-wasm32-wasi': 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.1) - '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.45 - '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.45 - '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.45 - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' + '@rolldown/binding-android-arm64': 1.1.3 + '@rolldown/binding-darwin-arm64': 1.1.3 + '@rolldown/binding-darwin-x64': 1.1.3 + '@rolldown/binding-freebsd-x64': 1.1.3 + '@rolldown/binding-linux-arm-gnueabihf': 1.1.3 + '@rolldown/binding-linux-arm64-gnu': 1.1.3 + '@rolldown/binding-linux-arm64-musl': 1.1.3 + '@rolldown/binding-linux-ppc64-gnu': 1.1.3 + '@rolldown/binding-linux-s390x-gnu': 1.1.3 + '@rolldown/binding-linux-x64-gnu': 1.1.3 + '@rolldown/binding-linux-x64-musl': 1.1.3 + '@rolldown/binding-openharmony-arm64': 1.1.3 + '@rolldown/binding-wasm32-wasi': 1.1.3 + '@rolldown/binding-win32-arm64-msvc': 1.1.3 + '@rolldown/binding-win32-x64-msvc': 1.1.3 rollup-plugin-dts@6.4.1(rollup@4.62.2)(typescript@5.9.3): dependencies: @@ -14905,14 +15539,14 @@ snapshots: optionalDependencies: '@babel/code-frame': 7.29.7 - rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0))(rollup@4.62.2): + rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2): dependencies: open: 11.0.0 picomatch: 4.0.4 source-map: 0.7.6 yargs: 18.0.0 optionalDependencies: - rolldown: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + rolldown: 1.1.3 rollup: 4.62.2 rollup@4.62.2: @@ -14966,6 +15600,10 @@ snapshots: dependencies: queue-microtask: 1.2.3 + sade@1.8.1: + dependencies: + mri: 1.2.0 + safe-array-concat@1.1.4: dependencies: call-bind: 1.0.9 @@ -15079,6 +15717,8 @@ snapshots: set-cookie-parser@2.7.2: {} + set-cookie-parser@3.1.1: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -15375,6 +16015,60 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + svelte-check@4.7.1(picomatch@4.0.4)(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@sveltejs/load-config': 0.2.0 + chokidar: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picocolors: 1.1.1 + sade: 1.8.1 + svelte: 5.56.4(@typescript-eslint/types@8.61.1) + typescript: 5.9.3 + transitivePeerDependencies: + - picomatch + + svelte-check@4.7.1(picomatch@4.0.4)(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@6.0.3): + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@sveltejs/load-config': 0.2.0 + chokidar: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picocolors: 1.1.1 + sade: 1.8.1 + svelte: 5.56.4(@typescript-eslint/types@8.61.1) + typescript: 6.0.3 + transitivePeerDependencies: + - picomatch + + svelte2tsx@0.7.57(svelte@5.56.4(@typescript-eslint/types@8.61.1))(typescript@5.9.3): + dependencies: + dedent-js: 1.0.1 + scule: 1.3.0 + svelte: 5.56.4(@typescript-eslint/types@8.61.1) + typescript: 5.9.3 + + svelte@5.56.4(@typescript-eslint/types@8.61.1): + dependencies: + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.5.5 + '@sveltejs/acorn-typescript': 1.0.10(acorn@8.17.0) + '@types/estree': 1.0.9 + '@types/trusted-types': 2.0.7 + acorn: 8.17.0 + aria-query: 5.3.1 + axobject-query: 4.1.0 + clsx: 2.1.1 + devalue: 5.8.1 + esm-env: 1.2.2 + esrap: 2.2.13(@typescript-eslint/types@8.61.1) + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.21 + zimmerframe: 1.1.4 + transitivePeerDependencies: + - '@typescript-eslint/types' + svgo@4.0.1: dependencies: commander: 11.1.0 @@ -15561,6 +16255,8 @@ snapshots: typescript@5.9.3: {} + typescript@6.0.3: {} + ufo@1.6.4: {} ultrahtml@1.6.0: {} @@ -15631,7 +16327,7 @@ snapshots: unicorn-magic@0.4.0: {} - unimport@6.3.0(oxc-parser@0.132.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)): + unimport@6.3.0(oxc-parser@0.132.0)(rolldown@1.1.3): dependencies: acorn: 8.17.0 escape-string-regexp: 5.0.0 @@ -15649,9 +16345,9 @@ snapshots: unplugin-utils: 0.3.1 optionalDependencies: oxc-parser: 0.132.0 - rolldown: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + rolldown: 1.1.3 - unimport@6.3.0(oxc-parser@0.133.0)(rolldown@1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)): + unimport@6.3.0(oxc-parser@0.133.0)(rolldown@1.1.3): dependencies: acorn: 8.17.0 escape-string-regexp: 5.0.0 @@ -15669,7 +16365,7 @@ snapshots: unplugin-utils: 0.3.1 optionalDependencies: oxc-parser: 0.133.0 - rolldown: 1.0.0-beta.45(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + rolldown: 1.1.3 unpipe@1.0.0: {} @@ -15814,23 +16510,23 @@ snapshots: vary@1.1.2: {} - vite-dev-rpc@2.0.0(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): + vite-dev-rpc@2.0.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): dependencies: birpc: 4.0.0 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vite-hot-client: 2.2.0(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite-hot-client: 2.2.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) - vite-hot-client@2.2.0(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): + vite-hot-client@2.2.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): dependencies: - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vite-node@5.3.0(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0): + vite-node@5.3.0(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0): dependencies: cac: 6.7.14 es-module-lexer: 2.1.0 obug: 2.1.3 pathe: 2.0.3 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) transitivePeerDependencies: - '@types/node' - jiti @@ -15844,7 +16540,7 @@ snapshots: - tsx - yaml - vite-plugin-checker@0.13.0(eslint@9.39.4(jiti@2.7.0))(optionator@0.9.4)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): + vite-plugin-checker@0.13.0(eslint@9.39.4(jiti@2.7.0))(optionator@0.9.4)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0)): dependencies: '@babel/code-frame': 7.29.7 chokidar: 4.0.3 @@ -15854,14 +16550,14 @@ snapshots: proper-lockfile: 4.1.2 tiny-invariant: 1.3.3 tinyglobby: 0.2.17 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) vscode-uri: 3.1.0 optionalDependencies: eslint: 9.39.4(jiti@2.7.0) optionator: 0.9.4 typescript: 5.9.3 - vite-plugin-checker@0.14.4(eslint@9.39.4(jiti@2.7.0))(optionator@0.9.4)(typescript@5.9.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): + vite-plugin-checker@0.14.4(eslint@9.39.4(jiti@2.7.0))(optionator@0.9.4)(typescript@6.0.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0)): dependencies: '@babel/code-frame': 7.29.7 chokidar: 5.0.0 @@ -15870,13 +16566,13 @@ snapshots: picomatch: 4.0.4 proper-lockfile: 4.1.2 tiny-invariant: 1.3.3 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) optionalDependencies: eslint: 9.39.4(jiti@2.7.0) optionator: 0.9.4 - typescript: 5.9.3 + typescript: 6.0.3 - vite-plugin-inspect@11.4.1(@nuxt/kit@3.21.7(magicast@0.5.3))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): + vite-plugin-inspect@11.4.1(@nuxt/kit@3.21.7(magicast@0.5.3))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): dependencies: ansis: 4.3.1 error-stack-parser-es: 1.0.5 @@ -15886,12 +16582,12 @@ snapshots: perfect-debounce: 2.1.0 sirv: 3.0.2 unplugin-utils: 0.3.1 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vite-dev-rpc: 2.0.0(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite-dev-rpc: 2.0.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) optionalDependencies: '@nuxt/kit': 3.21.7(magicast@0.5.3) - vite-plugin-inspect@11.4.1(@nuxt/kit@4.4.8(magicast@0.5.3))(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): + vite-plugin-inspect@11.4.1(@nuxt/kit@4.4.8(magicast@0.5.3))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): dependencies: ansis: 4.3.1 error-stack-parser-es: 1.0.5 @@ -15901,32 +16597,42 @@ snapshots: perfect-debounce: 2.1.0 sirv: 3.0.2 unplugin-utils: 0.3.1 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vite-dev-rpc: 2.0.0(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite-dev-rpc: 2.0.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) optionalDependencies: '@nuxt/kit': 4.4.8(magicast@0.5.3) - vite-plugin-vue-tracer@1.4.0(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)): + vite-plugin-vue-tracer@1.4.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)): dependencies: estree-walker: 3.0.3 exsolve: 1.0.8 magic-string: 0.30.21 pathe: 2.0.3 source-map-js: 1.2.1 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) vue: 3.5.30(typescript@5.9.3) - vite-plugin-vue-tracer@1.4.0(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)): + vite-plugin-vue-tracer@1.4.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)): dependencies: estree-walker: 3.0.3 exsolve: 1.0.8 magic-string: 0.30.21 pathe: 2.0.3 source-map-js: 1.2.1 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) vue: 3.5.38(typescript@5.9.3) - vite@6.4.3(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0): + vite-plugin-vue-tracer@1.4.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)): + dependencies: + estree-walker: 3.0.3 + exsolve: 1.0.8 + magic-string: 0.30.21 + pathe: 2.0.3 + source-map-js: 1.2.1 + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vue: 3.5.38(typescript@6.0.3) + + vite@6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.4) @@ -15938,10 +16644,11 @@ snapshots: '@types/node': 24.7.2 fsevents: 2.3.3 jiti: 2.7.0 + lightningcss: 1.32.0 terser: 5.48.0 yaml: 2.9.0 - vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0): + vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) @@ -15949,31 +16656,51 @@ snapshots: postcss: 8.5.15 rollup: 4.62.2 tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 24.7.2 + fsevents: 2.3.3 + jiti: 2.7.0 + lightningcss: 1.32.0 + terser: 5.48.0 + yaml: 2.9.0 + + vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0): + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.5.15 + rolldown: 1.1.3 + tinyglobby: 0.2.17 optionalDependencies: '@types/node': 22.15.3 + esbuild: 0.28.1 fsevents: 2.3.3 jiti: 2.7.0 terser: 5.48.0 yaml: 2.9.0 - vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0): + vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0): dependencies: - esbuild: 0.27.7 - fdir: 6.5.0(picomatch@4.0.4) + lightningcss: 1.32.0 picomatch: 4.0.4 postcss: 8.5.15 - rollup: 4.62.2 + rolldown: 1.1.3 tinyglobby: 0.2.17 optionalDependencies: '@types/node': 24.7.2 + esbuild: 0.28.1 fsevents: 2.3.3 jiti: 2.7.0 terser: 5.48.0 yaml: 2.9.0 - vitest-environment-nuxt@1.0.1(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(jsdom@27.0.1)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0): + vitefu@1.1.3(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): + optionalDependencies: + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + + vitest-environment-nuxt@1.0.1(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(jsdom@27.0.1)(lightningcss@1.32.0)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0): dependencies: - '@nuxt/test-utils': 3.17.2(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(jsdom@27.0.1)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0) + '@nuxt/test-utils': 3.17.2(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(jsdom@27.0.1)(lightningcss@1.32.0)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0) transitivePeerDependencies: - '@cucumber/cucumber' - '@jest/globals' @@ -15999,10 +16726,10 @@ snapshots: - vitest - yaml - vitest@4.1.8(@types/node@22.15.3)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): + vitest@4.1.8(@types/node@22.15.3)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.8 - '@vitest/mocker': 4.1.8(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@vitest/mocker': 4.1.8(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.8 '@vitest/runner': 4.1.8 '@vitest/snapshot': 4.1.8 @@ -16019,19 +16746,19 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.15.3 - '@vitest/browser-playwright': 4.1.8(playwright@1.60.0)(vite@7.3.5(@types/node@22.15.3)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) + '@vitest/browser-playwright': 4.1.8(playwright@1.60.0)(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) jsdom: 27.0.1 transitivePeerDependencies: - msw - vitest@4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): + vitest@4.1.8(@types/node@24.7.2)(@vitest/browser-playwright@4.1.8)(jsdom@27.0.1)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.8 - '@vitest/mocker': 4.1.8(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) + '@vitest/mocker': 4.1.8(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.8 '@vitest/runner': 4.1.8 '@vitest/snapshot': 4.1.8 @@ -16048,11 +16775,11 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.7.2 - '@vitest/browser-playwright': 4.1.8(playwright@1.60.0)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) + '@vitest/browser-playwright': 4.1.8(playwright@1.60.0)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) jsdom: 27.0.1 transitivePeerDependencies: - msw @@ -16084,7 +16811,7 @@ snapshots: '@vue/devtools-api': 6.6.4 vue: 3.5.30(typescript@5.9.3) - vue-router@5.1.0(@vue/compiler-sfc@3.5.38)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)): + vue-router@5.1.0(@vue/compiler-sfc@3.5.38)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@5.9.3)): dependencies: '@babel/generator': 8.0.0 '@vue-macros/common': 3.1.2(vue@3.5.30(typescript@5.9.3)) @@ -16106,12 +16833,12 @@ snapshots: yaml: 2.9.0 optionalDependencies: '@vue/compiler-sfc': 3.5.38 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vue-router@5.1.0(@vue/compiler-sfc@3.5.38)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3)): + vue-router@5.1.0(@vue/compiler-sfc@3.5.38)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)): dependencies: '@babel/generator': 8.0.0 - '@vue-macros/common': 3.1.2(vue@3.5.38(typescript@5.9.3)) + '@vue-macros/common': 3.1.2(vue@3.5.38(typescript@6.0.3)) '@vue/devtools-api': 8.1.3 ast-walker-scope: 0.9.0 chokidar: 5.0.0 @@ -16126,11 +16853,11 @@ snapshots: tinyglobby: 0.2.17 unplugin: 3.0.0 unplugin-utils: 0.3.1 - vue: 3.5.38(typescript@5.9.3) + vue: 3.5.38(typescript@6.0.3) yaml: 2.9.0 optionalDependencies: '@vue/compiler-sfc': 3.5.38 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) + vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) vue-sfc-transformer@0.1.17(@vue/compiler-core@3.5.38)(esbuild@0.28.1)(vue@3.5.30(typescript@5.9.3)): dependencies: @@ -16149,6 +16876,16 @@ snapshots: optionalDependencies: typescript: 5.9.3 + vue@3.5.30(typescript@6.0.3): + dependencies: + '@vue/compiler-dom': 3.5.30 + '@vue/compiler-sfc': 3.5.30 + '@vue/runtime-dom': 3.5.30 + '@vue/server-renderer': 3.5.30(vue@3.5.30(typescript@6.0.3)) + '@vue/shared': 3.5.30 + optionalDependencies: + typescript: 6.0.3 + vue@3.5.38(typescript@5.9.3): dependencies: '@vue/compiler-dom': 3.5.38 @@ -16159,6 +16896,16 @@ snapshots: optionalDependencies: typescript: 5.9.3 + vue@3.5.38(typescript@6.0.3): + dependencies: + '@vue/compiler-dom': 3.5.38 + '@vue/compiler-sfc': 3.5.38 + '@vue/runtime-dom': 3.5.38 + '@vue/server-renderer': 3.5.38(vue@3.5.38(typescript@6.0.3)) + '@vue/shared': 3.5.38 + optionalDependencies: + typescript: 6.0.3 + w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 @@ -16316,6 +17063,8 @@ snapshots: cookie-es: 3.1.1 youch-core: 0.3.3 + zimmerframe@1.1.4: {} + zip-stream@6.0.1: dependencies: archiver-utils: 5.0.2 From 789ad77a62bc5d8b51db7502f7af1c9b24c55b0b Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Thu, 16 Jul 2026 11:55:50 +0530 Subject: [PATCH 22/31] docs: add sveltekit to AGENTS.md hierarchy and file layout --- AGENTS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 11bd555..c100c7c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -35,6 +35,7 @@ shared utils). Every other package depends on it, directly or transitively: - `@thunderid/nextjs` → `@thunderid/node` + `@thunderid/react`. - `@thunderid/nuxt` → `@thunderid/node` + `@thunderid/vue`. - `@thunderid/express` → `@thunderid/node`. +- `@thunderid/sveltekit` → `@thunderid/browser`. - `@thunderid/react-router`, `@thunderid/tanstack-router` → `@thunderid/react`. **Before adding a helper/util/constant to a framework package, check whether it belongs lower in this @@ -80,6 +81,7 @@ packages/ nextjs/ Next.js SDK (client + server) nuxt/ Nuxt module express/ Express middleware + sveltekit/ SvelteKit SDK (client + server) react-router/ React Router integration tanstack-router/ TanStack Router integration samples/ Standalone quickstart demo apps, one per framework From 9229e26aef9456512f41b380827e5d01b0ad8a0a Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Thu, 16 Jul 2026 11:56:08 +0530 Subject: [PATCH 23/31] chore: remove temporary working files --- plan.md | 46 -- sdk-spec.md | 1824 --------------------------------------------------- 2 files changed, 1870 deletions(-) delete mode 100644 plan.md delete mode 100644 sdk-spec.md diff --git a/plan.md b/plan.md deleted file mode 100644 index fb2bc3f..0000000 --- a/plan.md +++ /dev/null @@ -1,46 +0,0 @@ -# Refactor Plan: Split `@thunderid/svelte` → Core Lib + SvelteKit - -## Goal -Align with the spec's 4-layer architecture. Extract SvelteKit SSR code from `packages/svelte` into a new `packages/sveltekit` package. - -## Target Dependency Tree -``` -Browser SDK ──→ @thunderid/svelte (CSR Core Lib) ──→ @thunderid/sveltekit (SSR) - ↑ -Node SDK (re-exports JavaScript SDK) ────────────────────────┘ -``` - -## Phase 1: Strip `packages/svelte` to CSR-only - -- Remove `src/server/` directory (moves to sveltekit) -- Remove `src/api/` directory (moves to sveltekit) -- Remove `@thunderid/node` from dependencies -- Remove `@sveltejs/kit` from peerDependencies -- Replace `@thunderid/node` type imports with `@thunderid/browser` equivalents -- Strip SSR branches from `ThunderIDSvelteClient.ts` -- Remove `rehydrateSessionFromPayload()` method -- Update package.json exports (no `./server` subpath) -- Update tests - -## Phase 2: Create `packages/sveltekit` - -- Name: `@thunderid/sveltekit` -- Server code uses `ThunderIDJavaScriptClient` from `@thunderid/node` (no `_validateMethod()` issue, no browser deps) -- Imports shared types from `@thunderid/svelte` -- Dependencies: `@thunderid/svelte`, `@thunderid/node`, `jose` -- Peer dependencies: `@sveltejs/kit`, `svelte` -- Exports: `"."` (re-exports CSR SDK), `"./server"` (hooks, routes, session, guard, config) - -## Phase 3: Update sample apps - -- `apps/svelte-playground` → switch imports from `@thunderid/svelte/server` to `@thunderid/sveltekit/server` -- `apps/svelte-csr-playground` → no changes - -## Phase 4: Workspace registration - -- Add `packages/sveltekit` to `pnpm-workspace.yaml` - -## Phase 5: Build & test - -- `pnpm build`, `pnpm typecheck`, `pnpm lint`, `pnpm test` -- End-to-end test: SSR playground sign-in flow, CSR playground sign-in flow diff --git a/sdk-spec.md b/sdk-spec.md deleted file mode 100644 index 1cc9133..0000000 --- a/sdk-spec.md +++ /dev/null @@ -1,1824 +0,0 @@ -# ThunderID SDK Specification - -**Version:** 1.0.0-draft -**Status:** Draft -**Last Updated:** 2026-03-08 - ---- - -## Table of Contents - -1. [Introduction](#1-introduction) -2. [SDK Architecture](#2-sdk-architecture) - - 2.1 [Overview](#21-overview) - - 2.2 [The Four Layers](#22-the-four-layers) - - 2.3 [General Architecture Diagram](#23-general-architecture-diagram) - - 2.4 [Worked Example — JavaScript Ecosystem](#24-worked-example--javascript-ecosystem) - - 2.5 [Applying the Pattern to Other Ecosystems](#25-applying-the-pattern-to-other-ecosystems) - - 2.6 [Layer Rules](#26-layer-rules) - - 2.7 [Integration Packages](#27-integration-packages) -3. [Guiding Principles](#3-guiding-principles) -4. [Operational Modes](#4-operational-modes) -5. [SDK Initialization & Configuration](#5-sdk-initialization--configuration) - - 5.1 [Initialization](#51-initialization) - - 5.2 [Configuration Reference](#52-configuration-reference) - - 5.3 [Preferences](#53-preferences) -6. [Identity Lifecycle Operations](#6-identity-lifecycle-operations) - - 6.1 [Authentication (Sign-In)](#61-authentication-sign-in) - - 6.2 [Registration (Sign-Up)](#62-registration-sign-up) - - 6.3 [Account Recovery](#63-account-recovery) - - 6.4 [Session Management](#64-session-management) - - 6.5 [Token Management](#65-token-management) - - 6.6 [User Profile Management](#66-user-profile-management) - - 6.7 [Organization Management](#67-organization-management) -7. [Framework Integration](#7-framework-integration) - - 7.1 [Client Interface](#71-client-interface) - - 7.2 [Initialization Patterns](#72-initialization-patterns) - - 7.3 [Exposed API Patterns](#73-exposed-api-patterns) -8. [UI Components](#8-ui-components) - - 8.1 [Component-Driven IAM](#81-component-driven-iam) - - 8.2 [Component Categories](#82-component-categories) - - 8.3 [Component Architecture](#83-component-architecture) - - 8.4 [Component Reference](#84-component-reference) -9. [API Design & Method Signatures](#9-api-design--method-signatures) -10. [Error Handling](#10-error-handling) -11. [Security Requirements](#11-security-requirements) -12. [Platform & Language Guidelines](#12-platform--language-guidelines) -13. [Extensibility & Customization](#13-extensibility--customization) -14. [Compliance & Standards](#14-compliance--standards) -15. [SDK Layout](#15-sdk-layout) - - 15.5 [Integration List](#155-integration-list) -16. [Documentation Requirements](#16-documentation-requirements) -17. [Sample Applications](#17-sample-applications) -18. [Glossary](#18-glossary) - ---- - -## 1. Introduction - -This document defines the functional requirements and design principles for the ThunderID SDK suite. - -The goal of the SDKs is to provide a consistent, secure, and developer-friendly way for applications to integrate with ThunderID across multiple environments and programming languages. - -This specification defines the expected behavior of ThunderID SDKs, including supported identity lifecycle operations, authentication mechanisms, operational modes, API design conventions, error handling strategies, and security requirements. - -It serves as a reference for: -- **SDK implementors** building ThunderID SDKs for any language or platform -- **Application developers** integrating SDKs into their products -- **Third-party contributors** extending or auditing SDK behavior - -> **Note to implementors:** This specification is intentionally language-agnostic. Method signatures are expressed in pseudocode. Platform-specific implementation guides are maintained separately per language/runtime. - ---- - -## 2. SDK Architecture - -### 2.1 Overview - -The ThunderID SDK suite is organised into four distinct layers. Each layer builds on the one below it, inheriting its contracts and extending them with platform or framework-specific capabilities. **No layer may skip its immediate parent**. If a framework SDK can be built on top of a Platform SDK or Core Lib SDK, it must use that layer instead of directly inheriting from the Agnostic SDK. - -This layering ensures: -- Protocol logic (OAuth2/OIDC, token management, flow orchestration) is implemented once and shared across all higher layers -- Each layer has a strictly scoped responsibility and cannot bleed concerns upward or downward -- A security patch or bug fix in a lower layer propagates automatically to every SDK above it - ---- - -### 2.2 The Four Layers - -| Layer | Responsibility | Has UI | Example | -|-------|---------------|--------|---------| -| **Agnostic SDK** | Language-specific implementation of the full `ThunderIDClient` interface and all protocol logic. No platform, browser, or framework dependencies. | No | JavaScript SDK, Swift SDK, Python SDK | -| **Platform SDK** | Extends the Agnostic SDK with platform-specific capabilities: native storage, redirect/callback handling, platform crypto, and platform-native session management. | No | Browser SDK, Node.js SDK, iOS SDK, Android SDK | -| **Core Lib SDK** | Extends the Platform SDK with a framework's reactive primitives (state, lifecycle, context). Provides the single developer-facing entry point. UI components are optional at this layer. | Optional | React SDK, Vue SDK | -| **Framework Specific SDK** | Thin integration layer for opinionated full-stack or meta-frameworks. Adds SSR support, file-based routing conventions, and framework-specific initialisation helpers. Builds on a Core Lib SDK. | Optional | Next.js SDK, Nuxt SDK, Angular SDK | - ---- - -### 2.3 General Architecture Diagram - -```mermaid -graph TD - A["Agnostic SDK
ThunderIDClient interface · Protocol logic
JWT handling · Error model
No platform or framework deps"] - - B["Platform SDK
Native storage · Crypto
Redirect & callback handling
Platform session management"] - - C["Core Lib SDK
Reactive state · Lifecycle hooks
Single developer entry point
UI components (optional)"] - - D["Framework Specific SDK
SSR support · Routing conventions
Framework-native initialisation
Opinionated DX layer"] - - A --> B - B --> C - C --> D - - style A fill:#1a1a2e,color:#e0e0ff,stroke:#4a4a8a - style B fill:#16213e,color:#e0e0ff,stroke:#4a4a8a - style C fill:#0f3460,color:#e0e0ff,stroke:#4a4a8a - style D fill:#533483,color:#e0e0ff,stroke:#7a5ab5 -``` - ---- - -### 2.4 Worked Example — JavaScript Ecosystem - -The JavaScript ecosystem demonstrates all four layers concretely. The same pattern applies to any other language ecosystem. - -```mermaid -graph TD - JS["JavaScript SDK — Agnostic
Full ThunderIDClient · OAuth2/OIDC protocol
JWT decode & validation
No browser APIs · No framework deps"] - - BR["Browser SDK — Platform
Web Crypto API · PKCE
Web Worker token isolation
Redirect & callback handling"] - - NO["Node.js SDK — Platform
Server-side sessions · Cookie/Redis storage
Confidential client flows
No browser APIs"] - - RE["React SDK — Core Lib
ThunderIDProvider · useThunderID hook
UI component library
CSR + SSR-ready"] - - VU["Vue SDK — Core Lib
ThunderIDPlugin · useThunderID composable
UI component library"] - - NX["Next.js SDK — Framework Specific
App Router & Pages Router support
Server components · Route handlers
Builds on React SDK"] - - NU["Nuxt SDK — Framework Specific
Auto-imports · Nuxt plugin
SSR & SSG support
Builds on Vue SDK"] - - AN["Angular SDK — Framework Specific
ThunderIDModule · ThunderIDService
Standalone provider · DI-first
Builds on Browser SDK"] - - JS --> BR - JS --> NO - BR --> RE - BR --> VU - BR --> AN - RE --> NX - VU --> NU - - style JS fill:#1a1a2e,color:#e0e0ff,stroke:#4a4a8a - style BR fill:#16213e,color:#e0e0ff,stroke:#4a4a8a - style NO fill:#16213e,color:#e0e0ff,stroke:#4a4a8a - style RE fill:#0f3460,color:#e0e0ff,stroke:#4a4a8a - style VU fill:#0f3460,color:#e0e0ff,stroke:#4a4a8a - style NX fill:#533483,color:#e0e0ff,stroke:#7a5ab5 - style NU fill:#533483,color:#e0e0ff,stroke:#7a5ab5 - style AN fill:#533483,color:#e0e0ff,stroke:#7a5ab5 -``` - -> **Note on Angular:** Angular's strong dependency injection model means it integrates most naturally as a Framework Specific SDK directly on the Browser SDK, rather than on an intermediate Core Lib SDK. The same exception applies to any framework whose DI or module system makes an intermediate reactive layer redundant. - ---- - -### 2.5 Applying the Pattern to Other Ecosystems - -The same four-layer model applies to every supported ecosystem. Implementors MUST follow the layer definitions in Section 2.2 and document which layer each SDK occupies. - -| Ecosystem | Agnostic | Platform | Core Lib | Framework Specific | -| --------- | -------- | -------- | -------- | ------------------ | -| **JavaScript** | JavaScript SDK | Browser SDK, Node.js SDK | React SDK, Vue SDK | Angular SDK, Express SDK, Next.js SDK, Nuxt SDK, React Router SDK, TanStack Router SDK | -| **Mobile — Apple** | Swift SDK | iOS SDK | SwiftUI SDK | — | -| **Mobile — Android** | Kotlin SDK | Android SDK | Jetpack Compose SDK | — | -| **Cross-platform Mobile (Dart)** | — | iOS SDK + Android SDK *(via platform channels)* | Flutter SDK (Dart) | — | -| **Cross-platform Mobile (JS)** | JavaScript SDK | iOS SDK + Android SDK *(via native modules)* | React Native SDK | — | -| **Python** | Python SDK | — *(server-side, no platform layer needed)* | Django / FastAPI SDK | — | -| **Go** | Go SDK | — *(server-side, no platform layer needed)* | — | — | - -> **Flutter note:** Flutter does not have an Agnostic layer of its own. The Flutter SDK sits at the Core Lib layer and delegates all protocol operations to the iOS and Android Platform SDKs via platform channels. Flutter UI widgets are written in Dart on top of the bridged responses. -> -> **React Native note:** React Native follows the same cross-platform bridge pattern as Flutter. The React Native SDK sits at the Core Lib layer, uses the JavaScript SDK for protocol logic, and delegates platform-specific operations (secure storage, biometrics, redirect handling) to the iOS and Android Platform SDKs via native modules. - ---- - -### 2.6 Layer Rules - -The following rules MUST be observed by all SDK implementors: - -- A layer MUST depend on exactly one parent layer (except where platform channels bridge two Platform SDKs, as in Flutter) -- A layer MUST NOT re-implement logic already present in its parent layer -- A layer MUST NOT expose internal implementation details of its parent layer through its own public API -- Each SDK MUST declare its minimum required version of its parent SDK as a versioned dependency -- A breaking change in any layer requires a **major version bump** in that layer and all layers that depend on it -- All SDK layers MUST document which version of this specification they implement - ---- - -### 2.7 Integration Packages - -Integration packages are distinct from SDKs. An **integration** adapts ThunderID authentication into an existing third-party auth framework or ecosystem tool that has its own auth abstraction layer. Integrations are not full ThunderIDClient implementations — they are thin adapters that delegate protocol operations to a ThunderID SDK. - -**How integrations differ from SDKs:** - -| Concern | SDK | Integration | -| ------- | --- | ----------- | -| Implements `ThunderIDClient` | Yes | No | -| Has a layer in the four-layer hierarchy | Yes | No — sits outside the hierarchy | -| Depends on | Its parent SDK layer | A ThunderID Platform or Core Lib SDK | -| Implements | ThunderID protocol logic | The target framework's provider/strategy/plugin interface | -| Lives under | `tools/sdks/` | `tools/integrations/` | -| Release tag | `sdk//v*` | `integration//v*` | - -**When to build an integration instead of an SDK:** - -Build an integration when the target ecosystem already has a well-established auth abstraction (e.g., Auth.js providers, Passport strategies, Backstage auth plugins) and developers expect to use ThunderID through that abstraction rather than the ThunderID SDK API directly. - -**Integration contract:** - -An integration MUST: - -- Declare a versioned dependency on the ThunderID SDK it consumes (Platform or Core Lib layer) -- Implement the target framework's auth interface fully and correctly -- Not re-implement any OAuth2/OIDC logic — delegate all protocol operations to the ThunderID SDK -- Follow the target framework's own conventions for error handling, session management, and configuration -- Document which ThunderID SDK version and which target framework version it supports - ---- - -## 3. Guiding Principles - -All SDKs built under this specification MUST adhere to the following principles: - -| Principle | Description | -|-----------|-------------| -| **Secure by Default** | All operations must apply security best practices without requiring developer configuration. Insecure options must be explicitly opted into. | -| **Mode Agnosticism** | The public API surface must remain identical regardless of the underlying operational mode (Redirect-Based or App-Native). | -| **Minimal Surface Area** | Expose only what is necessary. Internals must not leak implementation details. | -| **Fail Safely** | On error, the SDK must leave the application in a safe, defined state. Partial state must never be silently persisted. | -| **Extensibility** | The SDK must allow customization of flows, UI hooks, and token storage without requiring a fork. | -| **Observability** | The SDK must emit structured logs and events for diagnostics, without logging sensitive data. | -| **Consistency** | Method naming, error shapes, and event conventions must be uniform across all language implementations. | -| **Component-Driven IAM** | For UI-capable platforms, identity operations MUST be expressible as drop-in UI components (e.g., ``, ``, ``), not only as imperative API calls. Components must be composable, styleable, and not require custom wiring for standard flows. | -| **Simple Public API** | Regardless of internal complexity, the API surface exposed to application developers MUST be a single cohesive entry point per framework (e.g., one hook, one service, one provider). Internal sub-providers or helpers are implementation details and MUST NOT be required by application code. | - ---- - -## 4. Operational Modes - -SDKs MUST support two primary operational modes. The mode is set at initialization and applies globally to all operations. - ---- - -### 4.1 Redirect-Based Mode (Standards-Based OAuth2/OIDC) - -The SDK orchestrates authentication by redirecting the user to the ThunderID authorization endpoint and handling the callback response. - -**When to use:** -- Web applications with a browser-based front-end -- Any scenario where the application can safely redirect and receive callbacks -- Scenarios requiring full standards compliance (OAuth 2.0, OIDC) - -**Key characteristics:** -- Uses Authorization Code Flow with PKCE (mandatory) -- The SDK handles PKCE code verifier generation, storage, and exchange -- Redirect URIs must be pre-registered in the ThunderID console -- No credentials are handled by the application directly - -**Flow overview:** - -``` -Application SDK ThunderID - | | | - |--signIn()---->| | - | |--authorize (PKCE)->| - | |<--redirect+code----| - | |--token exchange--->| - | |<--tokens-----------| - |<--AuthResult--| | -``` - ---- - -### 4.2 App-Native Mode (API-Driven) - -The SDK communicates directly with ThunderID APIs, keeping the user within the native application experience throughout the entire flow. This is ThunderID's implementation for fully embedded authentication. - -**When to use:** -- Native mobile applications (iOS, Android) -- Desktop applications -- Scenarios where browser redirects are undesirable or impossible - -**Key characteristics:** -- Fully API-driven; no browser redirects -- The SDK manages multi-step authentication state internally -- Supports progressive step-up authentication (e.g., password → MFA) -- The SDK MUST NOT store plaintext credentials at any point -- Requires explicit enablement in ThunderID server configuration - -**Flow overview:** - -``` -Application SDK ThunderID - | | | - |--signIn()---->| | - | |--initiate auth---->| - | |<--auth step--------| - | |--submit factor---->| - | |<--next step / OK---| - |<--AuthResult--| | -``` - ---- - -### 4.3 Mode Configuration - -The mode is inferred from a minimal configuration rather than declared explicitly. Providing a small set of additional options is enough to switch modes — for example, in the React SDK, supplying a `signInPath` tells the SDK that the app has its own login page, and the mode is automatically switched to **App-Native Login** without any further configuration. See [Section 4.2](#42-app-native-mode-api-driven) for the full configuration schema. - ---- - -## 5. SDK Initialization & Configuration - -### 5.1 Initialization - -The SDK MUST be initialized once before any operations are performed. Calling any operation before initialization MUST throw a `SDKNotInitializedException`. - -Initialization style varies by platform — some SDKs expose a constructor-based client, others use a provider/context wrapper. A singleton pattern is NOT required; each platform SDK SHOULD follow the idioms natural to its ecosystem. - -#### JavaScript / TypeScript - -```js -const client = new ThunderIDClient({ - baseUrl: "https://localhost:8090", - clientId: "your-client-id", - // ... -}); -``` - -#### React - -```jsx - - - -``` - -#### Mobile (iOS / Android) - -```swift -// Swift -let client = ThunderIDClient(config: ThunderIDConfig( - baseUrl: "https://localhost:8090", - clientId: "your-client-id" -)) -``` - -```kotlin -// Kotlin -val client = ThunderIDClient( - ThunderIDConfig( - baseUrl = "https://localhost:8090", - clientId = "your-client-id" - ) -) -``` - -**Implementor notes:** - -- Initialization MUST validate the `config` object and throw `InvalidConfigurationException` on invalid or missing required fields -- Platform SDKs using a provider/context pattern (e.g., React) MUST apply the same validation rules at the provider level -- SDKs MAY support multiple client instances (e.g., for multi-tenant scenarios or testing) unless the platform's architecture makes a managed single instance the clear convention - -### 5.2 Configuration Reference - -Each platform SDK MUST implement all required fields and SHOULD implement all optional fields. Fields not applicable on a given platform MUST be documented as such in the platform-specific implementation guide. - -**Core** - -| Field | Type | Required | Default | Notes | -| ------- | ------ | ---------- | --------- | ------- | -| `baseUrl` | String | **Yes** | — | Base URL of the ThunderID server. Must be HTTPS — HTTP MUST be rejected. e.g. `https://localhost:8090` | -| `clientId` | String | Conditional | — | OAuth2 client ID. Required for **OAuth/OIDC (Redirect)** mode. | - -**Redirect URIs** - -| Field | Type | Required | Default | Notes | -|-------|------|----------|---------|-------| -| `afterSignInUrl` | String | No | Framework default | Where to redirect after successful sign-in. Must be pre-registered in ThunderID. | -| `afterSignOutUrl` | String | No | Framework default | Where to redirect after sign-out. Must match an allowed post-logout URI in ThunderID. | -| `signInUrl` | String | No | ThunderID-hosted sign-in page | Override with a custom sign-in page URL | -| `signUpUrl` | String | No | ThunderID-hosted sign-up page | Override with a custom sign-up page URL | - -**OAuth2 / OIDC** - -| Field | Type | Required | Default | Notes | -|-------|------|----------|---------|-------| -| `scopes` | String \| List\ | No | `["openid"]` | Scopes to request. Accepts a space-separated string or a list. | -| `clientSecret` | String | No | — | Confidential clients only. MUST NOT be used in browser or public clients. | -| `signInOptions` | Map\ | No | `{}` | Extra parameters appended to the authorize request. e.g. `{ "prompt": "login", "fidp": "OrganizationSSO" }` | -| `signOutOptions` | Map\ | No | `{}` | Extra parameters appended to the sign-out request. e.g. `{ "idTokenHint": "" }` | -| `signUpOptions` | Map\ | No | `{}` | Extra parameters appended to the sign-up request. e.g. `{ "appId": "" }` | - -**Application Identity** - -| Field | Type | Required | Default | Notes | -|-------|------|----------|---------|-------| -| `applicationId` | String | No | — | UUID of the ThunderID application. Used for branding and sign-up URL resolution. | -| `organizationHandle` | String | No* | — | Organization identifier. *Required when a custom domain is configured. *(Not yet implemented.)* | - -**Token Security** - -| Field | Type | Required | Default | Notes | -|-------|------|----------|---------|-------| -| `allowedExternalUrls` | List\ | No | `[]` | Allowlist of base URLs the SDK may attach access tokens to. Applies only when storage type is `webWorker`. | -| `tokenValidation.idToken.validate` | Boolean | No | `true` | Whether to validate ID tokens. Set to `false` only for testing. | -| `tokenValidation.idToken.validateIssuer` | Boolean | No | `true` | Whether to validate the `iss` claim of the ID token. | -| `tokenValidation.idToken.clockTolerance` | Integer | No | `0` | Allowed clock skew in seconds when validating token expiry. | - -**Session** - -| Field | Type | Required | Default | Notes | -|-------|------|----------|---------|-------| -| `syncSession` | Boolean | No | `false` | Synchronize app session with the IdP session via OIDC iframe-based session management. **Warning:** may not work in all browsers due to third-party cookie restrictions. | - -**Storage & Platform** - -| Field | Type | Required | Default | Notes | -|-------|------|----------|---------|-------| -| `storage` | StorageAdapter | No | Platform default | Custom token/session storage implementation. See Section 11.1. | -| `platform` | PlatformHint | No | Auto-detected | Optional hint for SDK runtime optimization. | -| `instanceId` | Integer | No | — | Enables multiple independent authentication contexts within a single application. | - -**UI** - -| Field | Type | Required | Default | Notes | -|-------|------|----------|---------|-------| -| `preferences` | Preferences | No | — | Theme and i18n customization. Applies only to SDKs with bundled UI components. See Section 5.3. | - - - -### 5.3 Preferences - -The `preferences` object allows applications to customize UI components shipped with the SDK. It is optional and applies only to SDKs that include bundled UI components. SDKs that do not ship UI components MAY omit this field entirely and MUST document that omission. - -**Preferences (top-level)** - -| Field | Type | Default | Notes | -|-------|------|---------|-------| -| `theme` | ThemePreferences | — | Theme customization options | -| `i18n` | I18nPreferences | — | Internationalization options | -| `resolveFromMeta` | Boolean | `false` | Resolve theme from the server Flow Meta API (`GET /flow/meta`). Applicable only for the ThunderID platform. | - -**ThemePreferences** - -| Field | Type | Default | Notes | -|-------|------|---------|-------| -| `mode` | `"light"` \| `"dark"` \| `"system"` | `"system"` | Color scheme mode | -| `direction` | `"ltr"` \| `"rtl"` | `"ltr"` | Text direction | -| `inheritFromBranding` | Boolean | `false` | Inherit theme settings from ThunderID Branding configuration | -| `overrides` | ThemeConfig (partial) | — | Partial overrides of the default theme token set | - -**I18nPreferences** - -| Field | Type | Default | Notes | -|-------|------|---------|-------| -| `language` | String | — | Hard locale override (e.g. `"fr-FR"`). Bypasses URL param, stored preference, and browser language detection. | -| `fallbackLanguage` | String | `"en-US"` | Locale to use when translations are unavailable for the active language | -| `bundles` | Map\ | — | Custom translation bundles to override SDK defaults | -| `storageStrategy` | `"cookie"` \| `"localStorage"` \| `"none"` | `"cookie"` | How the user's language selection is persisted. `"cookie"` is the default to support cross-subdomain portal scenarios. | -| `storageKey` | String | `"thunder-i18n-language"` | Key name used when reading/writing the language to the chosen storage | -| `cookieDomain` | String | Root domain | Cookie domain scope. Override for eTLD+1 domains (e.g. `.co.uk`) or custom cookie scoping. | -| `urlParam` | String \| `false` | `"lang"` | URL query parameter name for locale override. Set to `false` to disable URL-based language detection. | - - - ---- - -## 6. Identity Lifecycle Operations - -All operations MUST be mode-agnostic at the API level — the developer calls the same method regardless of whether the SDK is in redirect or app-native mode. The SDK resolves the correct underlying flow internally. All operations are **asynchronous**. - ---- - -### 6.1 Authentication (Sign-In) - -#### Redirect Mode - -The SDK initiates the standard OAuth2 Authorization Code flow with PKCE ([RFC 6749](https://datatracker.ietf.org/doc/html/rfc6749), [RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636), [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html)). The user is redirected to the ThunderID sign-in page, authenticates, and is redirected back to `afterSignInUrl` with an authorization code. The SDK exchanges the code for tokens transparently. - -The `signIn()` method accepts an optional `SignInOptions` map of additional authorize request parameters (e.g. `prompt`, `fidp`, `loginHint`). On success it resolves with the authenticated `User` object. - -#### App-Native Mode - -App-native sign-in uses ThunderID's Flow Execution API. The SDK drives the full authentication flow over direct API calls without any browser redirect. - -**Endpoint:** `POST /flow/execute` - -**Initiation** — The SDK sends an initial request with the application identifier and flow type: - -```json -{ "applicationId": "", "flowType": "AUTHENTICATION" } -``` - -The server responds immediately with a `flowId`, `flowStatus` (`PROMPT_ONLY`), a `stepId`, a `type` (e.g. `VIEW`), and a `data` object describing what inputs or actions are required for this step. - -**Step handling** — The SDK submits subsequent requests with the `flowId`, the selected `actionId`, and any required `inputs`: - -```json -{ "flowId": "", "actionId": "basic_auth", "inputs": { "username": "...", "password": "..." } } -``` - -Each response contains an updated `flowStatus`. A status of `PROMPT_ONLY` means the flow has more steps; the `data` field contains the next step's inputs and/or available actions. - -**Completion** — When `flowStatus` is `COMPLETE`, the response contains an `assertion` field holding the JWT authentication token directly. No additional token exchange step is required. - -**Error** — When `flowStatus` is `ERROR`, the response contains a `failureReason` field. The SDK MUST surface this as an `AUTHENTICATION_FAILED` error. - -**MFA** — Multi-factor steps arrive as additional `PROMPT_ONLY` steps. The `data.actions` array lists the available authenticator actions. The SDK surfaces each step to the application via the registered MFA step handler (see Section 7.1 Client Interface), allowing the application to render the appropriate UI for OTP, TOTP, passkey, or magic link steps. - -#### Silent Sign-In - -`signInSilently()` attempts a passive authentication using `prompt=none` via a hidden iframe (redirect mode only). It resolves with the `User` object if an active IdP session exists, or `false` if not. Implementors MUST document that this may fail in browsers with strict third-party cookie policies. - -#### Authentication State - -The SDK MUST expose synchronous accessors for current authentication state: whether the user is authenticated, and the current user object if available. - ---- - -### 6.2 Registration (Sign-Up) - -#### Redirect Mode - -`signUp()` redirects the user to the ThunderID self-registration page. An optional `SignUpOptions` map of additional parameters can be passed (e.g. `appId`). - -#### App-Native Mode - -App-native sign-up uses the **Flow Execution API** (`POST /flow/execute`). This API is open and does not require an authorization header. - -**Initiation** — The SDK calls the execute endpoint with `flowType: "REGISTRATION"`. The server responds with a `flowId`, `flowStatus: "PROMPT_ONLY"`, and a `type` describing what the client should do next. The SDK drives the same step-handling loop as app-native sign-in. - -#### Social & Enterprise Sign-Up - -Social and enterprise provider sign-ups follow the same `signIn()` API. On first sign-in with a new provider, the server handles JIT provisioning automatically. The resolved `User` object includes an `isNewUser` flag indicating whether this was a first-time sign-up. - -#### Admin-Initiated / Invite-Only Registration - -Admin-initiated registration uses the `INVITED_USER_REGISTRATION` flow type via the Flow Execution API. The invited user begins the flow with their invitation code. The SDK drives the same step-handling loop as self-registration. - ---- - -### 6.3 Account Recovery - -Account recovery flows are also driven by the Flow Execution API using `flowType: "PASSWORD_RECOVERY"`. - -**Forgot Password** — Initiate with `flowType: "PASSWORD_RECOVERY"`. The flow collects the user's identifier, sends a recovery notification (email or SMS), collects the OTP or confirmation code, and sets a new password. Each step is handled as a `VIEW` response with the appropriate input fields. - -**Forgot Username** — Initiate a recovery flow with the user's known claims (e.g. email address). The server sends the username via the configured notification channel. - -The SDK MUST expose methods to initiate both recovery types and to confirm/complete each step. Recovery flows follow the same `flowId`-based step loop as registration. - ---- - -### 6.4 Session Management - -`signOut()` terminates the current session. It accepts an optional `SignOutOptions` map (e.g. `idTokenHint`). By default the SDK MUST revoke the refresh token server-side before clearing local state. - -The SDK MUST expose session event callbacks so the application can react to session expiry and token refresh events. - ---- - -### 6.5 Token Management - -The SDK MUST expose methods to retrieve the current token set and to access user identity claims. - -`getAccessToken()` returns the current access token string, refreshing it transparently if near expiry. - -`decodeJwtToken()` decodes a JWT payload without signature verification. It MUST NOT be used for authorization decisions. - -`getUserInfo()` fetches verified user claims from the `/userinfo` endpoint. - -`exchangeToken()` performs a token exchange (RFC 8693) for scenarios such as organization switching or impersonation (organization switching is not yet implemented). - -> **Security note:** Always use `getUserInfo()` or server-side token introspection for authorization decisions. Never rely on client-side JWT decoding. - ---- - -### 6.6 User Profile Management - -The SDK MUST provide methods to retrieve and update the authenticated user's profile attributes. - -`getUserProfile()` fetches the authenticated user's profile attributes from ID token claims. - -`updateUserProfile()` accepts a map of claim URIs to updated values and applies them server-side. - -`changePassword()` requires the current password and the desired new password. The user must be currently authenticated. - ---- - -### 6.7 Organization Management - -> **Not yet implemented.** Organization management is planned for a future release. SDKs MUST NOT implement these operations yet. This section is preserved as a specification target. - -SDKs MUST support organization-aware operations where the ThunderID server has multi-organization capabilities enabled. - -`getAllOrganizations()` returns all organizations accessible to the authenticated user, paginated. - -`getMyOrganizations()` returns the organizations the signed-in user is a member of. - -`getCurrentOrganization()` returns the organization the current session is scoped to, or `null` if the session is not org-scoped. - -`switchOrganization()` performs a token exchange to obtain an organization-scoped access token. The SDK MUST update stored tokens atomically after a successful switch. - -All organization methods accept an optional `sessionId` parameter to support multi-session scenarios. - - - ---- - -## 7. Framework Integration - -This section defines how SDKs for UI frameworks (React, Angular, Vue, SwiftUI, etc.) MUST structure their initialization and public API exposure. These rules apply only to framework-level SDKs; core/headless SDKs are governed by Section 5 alone. - ---- - -### 7.1 Client Interface - -All SDK implementations MUST provide a client interface that exposes a consistent set of core operations. The pseudocode below defines the canonical `ThunderIDClient` interface that all platform clients MUST implement or map to: - -``` -interface ThunderIDClient { - - // ── Lifecycle ───────────────────────────────────────────────────────────── - - initialize(config: TConfig, storage?: StorageAdapter) -> Promise - reInitialize(config: Partial) -> Promise - getConfiguration() -> TConfig - - // ── Authentication ──────────────────────────────────────────────────────── - - // Redirect-based sign-in - signIn(options?: SignInOptions, sessionId?: String, onSuccess?: (afterSignInUrl: String) -> Void) -> Promise - - // App-native (embedded) sign-in — overloaded signature - signIn(payload: EmbeddedSignInPayload, request: EmbeddedFlowRequestConfig, sessionId?: String, onSuccess?: (afterSignInUrl: String) -> Void) -> Promise - - // Silent sign-in (prompt=none via iframe — redirect mode only) - signInSilently(options?: SignInOptions) -> Promise - - signOut(options?: SignOutOptions, sessionId?: String, afterSignOut?: (afterSignOutUrl: String) -> Void) -> Promise - - isSignedIn(sessionId?: String) -> Promise - isLoading() -> Boolean - - // ── Registration ────────────────────────────────────────────────────────── - - // Redirect-based sign-up - signUp(options?: SignUpOptions) -> Promise - - // App-native (embedded) sign-up — overloaded signature - signUp(payload: EmbeddedFlowExecuteRequestPayload) -> Promise - - // ── Token & Session ─────────────────────────────────────────────────────── - - getAccessToken(sessionId?: String) -> Promise - decodeJwtToken(token: String) -> Promise - exchangeToken(config: TokenExchangeRequestConfig, sessionId?: String) -> Promise - setSession(sessionData: Map, sessionId?: String) -> Promise - clearSession(sessionId?: String) -> Void - - // ── User & Profile ──────────────────────────────────────────────────────── - - getUser(options?: Map) -> Promise - getUserProfile(options?: Map) -> Promise - updateUserProfile(payload: Map, userId?: String) -> Promise - - // ── Organizations (not yet implemented) ────────────────────────────────── - - getAllOrganizations(options?: Map, sessionId?: String) -> Promise - getMyOrganizations(options?: Map, sessionId?: String) -> Promise> - getCurrentOrganization(sessionId?: String) -> Promise - switchOrganization(organization: Organization, sessionId?: String) -> Promise -} -``` - -**Key design notes:** -- `signIn()` and `signUp()` are **overloaded** — one signature handles redirect mode, the other handles app-native/embedded mode. Platform SDKs MUST use the idiomatic overload mechanism of their language (method overloading, union types, discriminated payloads). -- `signInSilently()` uses a `prompt=none` passive authorization request via an iframe. Implementors MUST document that this may fail in browsers with strict third-party cookie policies. -- `reInitialize()` accepts a **partial** config to allow updating only specific fields (e.g., switching organization context) without full re-initialization. -- `isLoading()` is a **synchronous** method, unlike all other state queries. It reflects whether the SDK is mid-initialization or mid-token-refresh. - ---- - -### 7.2 Initialization Patterns - -Framework SDKs MUST integrate initialization natively into the framework's composition model. The initialization API MUST be placed as close as possible to the application root so that all child components and services have access to the authenticated context. - -The following patterns MUST be followed per framework type: - -**React — Provider at root:** -```typescript -// ✅ REQUIRED: Wrap application root with ThunderIDProvider - - - - -// Internal implementation may use multiple providers -// but these MUST NOT be required in application code -``` - -**Angular — Module or standalone provider:** -```typescript -// ✅ REQUIRED: Register as application-level provider -// NgModule style -@NgModule({ - imports: [ThunderIDModule.forRoot(config)] -}) -export class AppModule {} - -// Standalone style -bootstrapApplication(AppComponent, { - providers: [provideThunderID(config)] -}) -``` - -**Vue — Plugin:** -```typescript -// ✅ REQUIRED: Install as application plugin -const app = createApp(App) -app.use(ThunderIDPlugin, config) -app.mount('#app') -``` - -**SwiftUI — Environment modifier:** -```swift -// ✅ REQUIRED: Inject into environment at root view -WindowGroup { - ContentView() - .thunderIDProvider(config: config) -} -``` - -**Rules for all framework SDKs:** -- Initialization MUST happen exactly once at the application root — never inside child components or views -- The config object MUST be validated at initialization time; invalid configs MUST throw/reject before the application renders -- Framework SDKs MAY support hot-reinitialization via `reInitialize()` for dynamic config changes (e.g., organization switching) without requiring a full page reload - ---- - -### 7.3 Exposed API Patterns - -The public API exposed to application developers MUST be a **single cohesive entry point** per framework. Regardless of internal complexity, developers should import and use one thing. - -**React — Single hook:** -```typescript -// ✅ ONE hook exposes everything -const { - signIn, - signOut, - signUp, - isSignedIn, - isLoading, - user, - getUserProfile, - updateUserProfile, - getAllOrganizations, - switchOrganization, -} = useThunderID(); - -// ❌ Application code MUST NOT need to call multiple hooks for standard flows -// Internal hooks (e.g., useThunderContext, useTokenManager) are implementation details -``` - -**Angular — Single injectable service:** -```typescript -// ✅ ONE service injected wherever needed -@Injectable() -export class MyComponent { - constructor(private thunder: ThunderIDService) {} - - signIn() { - this.thunder.signIn(); - } -} -``` - -**Vue — Single composable:** -```typescript -// ✅ ONE composable -const { signIn, signOut, isSignedIn, user } = useThunderID() -``` - -**What the single entry point MUST expose:** - -| Category | Methods / Properties | -|----------|---------------------| -| Authentication state | `isSignedIn`, `isLoading`, `user` | -| Auth actions | `signIn()`, `signOut()`, `signInSilently()` | -| Registration | `signUp()` | -| Token | `getAccessToken()`, `exchangeToken()` | -| Profile | `getUserProfile()`, `updateUserProfile()` | -| Organizations | `getAllOrganizations()`, `getMyOrganizations()`, `getCurrentOrganization()`, `switchOrganization()` *(not yet implemented)* | -| Lifecycle | `reInitialize()` | - ---- - -## 8. UI Components - -### 8.1 Component-Driven IAM - -Framework SDKs that target UI platforms MUST provide a library of ready-to-use UI components that encapsulate common identity flows. These components allow developers to add authentication, registration, and identity management to their applications without writing custom UI or wiring logic. - -**Requirements:** -- Components MUST work out of the box without additional configuration beyond what is provided to the root provider -- Components MUST be styleable and themeable through the `preferences.theme` config (see Section 5.3) -- Components MUST be accessible (WCAG 2.1 AA minimum) -- Components MUST support i18n via the `preferences.i18n` config -- Components SHOULD support a `Base*` variant (unstyled) alongside the default styled variant to allow complete style overrides without forking the component - ---- - -### 8.2 Component Categories - -Components are organized into four categories based on their responsibility: - -| Category | Purpose | Examples | -|----------|---------|---------| -| **Actions** | Trigger identity operations (buttons) | `SignInButton`, `SignOutButton`, `SignUpButton` | -| **Auth Flow** | Handle auth callbacks and redirects | `Callback` | -| **Control / Guard** | Conditionally render content based on auth state | `SignedIn`, `SignedOut`, `Loading` | -| **Presentation** | Display identity-related data and management UIs | `UserProfile`, `UserDropdown`, `OrganizationSwitcher` *(not yet implemented)*, `SignIn`, `SignUp` | - ---- - -### 8.3 Component Architecture - -Each non-trivial component MUST be implemented as a two-layer architecture: - -``` -Base → Unstyled, logic-only component - → Styled component (wraps Base with default theme) -``` - -This pattern allows: -- Developers who want the default look to use `` directly -- Developers who want full style control to use ` />` and supply their own styles -- Internal reuse of logic without duplicating behavior - -**Example (React):** -```typescript -// BaseSignInButton — unstyled, accepts className/style/children - - Sign In - - -// SignInButton — styled default, zero configuration needed - -``` - ---- - -### 8.4 Component Reference - -The following components MUST be provided by any framework SDK that includes a UI layer. The React SDK is used as the reference implementation; other frameworks MUST provide equivalent components adapted to their platform conventions. - -#### Actions - -| Component | Description | Props / Inputs | -|-----------|-------------|---------------| -| `SignInButton` | Triggers the sign-in flow on click. Handles redirect or embedded mode transparently. | `options?: SignInOptions`, `onSuccess?`, `onError?`, standard button props | -| `SignOutButton` | Triggers the sign-out flow on click. | `options?: SignOutOptions`, `onSuccess?`, `onError?`, standard button props | -| `SignUpButton` | Triggers the sign-up flow on click. | `options?: SignUpOptions`, `onSuccess?`, `onError?`, standard button props | - -Each action component MUST have a corresponding `Base*` unstyled variant. - -#### Auth Flow - -| Component | Description | Props / Inputs | -|-----------|-------------|---------------| -| `Callback` | Handles the OAuth2 redirect callback. MUST be rendered at the `afterSignInUrl` route. Exchanges the authorization code for tokens and redirects to the post-login destination. | `onSuccess?`, `onError?`, `loadingComponent?` | - -#### Control / Guard - -| Component | Description | Props / Inputs | -|-----------|-------------|---------------| -| `SignedIn` | Renders its children only when a user is authenticated. Renders nothing (or a fallback) otherwise. | `children`, `fallback?` | -| `SignedOut` | Renders its children only when no user is authenticated. | `children`, `fallback?` | -| `Loading` | Renders its children (or a spinner) while the SDK is initializing or loading auth state. | `children?`, `indicator?` | - -**Usage pattern (React):** -```typescript - - - - - - - - -``` - -#### Presentation - -**Authentication UI:** - -| Component | Description | Notes | -|-----------|-------------|-------| -| `SignIn` | Full sign-in form UI. Supports all configured authenticators, MFA steps, and social sign-in. | Renders server-driven flow in app-native mode | -| `SignUp` | Full self-registration form UI. Dynamically renders fields based on server-reported required claims. | See `getRegistrationRequirements()` | -| `AcceptInvite` | UI for accepting an admin-sent invitation and completing account setup. | Requires an `invitationCode` param | -| `InviteUser` | UI for admins to invite a new user by email. | Requires admin privileges | - -**User Management UI:** - -| Component | Description | Notes | -|-----------|-------------|-------| -| `User` | Displays the authenticated user's basic info (name, avatar). | Read-only | -| `UserDropdown` | A dropdown/menu showing the user's avatar and actions (profile, sign out). | Includes `SignOutButton` internally | -| `UserProfile` | Full editable profile management UI (view and update claims, change password). | Calls `getUserProfile()` and `updateUserProfile()` | - -**Organization UI** *(not yet implemented)* - -| Component | Description | Notes | -|-----------|-------------|-------| -| `Organization` | Displays the current organization's name and metadata. | Read-only | -| `OrganizationList` | Displays a list of organizations available to the user. | Calls `getMyOrganizations()` | -| `OrganizationProfile` | Displays and optionally edits the current organization's details. | May require admin privileges | -| `OrganizationSwitcher` | Dropdown UI to switch between accessible organizations. Triggers `switchOrganization()` on selection. | Commonly placed in nav/header | -| `CreateOrganization` | Form UI for creating a new sub-organization. | Requires appropriate permissions | - -**Other:** - -| Component | Description | Notes | -|-----------|-------------|-------| -| `LanguageSwitcher` | UI control to switch the application language. Persists selection via `preferences.i18n.storageStrategy`. | Adapts to configured i18n storage | - ---- - -**Base variant availability:** - -All presentation components that involve non-trivial layout MUST provide a `Base*` unstyled variant: - -| Styled Component | Unstyled Variant | -|-----------------|-----------------| -| `SignIn` | `BaseSignIn` | -| `SignUp` | `BaseSignUp` | -| `AcceptInvite` | `BaseAcceptInvite` | -| `InviteUser` | `BaseInviteUser` | -| `User` | `BaseUser` | -| `UserDropdown` | `BaseUserDropdown` | -| `UserProfile` | `BaseUserProfile` | -| `Organization` | `BaseOrganization` *(not yet implemented)* | -| `OrganizationList` | `BaseOrganizationList` *(not yet implemented)* | -| `OrganizationProfile` | `BaseOrganizationProfile` *(not yet implemented)* | -| `OrganizationSwitcher` | `BaseOrganizationSwitcher` *(not yet implemented)* | -| `CreateOrganization` | `BaseCreateOrganization` *(not yet implemented)* | -| `LanguageSwitcher` | `BaseLanguageSwitcher` | - ---- - -## 9. API Design & Method Signatures - -### 9.1 Naming Conventions - -All SDKs MUST follow these naming conventions, adapted to the idiomatic style of the target language (e.g., camelCase for JS/Java, snake_case for Python): - -| Operation Category | Method Prefix | -|-------------------|---------------| -| Authentication | `signIn`, `signOut` | -| Registration | `signUp`, `completeRegistration` | -| Recovery | `initiate*Recovery`, `confirm*Recovery` | -| Session | `refreshSession`, `getSession` | -| Token | `getTokens`, `decodeIDToken`, `getUserInfo` | -| Profile | `getUser*`, `updateUser*`, `changePassword` | -| Lifecycle | `initialize`, `reset` | - -**Options types (`SignInOptions`, `SignOutOptions`, `SignUpOptions`)** MUST be implemented as open, extensible map/record types rather than closed structs. This allows arbitrary server-specific parameters to be passed without requiring SDK changes. Typed convenience properties (e.g., `prompt`, `loginHint`) MAY be layered on top as named fields where the platform idiom supports it. - -``` -// Pseudocode — open record -SignInOptions = Map - -// Platform implementations: -// TypeScript: Record -// Java: Map -// Swift: [String: Any] -// Python: dict[str, Any] -``` - -### 9.2 Async Contract - -All operations that involve network I/O MUST be asynchronous. SDKs MUST use the idiomatic async pattern for the target platform: - -| Platform | Pattern | -|----------|---------| -| JavaScript/TypeScript | `Promise` / `async-await` | -| Java/Android | `CompletableFuture` or callback with `Result` | -| Swift/iOS | `async throws` + `Result` | -| Python | `async def` returning `Awaitable[T]` | -| Other | Equivalent async/callback pattern idiomatic to the language | - -### 9.3 Input Validation - -The SDK MUST validate all inputs before making any network call: -- Required fields must be present and non-empty -- Email fields must match a valid email format -- Password fields must meet the server-reported `PasswordPolicy` -- Invalid inputs MUST throw `InvalidInputException` synchronously (not as a rejected promise) - -### 9.4 Idempotency - -- `signOut()` called when no session exists MUST succeed silently (no error) -- `initialize()` called more than once MUST throw `AlreadyInitializedException` unless `reset()` was called first -- `refreshSession()` called concurrently MUST deduplicate requests; only one refresh call MUST be in flight at a time - ---- - -## 10. Error Handling - -### 10.1 Error Model - -All SDK errors MUST conform to the following structure: - -``` -IAMError { - code: ErrorCode // Machine-readable code (see 7.2) - message: String // Human-readable description (English) - cause: Error? // Underlying platform/network error, if any - requestId: String? // ThunderID server request trace ID, if available - statusCode: Integer? // HTTP status code, if applicable -} -``` - -### 10.2 Error Codes - -#### Configuration Errors - -| Code | Trigger | -|------|---------| -| `SDK_NOT_INITIALIZED` | Any operation called before `initialize()` | -| `ALREADY_INITIALIZED` | `initialize()` called after already initialized | -| `INVALID_CONFIGURATION` | Missing or invalid config fields | -| `INVALID_REDIRECT_URI` | Redirect URI not registered or malformed | - -#### Authentication Errors - -| Code | Trigger | -|------|---------| -| `AUTHENTICATION_FAILED` | Credentials incorrect or authentication rejected | -| `USER_ACCOUNT_LOCKED` | Account locked due to failed attempts | -| `USER_ACCOUNT_DISABLED` | Account has been deactivated | -| `SESSION_EXPIRED` | Session or token has expired | -| `MFA_REQUIRED` | MFA step is required to proceed | -| `MFA_FAILED` | MFA code was invalid or expired | -| `INVALID_GRANT` | Authorization code or refresh token is invalid/expired | -| `CONSENT_REQUIRED` | User must provide consent before proceeding | - -#### Registration Errors - -| Code | Trigger | -|------|---------| -| `USER_ALREADY_EXISTS` | Username or email already registered | -| `INVALID_INPUT` | Claim validation failed (e.g., weak password, invalid email) | -| `INVITATION_CODE_INVALID` | Invite code not found | -| `INVITATION_CODE_EXPIRED` | Invite code has passed its expiry | -| `REGISTRATION_DISABLED` | Self-registration is disabled on the server | - -#### Recovery Errors - -| Code | Trigger | -|------|---------| -| `RECOVERY_FAILED` | User not found or recovery not possible | -| `CONFIRMATION_CODE_INVALID` | OTP or confirmation code is wrong | -| `CONFIRMATION_CODE_EXPIRED` | OTP or confirmation code has expired | - -#### Network & Server Errors - -| Code | Trigger | -|------|---------| -| `NETWORK_ERROR` | No connectivity or DNS failure | -| `REQUEST_TIMEOUT` | Request exceeded configured timeout | -| `SERVER_ERROR` | ThunderID returned 5xx | -| `UNKNOWN_ERROR` | Unexpected error with no known classification | - -### 10.3 Error Handling Conventions - -- All errors MUST be catchable via the platform's standard error-handling mechanism (try/catch, `.catch()`, `Result` type, etc.) -- The SDK MUST NOT swallow errors silently -- Network retries MUST NOT be performed automatically, except for token refresh (see 5.4) -- Error messages MUST NOT include sensitive data (passwords, tokens, PII) -- The `requestId` field MUST be populated whenever a `Correlation-ID` or `X-Request-ID` header is present in the server response, to aid debugging - -### 10.4 Example Error Handling (Pseudocode) - -``` -try { - result = await client.signIn({ authenticator: BASIC, loginHint: "user@example.com" }) - handleSuccess(result) -} catch (error: IAMError) { - switch error.code { - case AUTHENTICATION_FAILED: - showInvalidCredentialsMessage() - case MFA_REQUIRED: - showMFAPrompt() - case USER_ACCOUNT_LOCKED: - showAccountLockedMessage(error.message) - case NETWORK_ERROR: - showOfflineMessage() - default: - logError(error.requestId, error.message) - showGenericError() - } -} -``` - ---- - -## 11. Security Requirements - -### 11.1 Token Storage - -The SDK MUST store tokens using the most secure storage mechanism available on the target platform. Implementors MUST use the following defaults: - -| Platform | Default Storage | -|----------|----------------| -| iOS | Keychain Services | -| Android | EncryptedSharedPreferences / Keystore | -| Web (Browser) | In-memory only. `localStorage` and `sessionStorage` MUST NOT be the default. | -| Web (Worker) | `webWorker` storage isolates tokens in a Web Worker, preventing main-thread access | -| Desktop (Electron, etc.) | OS credential store via keytar or equivalent | -| Server-side / CLI | Environment variable or OS keyring; never plain files | - -The SDK MUST expose a `StorageAdapter` interface to allow applications to provide a custom storage backend: - -``` -StorageAdapter { - store(key: String, value: String) -> Void - retrieve(key: String) -> String? - delete(key: String) -> Void - clear() -> Void -} -``` - -**`allowedExternalUrls` and token attachment:** -When the storage type is `webWorker` (or equivalent isolated storage), the SDK proxies outbound HTTP requests through the worker so that the main thread never has direct access to tokens. In this mode, the SDK MUST enforce an `allowedExternalUrls` allowlist: -- The access token MUST only be attached to requests whose URL starts with one of the configured base URLs -- Requests to URLs not on the allowlist MUST be rejected with `UNAUTHORIZED_REQUEST` -- Each entry in `allowedExternalUrls` MUST be a base URL without a trailing slash (e.g., `"https://api.example.com"`) - -### 11.2 PKCE (Proof Key for Code Exchange) - -In redirect mode, PKCE (RFC 7636) is **mandatory** and MUST be enforced by the SDK regardless of server configuration. The SDK MUST: -- Generate a cryptographically random `code_verifier` of at least 43 characters -- Derive `code_challenge` using `S256` (SHA-256). Plain method MUST NOT be used -- Store the `code_verifier` only in memory (never persisted to disk or `localStorage`) -- Clear the `code_verifier` immediately after the token exchange completes - -### 11.3 State Parameter (CSRF Protection) - -In redirect mode, the SDK MUST generate a cryptographically random `state` parameter for every authorization request and validate it on callback. Mismatched state MUST result in `AUTHENTICATION_FAILED` with a descriptive message. - -### 11.4 Token Validation - -Upon receiving an ID token, the SDK MUST: -1. Verify the JWT signature using the server's JWKS endpoint -2. Validate the `iss` claim matches the configured `baseUrl` -3. Validate the `aud` claim contains the configured `clientId` -4. Validate the `exp` claim (token must not be expired) -5. Validate the `nonce` claim if one was included in the authorization request - -> **Implementor note:** JWKS keys should be cached with appropriate TTL (recommended: match the `Cache-Control` header from the JWKS endpoint, minimum 5 minutes). The SDK MUST support key rotation by re-fetching JWKS on signature verification failure before returning an error. - -### 11.5 Credential Handling - -- The SDK MUST NOT log credentials (passwords, OTP codes, tokens) at any log level -- In app-native mode, credentials MUST be submitted immediately to the server and MUST NOT be stored in memory beyond the duration of the API call -- The SDK MUST use HTTPS for all communications. HTTP MUST be rejected (throw `INVALID_CONFIGURATION`) -- Certificate pinning SHOULD be supported as an optional configuration - -### 11.6 Sensitive Data in Logs - -The SDK MUST apply the following log-sanitization rules regardless of log level: - -- Access tokens: mask entirely (log only token type and expiry) -- Refresh tokens: never log -- Passwords and OTPs: never log -- Full email addresses: mask domain (e.g., `j***@***.com`) -- Phone numbers: mask except last 4 digits - -### 11.7 Session Security - -- Access tokens MUST be automatically refreshed before expiry (recommended: 60 seconds before `exp`) -- Refresh tokens MUST be rotated on use; the SDK MUST update stored tokens atomically -- On sign-out, the SDK MUST revoke the refresh token via the server's revocation endpoint (RFC 7009) before clearing local state - ---- - -## 12. Platform & Language Guidelines - -This specification is language-agnostic. The following table provides guidance to implementors on idiomatic adaptations. No single language implementation is canonical — each MUST follow the spec while conforming to the idioms of its target platform. - -| Concern | Guidance | -|---------|----------| -| **Naming style** | Follow the language convention: `camelCase` (JS/TS, Java, Swift, Kotlin), `snake_case` (Python, Rust), `PascalCase` for all type names universally | -| **Async pattern** | Use the platform's native async idiom. See Section 9.1. | -| **Error types** | Map `IAMError` to the platform's base exception/error type with a subclass or discriminated union hierarchy where idiomatic | -| **Null safety** | Respect the language's null safety model; optional fields use `Optional`, `T?`, `T \| undefined`, or equivalent | -| **Enums** | Implement `AuthenticatorType`, `ErrorCode`, etc. as typed enums or sealed classes — never raw strings | -| **Builder pattern** | For complex option objects (`SignInOptions`, `SDKConfig`), prefer builder/fluent patterns in Java/Kotlin | -| **Dependency injection** | SDKs SHOULD support DI-friendly initialization in frameworks where DI is standard (e.g., Spring, SwiftUI, Angular) | -| **Thread safety** | All public methods MUST be safe to call from any thread; internal state MUST be appropriately synchronized | -| **Framework integration** | Web/UI framework SDKs (React, Vue, Angular, SwiftUI) SHOULD provide framework-native primitives (hooks, providers, composables) in addition to the core imperative API | -| **Minimum versions** | Each platform SDK MUST document its minimum supported language/runtime version | - ---- - -### 12.1 Async Pattern Per Platform - -| Platform | Idiomatic Async Pattern | -|----------|------------------------| -| JavaScript / TypeScript | `Promise` + `async/await` | -| Java | `CompletableFuture` or callback with `Result` | -| Kotlin / Android | `suspend fun` returning `T` (coroutines) | -| Swift / iOS | `async throws` + `Result` | -| Python | `async def` returning `Awaitable[T]` | -| Dart / Flutter | `Future` | -| Other | Equivalent async/callback pattern idiomatic to the language | - ---- - -### 12.2 Method Signature Adaptation - -The pseudocode in this spec: - -``` -signIn(options: SignInOptions?) -> Promise -``` - -Maps to the following idiomatic signatures per platform: - -| Platform | Idiomatic Signature | -|----------|---------------------| -| TypeScript | `signIn(options?: SignInOptions): Promise` | -| Java | `CompletableFuture signIn(@Nullable SignInOptions options)` | -| Kotlin | `suspend fun signIn(options: SignInOptions? = null): AuthResult` | -| Swift | `func signIn(options: SignInOptions?) async throws -> AuthResult` | -| Python | `async def sign_in(options: SignInOptions \| None = None) -> AuthResult` | -| Dart | `Future signIn({SignInOptions? options})` | - ---- - -### 12.3 Configuration Adaptation - -The canonical `SDKConfig` schema (Section 4.2) maps to each platform's idiomatic config mechanism. Below is a reference showing how the React SDK implements the canonical config as a TypeScript interface: - -**React SDK (TypeScript) — Reference Implementation** - -```typescript -// BaseConfig is generic over the storage type T -interface BaseConfig { - // ── Core ────────────────────────────────────────────────────────────────── - baseUrl: string | undefined; - clientId?: string | undefined; - clientSecret?: string | undefined; // Confidential clients only - - // ── Redirect URIs ───────────────────────────────────────────────────────── - afterSignInUrl?: string | undefined; - afterSignOutUrl?: string | undefined; - signInUrl?: string | undefined; - signUpUrl?: string | undefined; - - // ── OAuth2 ──────────────────────────────────────────────────────────────── - scopes?: string | string[] | undefined; - signInOptions?: SignInOptions; // Record - signOutOptions?: SignOutOptions; // Record - signUpOptions?: SignUpOptions; // Record - - // ── Application Identity ────────────────────────────────────────────────── - applicationId?: string | undefined; - organizationHandle?: string | undefined; // Not yet implemented - - // ── Token Security ──────────────────────────────────────────────────────── - allowedExternalUrls?: string[]; - tokenValidation?: { - idToken?: { - validate?: boolean; - validateIssuer?: boolean; - clockTolerance?: number; - }; - }; - - // ── Session ─────────────────────────────────────────────────────────────── - syncSession?: boolean; - - // ── Storage & Platform ──────────────────────────────────────────────────── - storage?: T; - platform?: keyof typeof Platform; - instanceId?: number; - - // ── UI Preferences ──────────────────────────────────────────────────────── - preferences?: Preferences; -} -``` - -> **Notes on the React SDK implementation:** -> - `clientId` is typed as optional at the `BaseConfig` level but enforced as required by the framework layer at runtime -> - `storage` is generic (`T`) — the concrete storage types (`sessionStorage`, `webWorker`, `localStorage`) are defined at the React framework layer, not in the base config -> - `signInOptions`, `signOutOptions`, and `signUpOptions` are typed as open `Record` types to accommodate arbitrary server-specific parameters without requiring SDK changes -> - `preferences` is a ThunderID/UI-SDK-specific field; non-UI SDKs MAY omit it - -**Mapping to other platforms:** - -| Config Field (Spec) | React/TS | Java/Android | Swift/iOS | Python | -|---------------------|----------|--------------|-----------|--------| -| `baseUrl` | `string \| undefined` | `String` (non-null) | `String` | `str` | -| `scopes` | `string \| string[]` | `List` | `[String]` | `list[str] \| str` | -| `signInOptions` | `Record` | `Map` | `[String: Any]` | `dict[str, Any]` | -| `storage` | Generic `T` (framework-defined) | `StorageAdapter` interface | `StorageAdapter` protocol | `StorageAdapter` ABC | -| `preferences` | `Preferences` object | N/A (no bundled UI) | N/A (no bundled UI) | N/A (no bundled UI) | - ---- - -### 12.4 Framework-Native Integration Patterns - -Framework-native initialization and API exposure patterns — including provider, hook, service, and composable conventions — are fully specified in **Section 7: Framework Integration**. That section also includes code examples for React, Angular, Vue, SwiftUI, Android/Kotlin, and iOS/Swift. - -For language-specific type and signature adaptations, see Sections 12.1–12.3 above. - ---- - -## 13. Extensibility & Customization - -### 13.1 Custom Storage - -Implement `StorageAdapter` (see Section 10.1) and pass it in `SDKConfig.storage`. - -### 13.2 Custom Logger - -``` -LoggerAdapter { - debug(message: String, context: Map?) -> Void - info(message: String, context: Map?) -> Void - warn(message: String, context: Map?) -> Void - error(message: String, error: Error?, context: Map?) -> Void -} -``` - -The default logger is a no-op. Implementors MUST ensure the logger interface is called with sanitized data (see Section 11.6) before passing to any user-provided adapter. - -### 13.3 Custom HTTP Client - -The SDK SHOULD expose an `HTTPAdapter` interface to allow replacement of the default HTTP client (e.g., for proxy support, custom TLS configuration, or testing): - -``` -HTTPAdapter { - request(method: String, url: String, headers: Map, body: Any?) -> Promise -} - -HTTPResponse { - statusCode: Integer - headers: Map - body: String -} -``` - -### 13.4 Event Hooks - -The SDK MUST expose an event system for application-level observability: - -``` -// Subscribe to SDK lifecycle events -ThunderIDClient.on(event: SDKEvent, handler: (EventPayload) -> Void) - -SDKEvent enum { - SIGN_IN_SUCCESS - SIGN_IN_FAILED - SIGN_OUT - TOKEN_REFRESHED - TOKEN_REFRESH_FAILED - SESSION_EXPIRED - MFA_STEP_REQUIRED -} -``` - ---- - -## 14. Compliance & Standards - -All SDK implementations MUST comply with or support the following standards and specifications: - -| Standard | Relevance | -|----------|-----------| -| OAuth 2.0 (RFC 6749) | Core authorization framework | -| PKCE (RFC 7636) | Mandatory for redirect mode | -| OIDC Core 1.0 | ID token issuance and validation | -| JWT (RFC 7519) | Token format | -| JWKS (RFC 7517) | Public key retrieval for token validation | -| Token Revocation (RFC 7009) | Sign-out token revocation | -| TOTP (RFC 6238) | Time-based OTP MFA | -| FIDO2 / WebAuthn | Passkey authentication | -| SAML 2.0 | Enterprise federation | - ---- - -## 15. SDK Layout - -This section defines how to organize SDKs within the ThunderID monorepo (`asgardeo/thunder`). All SDKs — regardless of language or platform — live under `tools/sdks/` in this repository. - ---- - -### 15.0 Directory Structure - -Every SDK occupies a named subdirectory under `tools/sdks/`. Integration packages live separately under `tools/integrations/`. Both directories use the same flat layout — the directory is the publishable package root with no `code/` subdirectory: - -```text -tools/sdks/ -└── / # SDK library source — the publishable package (flat layout) - -tools/integrations/ -└── / # Integration package source — the publishable package (flat layout) -``` - -Sample applications live separately under `samples/apps/` (see [Section 17](#17-sample-applications)). - ---- - -### 15.1 SDK List - -All SDKs live under `tools/sdks/` in the `asgardeo/thunder` monorepo. - -| SDK | Ecosystem | Layer | Location | -| --- | --------- | ----- | -------- | -| `javascript` | JavaScript | Agnostic | [`tools/sdks/javascript`](https://github.com/asgardeo/thunder/tree/main/tools/sdks/javascript) | -| `browser` | JavaScript | Platform | [`tools/sdks/browser`](https://github.com/asgardeo/thunder/tree/main/tools/sdks/browser) | -| `node` | JavaScript | Platform | [`tools/sdks/node`](https://github.com/asgardeo/thunder/tree/main/tools/sdks/node) | -| `react` | JavaScript | Core Lib | [`tools/sdks/react`](https://github.com/asgardeo/thunder/tree/main/tools/sdks/react) | -| `vue` | JavaScript | Core Lib | [`tools/sdks/vue`](https://github.com/asgardeo/thunder/tree/main/tools/sdks/vue) | -| `angular` | JavaScript | Framework Specific | [`tools/sdks/angular`](https://github.com/asgardeo/thunder/tree/main/tools/sdks/angular) | -| `express` | JavaScript | Framework Specific | [`tools/sdks/express`](https://github.com/asgardeo/thunder/tree/main/tools/sdks/express) | -| `nextjs` | JavaScript | Framework Specific | [`tools/sdks/nextjs`](https://github.com/asgardeo/thunder/tree/main/tools/sdks/nextjs) | -| `nuxt` | JavaScript | Framework Specific | [`tools/sdks/nuxt`](https://github.com/asgardeo/thunder/tree/main/tools/sdks/nuxt) | -| `react-router` | JavaScript | Framework Specific | [`tools/sdks/react-router`](https://github.com/asgardeo/thunder/tree/main/tools/sdks/react-router) | -| `tanstack-router` | JavaScript | Framework Specific | [`tools/sdks/tanstack-router`](https://github.com/asgardeo/thunder/tree/main/tools/sdks/tanstack-router) | -| `react-native` | JavaScript | Core Lib | [`tools/sdks/react-native`](https://github.com/asgardeo/thunder/tree/main/tools/sdks/react-native) | -| `ios` | Swift | Platform | [`tools/sdks/ios`](https://github.com/asgardeo/thunder/tree/main/tools/sdks/ios) | -| `swiftui` | Swift | Core Lib | [`tools/sdks/swiftui`](https://github.com/asgardeo/thunder/tree/main/tools/sdks/swiftui) | -| `android` | Kotlin | Platform | [`tools/sdks/android`](https://github.com/asgardeo/thunder/tree/main/tools/sdks/android) | -| `compose` | Kotlin | Core Lib | [`tools/sdks/compose`](https://github.com/asgardeo/thunder/tree/main/tools/sdks/compose) | -| `flutter` | Dart | Core Lib | [`tools/sdks/flutter`](https://github.com/asgardeo/thunder/tree/main/tools/sdks/flutter) | -| `python` | Python | Agnostic | [`tools/sdks/python`](https://github.com/asgardeo/thunder/tree/main/tools/sdks/python) | -| `go` | Go | Agnostic | [`tools/sdks/go`](https://github.com/asgardeo/thunder/tree/main/tools/sdks/go) | - -> **Router SDKs:** `react-router` and `tanstack-router` sit at the Framework Specific layer. They build on top of the React Core Lib SDK and add router-specific concerns such as protected routes, callback route handling, and navigation guards. -> -> **Angular SDK:** Angular's strong dependency injection model means it integrates most naturally as a Framework Specific SDK directly on the Browser SDK, rather than on an intermediate Core Lib SDK (see §2.4). -> -> **React Native SDK:** Sits at the Core Lib layer. Uses the JavaScript SDK for protocol logic and delegates platform-specific operations to the iOS and Android Platform SDKs via native modules (see §2.5). - -**Rules:** - -- Every SDK package is independently versioned and published to its ecosystem's package registry. -- Packages reference each other via local workspace paths during development. -- No package may skip a dependency layer (see [Section 2.6](#26-layer-rules)). - ---- - -### 15.2 Naming Convention - -Repository and package names SHOULD follow the conventions natural to each ecosystem. -Ecosystem-specific package naming rules take precedence over a unified cross-language pattern. - -| Ecosystem | Package / Module Name | Convention rationale | -| ----------------------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------ | -| JavaScript / TypeScript | `@thunderid/*` | npm scope-based packages. Multiple SDK packages can live inside a monorepo (e.g., `@thunderid/react`, `@thunderid/browser`). | -| iOS (Swift) | `ThunderID` | Swift Package Manager libraries typically use PascalCase module names. | -| SwiftUI (Swift) | `ThunderIDSwiftUI` | Separate SPM product from the iOS Platform SDK. UI components only; depends on `ThunderID`. | -| Android (Kotlin) | `io.thunderid.*` | Java/Kotlin libraries follow reverse-domain naming (e.g., `io.thunderid.android`). | -| Compose (Kotlin) | `io.thunderid.compose` | Separate Gradle library from the Android Platform SDK. UI components only; depends on `io.thunderid:android`. | -| Flutter (Dart) | `thunderid_flutter` | Dart packages use `snake_case` with underscores separating words. | -| React Native (JS/TS) | `@thunderid/react-native` | Same npm scope as other JS packages. Follows React Native community naming conventions. | -| Angular (JS/TS) | `@thunderid/angular` | Same npm scope as other JS packages. | -| Python | `thunderid` | Python packages are typically lowercase with optional underscores if needed. | -| Go | `github.com/asgardeo/thunderid` | Go modules follow repository import paths rather than separate package registries. | - ---- - -### 15.3 SDK Checklist - -Every new SDK (`tools/sdks//`) must include the following before the first public release: - -- [ ] `tools/sdks//` — SDK library source with `README.md`, build configuration, and test suite -- [ ] `tools/sdks//README.md` — installation, quick-start, and link to this specification -- [ ] Sample application in `samples/apps/` (see [Section 17](#17-sample-applications)) -- [ ] CI — lint, build, and test passing on every pull request (see [Section 15.4](#154-cicd-pipelines)) -- [ ] Release pipeline — automated publish to the appropriate package registry on tag push (see [Section 15.4](#154-cicd-pipelines)) - ---- - -### 15.4 CI/CD Pipelines - -All SDK CI and release automation is wired into the existing monorepo pipelines in `.github/workflows/` and `.github/actions/`. - ---- - -#### Composite Actions - -Each SDK MUST have a composite action at `.github/actions/-sdk/action.yml`. The action encapsulates the full CI sequence for that SDK: - -1. Set up the SDK's toolchain (language runtime, package manager) -2. Install dependencies -3. Lint -4. Build -5. Test - -This single action is the contract between the SDK and the CI system. Both the PR builder and the release workflow call it — keeping the two pipelines in sync automatically. - -``` -.github/actions/ -└── -sdk/ - └── action.yml # composite action: setup → install → lint → build → test -``` - -**Example invocation:** - -```yaml -- name: 🧩 Build & Test SDK - uses: ./.github/actions/-sdk -``` - ---- - -#### PR Builder Integration - -When an SDK is added, a new job MUST be added to the existing `.github/workflows/pr-builder.yml`. The job: - -- Uses `dorny/paths-filter` (already in the workflow) to gate on changes under `tools/sdks//` -- Calls `./.github/actions/-sdk` when the path filter matches - ---- - -#### Release Pipeline - -Add a workflow at `.github/workflows/sdk-release.yml` that triggers on tags matching `sdk//v*`. - -**Tag convention:** `sdk//v` — for example, `sdk/react/v0.1.0` releases `@thunderid/react@0.1.0`. - -The workflow MUST: - -1. Extract the SDK name and version from the tag -2. Call `./.github/actions/-sdk` to lint, build, and test -3. Publish to the appropriate package registry: - -| Ecosystem | Registry | Auth secret | -| --------- | -------- | ----------- | -| JavaScript / TypeScript | npmjs.com | `NPM_TOKEN` | -| iOS (Swift) | Swift Package Index / GitHub Releases | `GITHUB_TOKEN` | -| Android (Kotlin) | Maven Central | `MAVEN_GPG_KEY`, `MAVEN_USERNAME`, `MAVEN_PASSWORD` | -| Flutter (Dart) | pub.dev | `PUB_CREDENTIALS` | -| Python | PyPI | `PYPI_TOKEN` | - -4. Create a GitHub Release scoped to the SDK (title: ` v`) - ---- - -### 15.5 Integration List - -All integration packages live under `tools/integrations/` in the `asgardeo/thunder` monorepo. See [Section 2.7](#27-integration-packages) for how integrations differ from SDKs. - -| Integration | Target Framework | Consumes SDK | Location | -| ----------- | ---------------- | ------------ | -------- | -| `authjs` | [Auth.js](https://authjs.dev) (Next-Auth v5+) | Node.js SDK | [`tools/integrations/authjs`](https://github.com/asgardeo/thunder/tree/main/tools/integrations/authjs) | -| `nuxtauth` | [NuxtAuth](https://sidebase.io/nuxt-auth) | Node.js SDK | [`tools/integrations/nuxtauth`](https://github.com/asgardeo/thunder/tree/main/tools/integrations/nuxtauth) | -| `passport` | [Passport.js](https://www.passportjs.org) | Node.js SDK | [`tools/integrations/passport`](https://github.com/asgardeo/thunder/tree/main/tools/integrations/passport) | -| `backstage` | [Backstage](https://backstage.io) | Node.js SDK | [`tools/integrations/backstage`](https://github.com/asgardeo/thunder/tree/main/tools/integrations/backstage) | - -**Naming convention:** - -| Ecosystem | Package name | Example | -| --------- | ------------ | ------- | -| JavaScript / TypeScript | `@thunderid/integration-` | `@thunderid/integration-authjs` | - -**Release tag convention:** `integration//v` — for example, `integration/authjs/v0.1.0`. - -**CI/CD:** Each integration follows the same composite action pattern as SDKs (§15.4). The composite action lives at `.github/actions/-integration/action.yml` and is called from both the PR builder and the release workflow. - ---- - -## 16. Documentation Requirements - -Every SDK release MUST be accompanied by documentation published to the ThunderID docs site. Documentation is not optional — an SDK without docs MUST NOT be considered shippable. - ---- - -### 16.0 ThunderID Docs Repository - -All SDK documentation — quickstart guides, complete guides, and API reference — is authored and maintained in the `docs/content/` directory of the [`asgardeo/thunder`](https://github.com/asgardeo/thunder) repository. Contributions are submitted as pull requests to that repository. - -**File locations within `asgardeo/thunder`:** - -```text -docs/content/ -├── guides/ -│ ├── quick-start/ -│ │ └── / # Quickstart guide per SDK -│ └── ... -└── sdks/ - └── / # API reference directory per SDK -``` - ---- - -### 16.1 Quickstart Guide - -Each SDK MUST have a quickstart guide that takes a developer from zero to a working sign-in flow in the shortest path possible. The quickstart MUST: - -- Target a specific framework or runtime (one quickstart per SDK package) -- Cover installation, initialization, and a minimal sign-in / sign-out integration -- Include working code snippets that can be copy-pasted directly -- Be authored under `docs/content/guides/quick-start//` in `asgardeo/thunder` -- Be published to the ThunderID docs site under a consistent URL pattern: - - `https://thunderidentity.org/docs/quick-starts//` - -**Reference examples:** - -- Web (React): [React Quickstart](https://brionmario.github.io/thunder-sdks/docs/next/guides/quick-start/connect-your-application/react) -- Mobile (Flutter): [Flutter Quickstart](https://brionmario.github.io/thunder-sdks/docs/next/guides/quick-start/connect-your-application/flutter) - -Nav entry pattern (added to the sidebar under `Get started > Connect App`): - -```yaml -- : - - Quickstart: guides/quick-start//index.md - - Complete Guide: guides/complete-guides//introduction.md -``` - ---- - -### 16.2 API Reference - -Each SDK MUST publish generated API reference documentation. The reference MUST cover every public method, type, and configuration option exposed by the SDK. - -API reference pages are authored under `docs/content/sdks//` in `asgardeo/thunder`. The directory structure mirrors the public API surface: - -```text -docs/content/sdks// -├── overview.md -├── client.md # ThunderIDClient — all methods and properties -├── configuration.md # Config fields -├── models.md # Public types (User, Organization, TokenResponse, etc.) -└── guides/ - ├── redirect-auth.md - ├── app-native-auth.md - └── token-management.md -``` - -Nav entry pattern (added to the sidebar under `SDK Documentation`): - -```yaml -- : - - Overview: sdks//overview.md - - APIs: - - : sdks//client.md - ... - - Guides: - - ...: sdks//guides/... -``` - -Published SDK references are indexed at the ThunderID docs site under `sdks/`. - -**Reference examples:** - -- Web (React): [React SDK Reference](https://brionmario.github.io/thunder-sdks/docs/next/sdks/react/overview) -- Mobile (Flutter): [Flutter SDK Reference](https://brionmario.github.io/thunder-sdks/docs/next/sdks/flutter/overview) - ---- - -## 17. Sample Applications - -Every SDK that targets an application developer (i.e. anything at the Core Lib or Framework Specific layer) MUST include at least one runnable sample application in the repository. Samples are first-class deliverables — an SDK MUST NOT be considered shippable without them. - ---- - -### 17.1 Philosophy - -A sample is the fastest proof that an SDK actually works end-to-end. It serves three purposes: - -1. **Validation** — the sample is run in CI against a real (or mock) ThunderID instance, catching integration regressions before release. -2. **Developer onboarding** — a developer can clone, configure, and run the sample in under five minutes to see the SDK working before writing a line of their own code. -3. **Specification compliance** — the sample demonstrates the canonical happy path prescribed by this specification, not a workaround or internal shortcut. - ---- - -### 17.2 Location & Structure - -Samples live under `samples/apps/` in the monorepo, one directory per sample. Each sample is a self-contained, runnable project with its own `package.json` (or equivalent) and `README.md`. - -```text -samples/apps/ -├── -b2c/ # e.g. ios-b2c, react-b2c -│ ├── package.json # (or equivalent build file) -│ ├── README.md -│ └── .env.example -└── -b2b/ # e.g. ios-b2b - └── ... -``` - -Sample directory names follow the pattern `-` (e.g. `ios-b2c`, `react-b2c`, `express-protected`). - -**Rules:** - -- Each sample MUST be independently installable (`npm install` / equivalent) without touching workspace root dependencies. -- Each sample MUST have its own `README.md` with: prerequisites, environment variable setup, run instructions, and a short description of what it demonstrates. -- Samples MUST NOT hardcode credentials or server URLs. All connection details MUST be supplied via environment variables (`.env.example` checked in; `.env` gitignored). - ---- - -### 17.3 Required Samples per SDK - -The table below lists the minimum required sample for each SDK. Additional samples (e.g. B2B, MFA, organization switching) are encouraged but not mandatory for the first release. - -| SDK | Sample name | What it demonstrates | -| --- | ----------- | -------------------- | -| **React SDK** | `react-b2c` | B2C single-page app: sign-in with redirect, display authenticated user's profile (name, email, avatar), sign-out. Includes a protected route that redirects unauthenticated users to sign-in. | -| **Vue SDK** | `vue-b2c` | Same scope as `react-b2c`, implemented with Vue 3 and the Vue SDK composable. | -| **Next.js SDK** | `nextjs-b2c` | B2C app using the App Router: a public home page, a sign-in flow, and a protected `/profile` server component that reads the session server-side. | -| **Nuxt SDK** | `nuxt-b2c` | B2C app equivalent to `nextjs-b2c`, implemented with Nuxt 3. | -| **Angular SDK** | `angular-b2c` | B2C single-page app with Angular routing: sign-in, profile page guarded by `AuthGuard`, sign-out. | -| **Express SDK** | `express-protected` | Minimal Express server with two routes: a public `/health` endpoint and a protected `/api/me` endpoint that returns the authenticated user's claims. Requests without a valid bearer token receive `401`. | -| **Node.js SDK** | `node-protected` | Same as `express-protected` but using the Node.js SDK directly (no Express), demonstrating SDK use in a plain HTTP server or serverless handler. | -| **React Router SDK** | `react-router-b2c` | B2C app with React Router v7: public and protected routes, callback route, and user profile page. | -| **TanStack Router SDK** | `tanstack-router-b2c` | B2C app with TanStack Router: same scope as `react-router-b2c`. | -| **SwiftUI SDK** | `ios-b2c` | Native iOS app (SwiftUI): sign-in sheet, user profile view showing claims, sign-out. | -| **Compose SDK** | `android-b2c` | Native Android app (Jetpack Compose): sign-in screen, profile screen, sign-out. | -| **Flutter SDK** | `flutter-b2c` | Cross-platform Flutter app: sign-in, user profile screen, sign-out, running on iOS and Android. | -| **Python / Django SDK** | `django-protected` | Django app with a public index view and a protected `/profile` view that requires an active session. | -| **Python / FastAPI SDK** | `fastapi-protected` | FastAPI app with a public root endpoint and a protected `/me` endpoint secured with a bearer token dependency. | - ---- - -### 17.4 Sample Quality Standards - -Every sample MUST meet the following minimum bar before the SDK is considered shippable: - -- [ ] Runs successfully against a local ThunderID instance using only the `.env.example` variables. -- [ ] Demonstrates the happy path without requiring any code changes — only environment variable configuration. -- [ ] Uses the public SDK API exclusively. No internal imports, no monkey-patching, no workarounds. -- [ ] Has no known security issues: no hardcoded secrets, no `dangerouslyAllowBrowser`-style flags enabled in production code, no disabled PKCE or token validation. -- [ ] CI runs the sample build (and, where practical, a headless integration test) on every pull request. - ---- - -### 17.5 B2C Reference Flow (Minimum Viable Sample) - -The following flow defines the minimum a B2C sample MUST demonstrate. Framework-specific samples may expand on this but MUST NOT do less. - -```text -1. User visits the app — unauthenticated state is shown (e.g. "Sign In" button or redirect to sign-in page) -2. User clicks sign-in → SDK initiates the authentication flow -3. User completes sign-in at the ThunderID sign-in page -4. User is redirected back to the app — authenticated state is shown -5. App displays: display name, email address, and profile picture (or initials fallback) -6. User clicks sign-out → session is terminated, user returns to unauthenticated state -``` - -For server-side SDKs (Express, Node.js, Django, FastAPI), replace steps 1–6 with: - -```text -1. Client sends GET /public → 200 OK (no auth required) -2. Client sends GET /protected without token → 401 Unauthorized -3. Client sends GET /protected with valid Bearer token → 200 OK with user claims JSON -``` - ---- - -## 18. Glossary - -| Term | Definition | -|------|------------| -| **IAM** | Identity and Access Management | -| **OIDC** | OpenID Connect — an identity layer on top of OAuth 2.0 | -| **PKCE** | Proof Key for Code Exchange — a security extension for OAuth 2.0 (RFC 7636) | -| **JWKS** | JSON Web Key Set — a set of public keys used to verify JWTs | -| **MFA** | Multi-Factor Authentication | -| **TOTP** | Time-Based One-Time Password (RFC 6238) | -| **JIT** | Just-In-Time provisioning — account creation at first sign-in | -| **SAML** | Security Assertion Markup Language — an XML-based standard for SSO | -| **App-Native Mode** | ThunderID's API-driven authentication flow with no browser redirects | -| **Redirect Mode** | Standard OAuth2/OIDC flow using browser redirects | -| **Claim** | A key-value pair representing a user attribute (e.g., `email`, `given_name`) | -| **StorageAdapter** | An interface for custom token persistence backends | -| **AuthResult** | The result returned upon successful authentication, containing tokens and user profile | -| **allowedExternalUrls** | An allowlist of base URLs to which the SDK may attach access tokens in outbound requests. Enforced in `webWorker` storage mode. | -| **webWorker storage** | A browser storage strategy that isolates tokens inside a Web Worker, preventing main-thread JavaScript from accessing them directly | -| **organizationHandle** | A string identifier for a ThunderID organization; required when a custom domain is configured | -| **applicationId** | The UUID of the registered ThunderID application; used for branding resolution and sign-up flow | -| **syncSession** | An optional feature that synchronizes the application session with the IdP session via OIDC iframe-based session management | -| **clockTolerance** | The allowed clock skew (in seconds) when validating the `exp` and `iat` claims of an ID token | -| **instanceId** | An optional integer that enables multiple independent authentication contexts within a single application instance | -| **Preferences** | An optional config block for UI customization (theme, i18n) available in SDKs that ship bundled UI components | -| **ThunderIDClient** | The canonical client interface all SDK implementations must fulfill, defining all core authentication, session, token, profile, and organization operations | -| **EmbeddedFlow** | The app-native, API-driven authentication flow; referred to as "embedded" in the client interface to distinguish it from redirect-based flows | -| **signIn** | The standard public API term for initiating authentication. Never `login`. | -| **signOut** | The standard public API term for terminating a session. Never `logout`. | -| **signUp** | The standard public API term for initiating registration. Never `register`. | -| **signInSilently** | A passive authentication attempt using `prompt=none` sent from an iframe; succeeds only if an active session exists at the IdP | -| **ThunderIDProvider** | The React-specific root provider component that initializes the SDK and makes the authentication context available to the component tree | -| **useThunderID** | The single hook (React) or composable (Vue) that exposes the full SDK API to any component within the provider tree | -| **Base\* component** | An unstyled variant of a UI component (e.g., `BaseSignIn`) that provides logic without any default styling, enabling full visual customization | -| **Control component** | A component that renders children conditionally based on authentication state (e.g., `SignedIn`, `SignedOut`, `Loading`) | -| **Callback component** | A component rendered at the OAuth2 redirect callback URL that handles code exchange and post-sign-in redirection | -| **OrganizationSwitcher** | A UI component that lists accessible organizations and triggers `switchOrganization()` when a user selects one | -| **Core SDK** | The language-agnostic specification layer that all platform SDKs implement against; contains no runtime code | -| **JavaScript SDK** | The JS/TS runtime implementation of `ThunderIDClient`; contains all OAuth2/OIDC protocol logic; has no browser or framework dependencies | -| **Browser SDK** | Extends the JavaScript SDK with browser-specific APIs (Web Crypto, Web Worker, redirect handling); the base for all web framework SDKs | -| **Platform Channel** | A Flutter mechanism for calling native iOS (Swift) or Android (Kotlin) code from Dart; used by the Flutter SDK to delegate protocol operations to the native SDKs | -| **Layer** | A single SDK in the dependency tree; each layer builds on exactly one parent and MUST NOT skip levels | - ---- - -*ThunderID SDK Specification — v1.0.0-draft* -*This document is intended for SDK implementors and external development partners.* From 1dee8154c846a86b2949fb2dcce26275b639d893 Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Thu, 16 Jul 2026 13:27:16 +0530 Subject: [PATCH 24/31] refactor: move sample app to samples/sveltekit to match upstream convention --- pnpm-workspace.yaml | 2 +- samples/{apps/sveltekit-b2c => sveltekit}/.env.example | 0 samples/{apps/sveltekit-b2c => sveltekit}/.gitignore | 0 samples/{apps/sveltekit-b2c => sveltekit}/.npmrc | 0 samples/{apps/sveltekit-b2c => sveltekit}/.prettierignore | 0 samples/{apps/sveltekit-b2c => sveltekit}/.prettierrc | 0 .../{apps/sveltekit-b2c => sveltekit}/.vscode/extensions.json | 0 samples/{apps/sveltekit-b2c => sveltekit}/README.md | 0 samples/{apps/sveltekit-b2c => sveltekit}/package.json | 0 samples/{apps/sveltekit-b2c => sveltekit}/src/app.d.ts | 0 samples/{apps/sveltekit-b2c => sveltekit}/src/app.html | 0 samples/{apps/sveltekit-b2c => sveltekit}/src/hooks.server.ts | 0 .../{apps/sveltekit-b2c => sveltekit}/src/lib/AppShell.svelte | 0 .../sveltekit-b2c => sveltekit}/src/lib/assets/favicon.svg | 0 samples/{apps/sveltekit-b2c => sveltekit}/src/lib/index.ts | 0 .../sveltekit-b2c => sveltekit}/src/routes/+layout.server.ts | 0 .../{apps/sveltekit-b2c => sveltekit}/src/routes/+layout.svelte | 0 .../{apps/sveltekit-b2c => sveltekit}/src/routes/+page.svelte | 0 .../src/routes/api/auth/callback/+server.ts | 0 .../src/routes/api/auth/signin/+server.ts | 0 .../src/routes/api/auth/signout/+server.ts | 0 .../src/routes/callback/+page.svelte | 0 .../src/routes/protected/+page.server.ts | 0 .../src/routes/protected/+page.svelte | 0 samples/{apps/sveltekit-b2c => sveltekit}/static/robots.txt | 0 samples/{apps/sveltekit-b2c => sveltekit}/tsconfig.json | 0 samples/{apps/sveltekit-b2c => sveltekit}/vite.config.ts | 0 27 files changed, 1 insertion(+), 1 deletion(-) rename samples/{apps/sveltekit-b2c => sveltekit}/.env.example (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/.gitignore (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/.npmrc (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/.prettierignore (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/.prettierrc (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/.vscode/extensions.json (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/README.md (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/package.json (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/src/app.d.ts (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/src/app.html (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/src/hooks.server.ts (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/src/lib/AppShell.svelte (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/src/lib/assets/favicon.svg (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/src/lib/index.ts (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/src/routes/+layout.server.ts (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/src/routes/+layout.svelte (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/src/routes/+page.svelte (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/src/routes/api/auth/callback/+server.ts (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/src/routes/api/auth/signin/+server.ts (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/src/routes/api/auth/signout/+server.ts (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/src/routes/callback/+page.svelte (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/src/routes/protected/+page.server.ts (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/src/routes/protected/+page.svelte (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/static/robots.txt (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/tsconfig.json (100%) rename samples/{apps/sveltekit-b2c => sveltekit}/vite.config.ts (100%) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index df44214..80ca3d6 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,6 +1,6 @@ packages: - packages/* - - samples/*/* + - samples/* minimumReleaseAgeExclude: # JUSTIFICATION: Temporary exclusion due to recent release. TODO: Remove after the time passes. diff --git a/samples/apps/sveltekit-b2c/.env.example b/samples/sveltekit/.env.example similarity index 100% rename from samples/apps/sveltekit-b2c/.env.example rename to samples/sveltekit/.env.example diff --git a/samples/apps/sveltekit-b2c/.gitignore b/samples/sveltekit/.gitignore similarity index 100% rename from samples/apps/sveltekit-b2c/.gitignore rename to samples/sveltekit/.gitignore diff --git a/samples/apps/sveltekit-b2c/.npmrc b/samples/sveltekit/.npmrc similarity index 100% rename from samples/apps/sveltekit-b2c/.npmrc rename to samples/sveltekit/.npmrc diff --git a/samples/apps/sveltekit-b2c/.prettierignore b/samples/sveltekit/.prettierignore similarity index 100% rename from samples/apps/sveltekit-b2c/.prettierignore rename to samples/sveltekit/.prettierignore diff --git a/samples/apps/sveltekit-b2c/.prettierrc b/samples/sveltekit/.prettierrc similarity index 100% rename from samples/apps/sveltekit-b2c/.prettierrc rename to samples/sveltekit/.prettierrc diff --git a/samples/apps/sveltekit-b2c/.vscode/extensions.json b/samples/sveltekit/.vscode/extensions.json similarity index 100% rename from samples/apps/sveltekit-b2c/.vscode/extensions.json rename to samples/sveltekit/.vscode/extensions.json diff --git a/samples/apps/sveltekit-b2c/README.md b/samples/sveltekit/README.md similarity index 100% rename from samples/apps/sveltekit-b2c/README.md rename to samples/sveltekit/README.md diff --git a/samples/apps/sveltekit-b2c/package.json b/samples/sveltekit/package.json similarity index 100% rename from samples/apps/sveltekit-b2c/package.json rename to samples/sveltekit/package.json diff --git a/samples/apps/sveltekit-b2c/src/app.d.ts b/samples/sveltekit/src/app.d.ts similarity index 100% rename from samples/apps/sveltekit-b2c/src/app.d.ts rename to samples/sveltekit/src/app.d.ts diff --git a/samples/apps/sveltekit-b2c/src/app.html b/samples/sveltekit/src/app.html similarity index 100% rename from samples/apps/sveltekit-b2c/src/app.html rename to samples/sveltekit/src/app.html diff --git a/samples/apps/sveltekit-b2c/src/hooks.server.ts b/samples/sveltekit/src/hooks.server.ts similarity index 100% rename from samples/apps/sveltekit-b2c/src/hooks.server.ts rename to samples/sveltekit/src/hooks.server.ts diff --git a/samples/apps/sveltekit-b2c/src/lib/AppShell.svelte b/samples/sveltekit/src/lib/AppShell.svelte similarity index 100% rename from samples/apps/sveltekit-b2c/src/lib/AppShell.svelte rename to samples/sveltekit/src/lib/AppShell.svelte diff --git a/samples/apps/sveltekit-b2c/src/lib/assets/favicon.svg b/samples/sveltekit/src/lib/assets/favicon.svg similarity index 100% rename from samples/apps/sveltekit-b2c/src/lib/assets/favicon.svg rename to samples/sveltekit/src/lib/assets/favicon.svg diff --git a/samples/apps/sveltekit-b2c/src/lib/index.ts b/samples/sveltekit/src/lib/index.ts similarity index 100% rename from samples/apps/sveltekit-b2c/src/lib/index.ts rename to samples/sveltekit/src/lib/index.ts diff --git a/samples/apps/sveltekit-b2c/src/routes/+layout.server.ts b/samples/sveltekit/src/routes/+layout.server.ts similarity index 100% rename from samples/apps/sveltekit-b2c/src/routes/+layout.server.ts rename to samples/sveltekit/src/routes/+layout.server.ts diff --git a/samples/apps/sveltekit-b2c/src/routes/+layout.svelte b/samples/sveltekit/src/routes/+layout.svelte similarity index 100% rename from samples/apps/sveltekit-b2c/src/routes/+layout.svelte rename to samples/sveltekit/src/routes/+layout.svelte diff --git a/samples/apps/sveltekit-b2c/src/routes/+page.svelte b/samples/sveltekit/src/routes/+page.svelte similarity index 100% rename from samples/apps/sveltekit-b2c/src/routes/+page.svelte rename to samples/sveltekit/src/routes/+page.svelte diff --git a/samples/apps/sveltekit-b2c/src/routes/api/auth/callback/+server.ts b/samples/sveltekit/src/routes/api/auth/callback/+server.ts similarity index 100% rename from samples/apps/sveltekit-b2c/src/routes/api/auth/callback/+server.ts rename to samples/sveltekit/src/routes/api/auth/callback/+server.ts diff --git a/samples/apps/sveltekit-b2c/src/routes/api/auth/signin/+server.ts b/samples/sveltekit/src/routes/api/auth/signin/+server.ts similarity index 100% rename from samples/apps/sveltekit-b2c/src/routes/api/auth/signin/+server.ts rename to samples/sveltekit/src/routes/api/auth/signin/+server.ts diff --git a/samples/apps/sveltekit-b2c/src/routes/api/auth/signout/+server.ts b/samples/sveltekit/src/routes/api/auth/signout/+server.ts similarity index 100% rename from samples/apps/sveltekit-b2c/src/routes/api/auth/signout/+server.ts rename to samples/sveltekit/src/routes/api/auth/signout/+server.ts diff --git a/samples/apps/sveltekit-b2c/src/routes/callback/+page.svelte b/samples/sveltekit/src/routes/callback/+page.svelte similarity index 100% rename from samples/apps/sveltekit-b2c/src/routes/callback/+page.svelte rename to samples/sveltekit/src/routes/callback/+page.svelte diff --git a/samples/apps/sveltekit-b2c/src/routes/protected/+page.server.ts b/samples/sveltekit/src/routes/protected/+page.server.ts similarity index 100% rename from samples/apps/sveltekit-b2c/src/routes/protected/+page.server.ts rename to samples/sveltekit/src/routes/protected/+page.server.ts diff --git a/samples/apps/sveltekit-b2c/src/routes/protected/+page.svelte b/samples/sveltekit/src/routes/protected/+page.svelte similarity index 100% rename from samples/apps/sveltekit-b2c/src/routes/protected/+page.svelte rename to samples/sveltekit/src/routes/protected/+page.svelte diff --git a/samples/apps/sveltekit-b2c/static/robots.txt b/samples/sveltekit/static/robots.txt similarity index 100% rename from samples/apps/sveltekit-b2c/static/robots.txt rename to samples/sveltekit/static/robots.txt diff --git a/samples/apps/sveltekit-b2c/tsconfig.json b/samples/sveltekit/tsconfig.json similarity index 100% rename from samples/apps/sveltekit-b2c/tsconfig.json rename to samples/sveltekit/tsconfig.json diff --git a/samples/apps/sveltekit-b2c/vite.config.ts b/samples/sveltekit/vite.config.ts similarity index 100% rename from samples/apps/sveltekit-b2c/vite.config.ts rename to samples/sveltekit/vite.config.ts From 243c199a79fbf822943d292e17d16eae45dbd3de Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Thu, 16 Jul 2026 13:30:49 +0530 Subject: [PATCH 25/31] fix: remove debug logging from callback handler --- packages/sveltekit/src/server/routes/callback.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/sveltekit/src/server/routes/callback.ts b/packages/sveltekit/src/server/routes/callback.ts index 5c4e8a2..20016b2 100644 --- a/packages/sveltekit/src/server/routes/callback.ts +++ b/packages/sveltekit/src/server/routes/callback.ts @@ -42,9 +42,6 @@ export function createCallbackHandler(config?: ThunderIDSvelteKitConfig): (event let tokenResponse: TokenResponse; try { - const cfgData = await (client as any).configProvider?.(); - logger.info('callback DEBUG: tokenRequest=' + JSON.stringify(cfgData?.tokenRequest) + ' hasSecret=' + Boolean(cfgData?.clientSecret) + ' authMethod=' + (cfgData?.tokenRequest?.authMethod ?? 'UNSET')); - tokenResponse = await (client as any).requestAccessToken( code, sessionState ?? '', From 374d81c2e9a71ffd6a5679c78f3c2e250021a9e9 Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Thu, 16 Jul 2026 13:33:13 +0530 Subject: [PATCH 26/31] chore: update lockfile after workspace pattern change --- pnpm-lock.yaml | 2260 +----------------------------------------------- 1 file changed, 22 insertions(+), 2238 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 176f1ef..d162623 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -338,7 +338,7 @@ importers: version: 9.39.4(jiti@2.7.0) next: specifier: 15.5.18 - version: 15.5.18(@playwright/test@1.60.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + version: 15.5.18(@babel/core@7.29.7)(@playwright/test@1.60.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) prettier: specifier: 'catalog:' version: 3.6.2 @@ -749,11 +749,11 @@ importers: specifier: 3.5.30 version: 3.5.30(typescript@5.9.3) - samples/apps/sveltekit-b2c: + samples/sveltekit: dependencies: '@thunderid/sveltekit': specifier: workspace:^ - version: link:../../../packages/sveltekit + version: link:../../packages/sveltekit devDependencies: '@sveltejs/adapter-auto': specifier: ^7.0.1 @@ -783,109 +783,6 @@ importers: specifier: ^8.0.16 version: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - samples/browser/quickstart: - dependencies: - '@thunderid/browser': - specifier: workspace:* - version: link:../../../packages/browser - devDependencies: - vite: - specifier: ^6.3.5 - version: 6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) - - samples/express/quickstart: - dependencies: - '@thunderid/express': - specifier: workspace:* - version: link:../../../packages/express - cookie-parser: - specifier: ^1.4.7 - version: 1.4.7 - express: - specifier: ^4.21.2 - version: 4.22.2 - - samples/nextjs/quickstart: - dependencies: - '@thunderid/nextjs': - specifier: workspace:* - version: link:../../../packages/nextjs - next: - specifier: ^15.3.3 - version: 15.5.18(@playwright/test@1.60.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - react: - specifier: ^19.2.3 - version: 19.2.3 - react-dom: - specifier: ^19.2.3 - version: 19.2.3(react@19.2.3) - devDependencies: - '@types/node': - specifier: ^24.7.2 - version: 24.7.2 - '@types/react': - specifier: ^19.2.14 - version: 19.2.14 - '@types/react-dom': - specifier: ^19.2.3 - version: 19.2.3(@types/react@19.2.14) - typescript: - specifier: ^5.9.3 - version: 5.9.3 - - samples/nuxt/quickstart: - dependencies: - '@thunderid/nuxt': - specifier: workspace:* - version: link:../../../packages/nuxt - nuxt: - specifier: ^4.4.8 - version: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) - vue: - specifier: ^3.5.13 - version: 3.5.30(typescript@6.0.3) - - samples/react/quickstart: - dependencies: - '@thunderid/react': - specifier: workspace:* - version: link:../../../packages/react - '@thunderid/react-router': - specifier: workspace:* - version: link:../../../packages/react-router - react: - specifier: ^19.2.3 - version: 19.2.3 - react-dom: - specifier: ^19.2.3 - version: 19.2.3(react@19.2.3) - react-router: - specifier: ^7.6.2 - version: 7.15.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3) - devDependencies: - '@vitejs/plugin-react': - specifier: ^4.5.2 - version: 4.7.0(vite@6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0)) - vite: - specifier: ^6.3.5 - version: 6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) - - samples/vue/quickstart: - dependencies: - '@thunderid/vue': - specifier: workspace:* - version: link:../../../packages/vue - vue: - specifier: ^3.5.13 - version: 3.5.30(typescript@6.0.3) - devDependencies: - '@vitejs/plugin-vue': - specifier: ^5.2.3 - version: 5.2.4(vite@6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@6.0.3)) - vite: - specifier: ^6.3.5 - version: 6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) - packages: '@asamuzakjp/css-color@4.1.2': @@ -1013,18 +910,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-jsx-self@7.29.7': - resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - - '@babel/plugin-transform-react-jsx-source@7.29.7': - resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typescript@7.29.7': resolution: {integrity: sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==} engines: {node: '>=6.9.0'} @@ -2068,30 +1953,10 @@ packages: peerDependencies: nuxt: ^3.21.7 - '@nuxt/nitro-server@4.4.8': - resolution: {integrity: sha512-cc1fxgSx34Htesx3JBO+hMhbqd6VljXDC06P+UOA5z53cR224TmEFYT/MUuZDkrtt4qLnSG8yq0IxhEM3NCUlw==} - engines: {node: ^22.12.0 || ^24.11.0 || >=26.0.0} - peerDependencies: - '@babel/plugin-proposal-decorators': ^7.25.0 - '@babel/plugin-syntax-typescript': ^7.25.0 - '@rollup/plugin-babel': ^6.0.0 || ^7.0.0 - nuxt: ^4.4.8 - peerDependenciesMeta: - '@babel/plugin-proposal-decorators': - optional: true - '@babel/plugin-syntax-typescript': - optional: true - '@rollup/plugin-babel': - optional: true - '@nuxt/schema@3.21.7': resolution: {integrity: sha512-H35IqlFu4YHXnW0Aw24yCpRuWOJjd9iwS6u9vinsopJo8rrM1/2aKMajX3l9ifhni1XN8nqcKo622z+5ZCQM4w==} engines: {node: ^14.18.0 || >=16.10.0} - '@nuxt/schema@4.4.8': - resolution: {integrity: sha512-igfWuMF0x0Pmx/XwhPwH/bcXgbuwNnjUjqxCAsY6VQhmGKo0e9soJq3Q0ohj+rBkBfX6o2ysTP1/t2M82aK4qA==} - engines: {node: ^14.18.0 || >=16.10.0} - '@nuxt/telemetry@2.8.0': resolution: {integrity: sha512-zAwXY24KYvpLTmiV+osagd2EHkfs5IF+7oDZYTQoit5r0kPlwaCNlzHp5I/wUAWT4LBw6lG8gZ6bWidAdv/erQ==} engines: {node: '>=18.12.0'} @@ -2149,26 +2014,6 @@ packages: rollup-plugin-visualizer: optional: true - '@nuxt/vite-builder@4.4.8': - resolution: {integrity: sha512-54M/k6qVY85Qeoe1m/lPZ0SANGJEbI50r5uYgh3XT942ENve3K5Nk6TMYp8i5wGGC4TWvPea+1mlCrp8rjsXag==} - engines: {node: ^22.12.0 || ^24.11.0 || >=26.0.0} - peerDependencies: - '@babel/plugin-proposal-decorators': ^7.25.0 - '@babel/plugin-syntax-jsx': ^7.25.0 - nuxt: 4.4.8 - rolldown: ^1.0.0-beta.38 - rollup-plugin-visualizer: ^6.0.0 || ^7.0.1 - vue: ^3.3.4 - peerDependenciesMeta: - '@babel/plugin-proposal-decorators': - optional: true - '@babel/plugin-syntax-jsx': - optional: true - rolldown: - optional: true - rollup-plugin-visualizer: - optional: true - '@one-ini/wasm@0.1.1': resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} @@ -2178,84 +2023,42 @@ packages: cpu: [arm] os: [android] - '@oxc-minify/binding-android-arm-eabi@0.133.0': - resolution: {integrity: sha512-D8M1+nqwLaACHZsld/t6f+cE4N97XOu5iQ88f1ZaYH4ptFzFrXo5N7wUKACTI4xmNUD+6W0Y4Apk5U2r8HLdBQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [android] - '@oxc-minify/binding-android-arm64@0.132.0': resolution: {integrity: sha512-XYogHG1aSjNEMKWUfWmBWtN9rnpQ2nA4MiecdiAOfofDHTQiU5ybrPH6VvDAtRXf2kr8WtPNX7eenhC3uWFWoA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxc-minify/binding-android-arm64@0.133.0': - resolution: {integrity: sha512-dnQUJdpOEh/nZfQtvGGN61VcCCcPJ2aCm+ndl8GIA2lk2GpmIBgZ9h+phLVhgUFGt2es+2AQc0xvtK7RFNsViw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [android] - '@oxc-minify/binding-darwin-arm64@0.132.0': resolution: {integrity: sha512-gm/M5dgm7IvA/g9tweMqiFyD15yKrxGUX3myjFP+EYIYVW+RYuvwU5MAIZUOxXY0GnjU1/iRN/JkLhwvhZVsDQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxc-minify/binding-darwin-arm64@0.133.0': - resolution: {integrity: sha512-K6+aXlOlsCcibpTiTitQYNXWGGwea0fEKF/kGHCNB+MNqOLCkdC7wesycaABYcXcyr58DhDoJnVb8E4Hq95iVw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [darwin] - '@oxc-minify/binding-darwin-x64@0.132.0': resolution: {integrity: sha512-s7ecbOJeLccy3nqQlkiq9cV0D0q8j1OyHmxRz22m8qZlcKrc3s4gmhwj5ertipA8ePn3FOXv4azf8b5gatDDug==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxc-minify/binding-darwin-x64@0.133.0': - resolution: {integrity: sha512-BFEXHxYNwThyaO63p1VE5MOOXNGkHsHfkmajOCKXH40TfllTHQenXhpJ9mHDoF7EhaQjArpPjlDY88BuPjhurw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [darwin] - '@oxc-minify/binding-freebsd-x64@0.132.0': resolution: {integrity: sha512-pdYVNmY9NgKetEWzXlVIUlPm4Z3Gz979nTbZUpHlqpjU/rtulpm0fgROo6rlTk+W0HhZCCZ0Jzy1LBKgn5g3mg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxc-minify/binding-freebsd-x64@0.133.0': - resolution: {integrity: sha512-oT5dbcXnS/cbpdXCpudAeVg/fqH1XnKhLUE/vkuRTuocjOd/GA2MoNMMhLWUvqNXO0xJnYmo2ISmDxShkItfOQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [freebsd] - '@oxc-minify/binding-linux-arm-gnueabihf@0.132.0': resolution: {integrity: sha512-QdV2II2mrbygZO/D+umhb+jMs+kmNO2pvQ+kahY8DN7qZVvaR2CiWBQaAxi3yuI0JvmymcUBEFhRrXsaL/lUqg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-minify/binding-linux-arm-gnueabihf@0.133.0': - resolution: {integrity: sha512-tJ3B+b7DOuTsIMXSmu5xHHCakrBqqcrp4COYd/lelOdDvkbFoDRGnwH91POUOSUEOI/WLzIMkDqAH2SZ3N2jhQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - '@oxc-minify/binding-linux-arm-musleabihf@0.132.0': resolution: {integrity: sha512-6OJMBb53luST+xxNSzzg/rRkxMnR4NFQegdu3PCuDEUtP2OEgjmpvvBrHghITpzRsUqnQ/YTl/ItDiLVeoslUA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-minify/binding-linux-arm-musleabihf@0.133.0': - resolution: {integrity: sha512-XMUHfdilk1KTtOM2vA1bwDso07/wkLm/GgDOO9z/ioxrZoQyjXnJRW665VXa08z2BqEgwHRc1zH9p7s6sKPQbg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - '@oxc-minify/binding-linux-arm64-gnu@0.132.0': resolution: {integrity: sha512-ND2GZp6StGQWhSBwOfX13kCCG7O/Z6sEL/dBsWSIgZaetEDUPLOWtKIm2f+TuYUSSmU5nJTSSE5psh9kGcCweQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2263,13 +2066,6 @@ packages: os: [linux] libc: [glibc] - '@oxc-minify/binding-linux-arm64-gnu@0.133.0': - resolution: {integrity: sha512-UEff2jopbwJ4SndmxK06uqXrOpwWiJERJPdgDTBywwXP9QgW0p1YkQnBNt4+jK0I/hdLpbbyaCLLuUPKbaU70w==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - '@oxc-minify/binding-linux-arm64-musl@0.132.0': resolution: {integrity: sha512-3k8ezEKmxs9Wel4N4vfF/8u764mA57j065P8nB4cU2PO/lLKloN0OA41ynfDUrSM1f5jBuF8+mLOj++aNnu4OA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2277,13 +2073,6 @@ packages: os: [linux] libc: [musl] - '@oxc-minify/binding-linux-arm64-musl@0.133.0': - resolution: {integrity: sha512-yqskeIapQvx7Tu/OLsepLPcGsHGzfYy9PX6gIbhaOHfF+LA2zHBKnKb587FGx+lQjHLQR0llfmoSuXQ6q2EN+A==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [musl] - '@oxc-minify/binding-linux-ppc64-gnu@0.132.0': resolution: {integrity: sha512-vM6jZIxoHoIS5rPb3K3Di0IureL4oU+wOWBy6tLSrjwW2IHqy0442CzO/Ks2U9VCuHV1q0bUGCF0H6AxCEjJHQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2291,13 +2080,6 @@ packages: os: [linux] libc: [glibc] - '@oxc-minify/binding-linux-ppc64-gnu@0.133.0': - resolution: {integrity: sha512-r7PnUNxRB9D/gQjCVeasoieJVUF48n43rvk/jYbGAw9sRfYGoEo/rOs0GyTZU9ttss8HzjBaerAbADbAL8K8vw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ppc64] - os: [linux] - libc: [glibc] - '@oxc-minify/binding-linux-riscv64-gnu@0.132.0': resolution: {integrity: sha512-KburrmtWpeZg58uo275QRwy5bbNOXQd1WDI2tGxkY2dJBlO7N5V9+Uthvqn6KI/6RBtjd2T5NO4dCC0fgUxGvw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2305,13 +2087,6 @@ packages: os: [linux] libc: [glibc] - '@oxc-minify/binding-linux-riscv64-gnu@0.133.0': - resolution: {integrity: sha512-omXWC8I9lAMMjQIeadfItP5H4VDAiuU2BiVCtHMH3ktTbFq04sxscZhK4NFUUuw3fApDdXmfd7LW18q0JBHarg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [riscv64] - os: [linux] - libc: [glibc] - '@oxc-minify/binding-linux-riscv64-musl@0.132.0': resolution: {integrity: sha512-MnahA2MNEtEdxWdUy24JXkMUNgGPqH285GL2L22Zz7k9ixsguFD+bTbbcR88pNqdb075nazozzv3edF83+azCA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2319,13 +2094,6 @@ packages: os: [linux] libc: [musl] - '@oxc-minify/binding-linux-riscv64-musl@0.133.0': - resolution: {integrity: sha512-LtFA3Hi8LVD/zuiPLKy9Aiz7N1IOj8rRhdXiW38GKQ9mAhj+Ko6IHGcTk2A7yNDA1DZBl7r+Qd4PEGWgVelPPw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [riscv64] - os: [linux] - libc: [musl] - '@oxc-minify/binding-linux-s390x-gnu@0.132.0': resolution: {integrity: sha512-VE99UPZyQO2MAG4gLGXzrBumD5PGNaiWe+EakaROGCVbT0YH/d9z2ByYqbdWAMEBiTHjthyZp6UUEFVda+LnpQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2333,13 +2101,6 @@ packages: os: [linux] libc: [glibc] - '@oxc-minify/binding-linux-s390x-gnu@0.133.0': - resolution: {integrity: sha512-rFsPDsT1j3beSInbrFukAAlTg101PcqdVMXDioR9AgJ1180tZ8s8D+pNDpQTRmPd3956mnpAE+Cs77Xoo/QZAQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [s390x] - os: [linux] - libc: [glibc] - '@oxc-minify/binding-linux-x64-gnu@0.132.0': resolution: {integrity: sha512-FKxBkYrSAWNF4V6MacAJ/1E2SJobKKQ2CtW6Aq+pLzzEOjgk2SmxnK7I0bATlFH/O70tbTKDzWb17bySGYRcog==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2347,13 +2108,6 @@ packages: os: [linux] libc: [glibc] - '@oxc-minify/binding-linux-x64-gnu@0.133.0': - resolution: {integrity: sha512-xlrtAmDWZI8BEmsaXMYfblWuLIY5UnnRkit1VLkmVDb5ceZRZf4oEXK1QeYf5Z33dT0WK1Ek++P+TL/ZMCpyGQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [glibc] - '@oxc-minify/binding-linux-x64-musl@0.132.0': resolution: {integrity: sha512-W8IqA2XRvg/b6l/f+2SdV45/KKmpmwTabrjiMtpg/wzJU5cmKUoHihtJXPc9NA0Ls9S/oP0wB3PMCRQoNr5J1A==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2361,155 +2115,77 @@ packages: os: [linux] libc: [musl] - '@oxc-minify/binding-linux-x64-musl@0.133.0': - resolution: {integrity: sha512-kd36CDkTkZDMNfVceNTSfpWnitE1+GjZmzJCeq8yaxsgvs/MXg8aauI2RgFjElYZIHSMyZku4pQ7Jtl3ZEYI6w==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [musl] - '@oxc-minify/binding-openharmony-arm64@0.132.0': resolution: {integrity: sha512-X1BL65pI9bfOesLdVUcErjbEAUA3qmzjXCwXPCYsFZT7ela7SsK85+sN3m2TJNxmX1mrFKNg5g8bH+d2zHresw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxc-minify/binding-openharmony-arm64@0.133.0': - resolution: {integrity: sha512-pI38dJBqfkNbFoL/GEarAzGDjKGVCZTdg0a8NKh1PP9GqWleXT6HLtXE4CZ+54e+2u68qVYVBwhbWAiRfwlUZA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [openharmony] - '@oxc-minify/binding-wasm32-wasi@0.132.0': resolution: {integrity: sha512-QcIiwBOj+bV5ub5x39Xb+v0boviykxUtVvVJaIEbG/IH97avFzZcBXec8awYlemLDvgG4WKQwr17x7COR5zwFg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@oxc-minify/binding-wasm32-wasi@0.133.0': - resolution: {integrity: sha512-AkLr+d+LLY4/55J/TrE0srNBUpZPzyU+cygdse7yZ9AhCndryNqe2y6e8naVK0TV7n8lxBd2OGGJAkho6blAkw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [wasm32] - '@oxc-minify/binding-win32-arm64-msvc@0.132.0': resolution: {integrity: sha512-ahFMaa84QVTIROWpLhZcS9jKIv+CXzsJaMmgje7JtlVp1Kaar6tzVCt3EH2aPhSc8RvbGqfmnGdQu/kGwPAZVA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxc-minify/binding-win32-arm64-msvc@0.133.0': - resolution: {integrity: sha512-V92v7397t2073g+mSfaLHnPeoz6hA/1U4JNLeUBP87eWGZgVxDZ2qz3t3wFyYqXGJ/0VoEwdP8yrHLQQ7QzAOQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [win32] - '@oxc-minify/binding-win32-ia32-msvc@0.132.0': resolution: {integrity: sha512-tpBkLklqOnaYtlIh6gjmL60pP0Kn2hwaw1Fw3sJyIKwdkCPHsOPy/MRgBUpM0a/SeGFbsZRQkHnWfZXS1GTbbQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxc-minify/binding-win32-ia32-msvc@0.133.0': - resolution: {integrity: sha512-2DP5RbG/SSaRVtmuwgTH6Ati4+uuOJjoI88dQnC5hD0zCC90EVDXZSXyJQ5i/OxLE1UAy58Wo2DJot/OrUspzA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ia32] - os: [win32] - '@oxc-minify/binding-win32-x64-msvc@0.132.0': resolution: {integrity: sha512-a69yKrBl2p9O8cdAHbHih56eKhcpKJRVkRu/S+CwCdR2Zsh4nnqYIllF96Lxg3jDjRQNL3t0xZNdYBDG5Vgq+w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxc-minify/binding-win32-x64-msvc@0.133.0': - resolution: {integrity: sha512-PJ75c6PlBx87tau0W35J43eGCv4wrDmdZ+4ddTZAnGtZqEeCVsLdmDPOEMe2DepogqlSVUF2kGBWtnFUJ5c7Rw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [win32] - '@oxc-parser/binding-android-arm-eabi@0.132.0': resolution: {integrity: sha512-KrLaPWa5c9Y7LkW+rKkaUE3y7DBDrQtaf7rlsSDfv6KAHUjgzAIRA761Lrrp6//Yd/Rlie/yEOt9YENCoJnOcw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxc-parser/binding-android-arm-eabi@0.133.0': - resolution: {integrity: sha512-l/44caGse+VpnY9gx0yvvc5QnnG3yG1FO3KZgYvNL1GZrfK86zIwAOgGEVlxDyRymzrU/KHiblPFpevKOmJmUA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [android] - '@oxc-parser/binding-android-arm64@0.132.0': resolution: {integrity: sha512-SThDrSeamB/kG2+NxcJ5/wSLcV6dUqDknrPLqFYQ0ST/55mtBP4M7Q/f3QbubH6aAd11wpzZn/nwbVRSdobOpg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxc-parser/binding-android-arm64@0.133.0': - resolution: {integrity: sha512-KUHmPMziLBp4u+zbrLdB7iWS7KshuZe+RAp7ELnY9SI9nNXBZ+dp8fiBqWOxhXqn+FQg3a4UcQhwmsJOKV8Jjg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [android] - '@oxc-parser/binding-darwin-arm64@0.132.0': resolution: {integrity: sha512-Lc0f/TYoKBghE5/2Gsv7bLXk+TJZunx2Tf61X8hG4ARXdc8UYI26dCGccFSd1AyFbK3jfaNXtMnupggDbjPXdQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxc-parser/binding-darwin-arm64@0.133.0': - resolution: {integrity: sha512-q8dWmnU/8ea2tga9w2f1PinQ5rcMPDUGkF64T189b65YMjUomET4oy5oRldOr4AwOQkneOG/Zttnz1Dvrc62wg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [darwin] - '@oxc-parser/binding-darwin-x64@0.132.0': resolution: {integrity: sha512-RG2eJIpf7C21z9HSSXFw1bTArdpKe7Y4fwcJTwRq1yCSe1vSavaN9GA1sm9KqzemTLAGVktQ+7qBTGp0vQeUZg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxc-parser/binding-darwin-x64@0.133.0': - resolution: {integrity: sha512-cOKeIELIB2bJnCKwqx4Rdj+1Lss/U6uCbLxRySZrhyOOQa1flKhwZFjEHRHxk8fU1NKmhK5OnTdPQ4CpjuFuVw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [darwin] - '@oxc-parser/binding-freebsd-x64@0.132.0': resolution: {integrity: sha512-wQIPntPLtJ8NcBpvKPbEv3NqzV6k8eP8tP/jE9Rg8HTg/j7urZGFSsTCPCW5k77Qfw2DM4vRvc9p3I4yq/Shvw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxc-parser/binding-freebsd-x64@0.133.0': - resolution: {integrity: sha512-OpaSv4pW3KgFrMYQxTaS0aOE4T1DQF3qZE/4B6uqqv1KgPWWd4UQhJALi8PJPX1RRV5K7ThKXRfF7qGg2+3l1A==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [freebsd] - '@oxc-parser/binding-linux-arm-gnueabihf@0.132.0': resolution: {integrity: sha512-PixKEpeSe3yxQWqNyOCBALRYc72+Tj7ILDofUl3iXo25cVOzLA6jHUhmOINRtWIPh7dbUie3QNeabwaQpZTw6w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm-gnueabihf@0.133.0': - resolution: {integrity: sha512-JGK1wlGrGwxBIlVSF7KWTX1/ru6BEtf28fRROztDRkLfiW+Kxa4onnriezMIiogfn9hVw2KzYcKiLjkLR2ns8A==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - '@oxc-parser/binding-linux-arm-musleabihf@0.132.0': resolution: {integrity: sha512-sCR+DzGHlyHKnbA2z9zWjTUhIo8Sy0enJl4RDsBwPmkxYynPatpwOAWe8W5127SlW0boqUWHGtr1NWn5UwIhXQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-parser/binding-linux-arm-musleabihf@0.133.0': - resolution: {integrity: sha512-yuZO533Ftonxn/iyoqQzURzLQHMspvsIyfiCSNi1t/ER4eIQaR0SsmUOUm5b/lmSig7IWIUa5/BrbEkAPwcilQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - '@oxc-parser/binding-linux-arm64-gnu@0.132.0': resolution: {integrity: sha512-sQBix5P2cW+IpzTcCwYxnh9yALrKSIkKJThspBvMGcygSMnbzkSvhN7SfuX1hvBk8y1XEChsdkU3ET0V5DmzUw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2517,13 +2193,6 @@ packages: os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-arm64-gnu@0.133.0': - resolution: {integrity: sha512-hvpbqT5pN2rR+3+xtWeizwfR/aZ0vGceg6TqYMl+ToxMpk9/tmnX7kSvQnfEUkoua8mhogzvIKsAkn0wxgblBA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - '@oxc-parser/binding-linux-arm64-musl@0.132.0': resolution: {integrity: sha512-WozHg3Kc//8Sk756HXXgMbEAvqtG+Lzb9JOojwQzIGDtN78Az2dLttkb71akWYUF/8IgYfDSlfKh4Uot8is5Vw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2531,13 +2200,6 @@ packages: os: [linux] libc: [musl] - '@oxc-parser/binding-linux-arm64-musl@0.133.0': - resolution: {integrity: sha512-wJQGamIosQBoJHW9+S5XxrtKRo3eyJxsnS1XCPrqN0LHi8uw1pTqqTfn3t/NVuvbBg7Pumn4ez9Eidgcn0xbEg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [musl] - '@oxc-parser/binding-linux-ppc64-gnu@0.132.0': resolution: {integrity: sha512-CmX/ulNBOEwWTyVRmcpYKAcAizW6+OjtLJgo7fXoL9OqQvjF4VER8tPomv44vwzfSCy1BHbsB0ZlZYzYJNj4cA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2545,13 +2207,6 @@ packages: os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-ppc64-gnu@0.133.0': - resolution: {integrity: sha512-Koaz32/O5+abIfrNGdyndgRvdOZ9jEf5/z3Ep9h3h2QWpdDiUQpVwgH0OcMXCs+l9aXxPLtkupqyVig9W6FDKw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ppc64] - os: [linux] - libc: [glibc] - '@oxc-parser/binding-linux-riscv64-gnu@0.132.0': resolution: {integrity: sha512-j9oQS+hM90SdhviNGWbPgT4+Rlq+ac++q/zjgwPD1mVHgxHzATvoRGtDx0sXGmFOQ9J9YkwAhYGb5MAHL6TAsA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2559,13 +2214,6 @@ packages: os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-riscv64-gnu@0.133.0': - resolution: {integrity: sha512-R4vOjWzxhnNWHnVLeiB6jNuIifdy9vcMXZGPc7StXcxBovI+U2zg1QhZ9o8OjV80oGivs1lX5NfPLzk4IPqlRA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [riscv64] - os: [linux] - libc: [glibc] - '@oxc-parser/binding-linux-riscv64-musl@0.132.0': resolution: {integrity: sha512-bLz+Xi+Agnfmd7kWPEsSVwCn2k4EyIalZkNBcQ0OGIv9rqn8VgCPLNd03tM9mKX/5TdlvDXalz0q71BIrOPNqg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2573,13 +2221,6 @@ packages: os: [linux] libc: [musl] - '@oxc-parser/binding-linux-riscv64-musl@0.133.0': - resolution: {integrity: sha512-iwgBNUTHiMdxARLYuM0SBlnYeb19iw1Ea5M+4ERZupCsBMLArti6FyZ6UfFjJxIiTDr2oW2DGQFxlQVQ/dW9rA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [riscv64] - os: [linux] - libc: [musl] - '@oxc-parser/binding-linux-s390x-gnu@0.132.0': resolution: {integrity: sha512-U6t2qbJU0ypTfyj9QV3W1Y6mITDTL8ai/OR6NUn85vyHthOvobKWgXzU4tu0EskSzlpuVFz1g0jFGulDIUKHxQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2587,13 +2228,6 @@ packages: os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-s390x-gnu@0.133.0': - resolution: {integrity: sha512-ZwZNo8FZmB/gVfboQl+wXilBigGl+6nQQs+nITOeAP/HcAOjiHl6XZJL9F/KXNEspODQcbjAiyjUbeCJd9a0fA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [s390x] - os: [linux] - libc: [glibc] - '@oxc-parser/binding-linux-x64-gnu@0.132.0': resolution: {integrity: sha512-WcEaSNHFk8yz5YFlQQAlhq6jOFmZBB/RKE7uzhyCIf+pF1Lmv9gUH4221mle2Gd9iHyWT3ySNph8yZgb1xYdWg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2601,13 +2235,6 @@ packages: os: [linux] libc: [glibc] - '@oxc-parser/binding-linux-x64-gnu@0.133.0': - resolution: {integrity: sha512-govCvWx1dBlED3uu4qXctxpRcouu9I8Kn+DBktGCl760JtlGJzc9l/OmPJKlYWSbrRqKkMZehNeZ/4Wfma7uSA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [glibc] - '@oxc-parser/binding-linux-x64-musl@0.132.0': resolution: {integrity: sha512-iQrV4iJzQgRwK3BWRmQl1C3C6g3wYpXN2WLdQdyR+efoUnncdShZAVp9OgcojtlD3MDRbuOMGG3SjxF4fL4nlQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2615,77 +2242,38 @@ packages: os: [linux] libc: [musl] - '@oxc-parser/binding-linux-x64-musl@0.133.0': - resolution: {integrity: sha512-ssTlpXD5Mq9uCssDJPzlRWqBt4Y7Zzd9i+XZhWmK/9Y6KUIuAxVYTYiI8lxcGWi0+3/Cz4A8q9UrD4NK9Y2j7g==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [musl] - '@oxc-parser/binding-openharmony-arm64@0.132.0': resolution: {integrity: sha512-FWzmUGrZ6GUby4U7WIwcCtab6tdmlTO3xTRRKyb5kjIJVEiaUAT8animUG/nK8ZCA8gkRkPOTId4rl6uTqUmJQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxc-parser/binding-openharmony-arm64@0.133.0': - resolution: {integrity: sha512-51aByfXhPtLEdWG4a2Ihdw6cPWV1ei1AarALpFdDP8MLWDLE2NuUMgbo3DERR2Kt8fT/ok1GUvBiLxVGke9uUQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [openharmony] - '@oxc-parser/binding-wasm32-wasi@0.132.0': resolution: {integrity: sha512-TlbMppxJI5CjWDes0QaP6G3aneVg1yikBu5QYI+DUShF9WDL66ccgKFNNGmi/Wybtszw6hxwAvv76T4DaPKnHw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@oxc-parser/binding-wasm32-wasi@0.133.0': - resolution: {integrity: sha512-2e16tkKp+wDO2GTAmXfxbBcCmGEaFPIJEIRBBmVKNVXSc8/fJsSIaBGyFTPHM9ST5GNWgJcYIt94rDTks+PLwA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [wasm32] - '@oxc-parser/binding-win32-arm64-msvc@0.132.0': resolution: {integrity: sha512-RH/NbFjGKqdUAUi7Oh3LQPxUk2hsWFEEQ38HSnbRQT8QjBZFKqL1fMbmsB3N4jy/KPh9iX94+9dmkEMBBbambw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxc-parser/binding-win32-arm64-msvc@0.133.0': - resolution: {integrity: sha512-KPTNDKbxH1cglrqTyVeXHb4Pk4oksz8EcE1/v8zqU7N4UXbiHfA/IwtXZ2U77fnRAWBbgVkl/lZbL7o3hRdejg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [win32] - '@oxc-parser/binding-win32-ia32-msvc@0.132.0': resolution: {integrity: sha512-JUr4jQY9jxoIB/YTLXr6XofSi5xikj6p5/Ns1h0VOBDT0j1jKU+kMsv2xxv51RwnETcXpA1Yw/9oUAfcqfaqEA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxc-parser/binding-win32-ia32-msvc@0.133.0': - resolution: {integrity: sha512-Una1bNYv9zCavQrfnDR9wuZVB3itLjCEH4Oz7i6CwAJN/Xq9b+zbbcxmvdkKvvJt4Ngc/MBmIYlbLo3zS4TQ0A==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ia32] - os: [win32] - '@oxc-parser/binding-win32-x64-msvc@0.132.0': resolution: {integrity: sha512-2dapgHpA5X8DSXF4AU36hJWYf6zP0tKjMXFRAZFBD62pkevW/uhFDXoFH9Y/3Fd2EtDrw5ByNnR1wVE9X9y0SQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxc-parser/binding-win32-x64-msvc@0.133.0': - resolution: {integrity: sha512-kjBhCiOGSYTwDJQuuZa7a94JbP8htWu7J0X1KwH74kV2K5eYf6eyJRYmkpCDvr0XEL8tMxYI4WU1VekblFCLgg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [win32] - '@oxc-project/types@0.132.0': resolution: {integrity: sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==} - '@oxc-project/types@0.133.0': - resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==} - '@oxc-project/types@0.137.0': resolution: {integrity: sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==} @@ -2698,84 +2286,42 @@ packages: cpu: [arm] os: [android] - '@oxc-transform/binding-android-arm-eabi@0.133.0': - resolution: {integrity: sha512-2A79NBpyBKgHJ0FwgC8D1hzp3x2ujyvqq/kG+M76YyDMMkxLhX6A3vjnAnfEKycOoZxuKhwYu8BF9hKq67ykIA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [android] - '@oxc-transform/binding-android-arm64@0.132.0': resolution: {integrity: sha512-sr2BbEHtc5OkAN2nt5BpWGg/MnDkyQKf6tSjaZZ6k7Bb2FOa2CzZDy2pvH6tYdg+Ch/p/OGXXhENFVV9GU7ASw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxc-transform/binding-android-arm64@0.133.0': - resolution: {integrity: sha512-dynEph/hyoSgBzd2XbNlW37NK97nU6tZMs5jrhObUxSasBV/Gv9THZrWj9AlbWiMXR07WFYE82C9axjntYyBSw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [android] - '@oxc-transform/binding-darwin-arm64@0.132.0': resolution: {integrity: sha512-yjL1GbN9Bb1HqjE8CS8NSwoZtDWgUGy43VbuFhmT4LEDx4Ph0guzVAyUKhc2CqqA4/x60qDvcH6QxwrguaqEVA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxc-transform/binding-darwin-arm64@0.133.0': - resolution: {integrity: sha512-4hGgKOG+dZSN3xjcgNWpcihekRG7/YbbAdjyz07yv0HjzA6kdqYAhGrn84374UPO2h6etYJwsCBoM9iJHHvJ8w==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [darwin] - '@oxc-transform/binding-darwin-x64@0.132.0': resolution: {integrity: sha512-e3vVXEbNw93aHr3si8eVpUgl+jWF6Ry8RgUihgSxiI+2c/VMxiPsEDghkqPcjujqsMYDRdISWJi23xk+PP72ow==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxc-transform/binding-darwin-x64@0.133.0': - resolution: {integrity: sha512-7J11/9PFkznmKuANkCAjt3znV1BcDFXQSgDiBvDxXT3Wm6995/zxrJD5zmo+5XSgY4sm+2V8/ED6ZSD3mKOC5A==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [darwin] - '@oxc-transform/binding-freebsd-x64@0.132.0': resolution: {integrity: sha512-dIhAhkX8/It4IaKI944fN3jmfzunqv2sEG2G4fQdP5/1psycdqUHoVaY23DbpuYRIu4sWAdn/e1zQFP0GMkQOQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxc-transform/binding-freebsd-x64@0.133.0': - resolution: {integrity: sha512-5EMAO0vzCpUfhn6aSjIUeJeRI2ztevHwSVr/M8sZ2VBYc79UuOfjjMCQ67LtUbgpvQtpBWkzeAHCP3L7JFYmlg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [freebsd] - '@oxc-transform/binding-linux-arm-gnueabihf@0.132.0': resolution: {integrity: sha512-eR0dfj1us7DNbGZ6eBdAqWnLZRkLqHFqewSHudX4gV7di3By8E05+M+qsGTB/zq/78Z0BYJeK1zGWu9un6jocw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-transform/binding-linux-arm-gnueabihf@0.133.0': - resolution: {integrity: sha512-z6XT8tmo9sPmCIYaFIxDelBU4wXLwwWMX2VNCMIY6bkQp5r+kRtVXYS3yLbJHMKEhRKvw/g+Z7fO9aadsGGEAw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - '@oxc-transform/binding-linux-arm-musleabihf@0.132.0': resolution: {integrity: sha512-naNx0WaV70hKtgQ5LUS/jzRTy6XEQZ1krK7KTFZQLI1mEz+GqLrwsLCqEmtrQ6HcqLhvGvA6GAWfFrc/0mWryA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxc-transform/binding-linux-arm-musleabihf@0.133.0': - resolution: {integrity: sha512-GQDpEV2VhHG8hT5BviDv+emi9oHYhfv+JJJWROYp+eGgWjiQMp4QZVb6Bu3kwVMzkwy0r200ToA1KThYTq53ug==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - '@oxc-transform/binding-linux-arm64-gnu@0.132.0': resolution: {integrity: sha512-TWk1p0tbtE1tkMEABftfgXhMEfuoz3QieqBtMBXXyijizw/2YKNzbVSndG+vV73cSZgbyfoZ346pmuz0tQMzyw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2783,13 +2329,6 @@ packages: os: [linux] libc: [glibc] - '@oxc-transform/binding-linux-arm64-gnu@0.133.0': - resolution: {integrity: sha512-VstR+NEQAJb80ysWk2vPjEvg0JzwEjKn2hDbC/joa5zGXkCnVVCWgAGG8c6o23S981a7XRpCMcClBgeD1q9H2A==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - '@oxc-transform/binding-linux-arm64-musl@0.132.0': resolution: {integrity: sha512-LxURDI0Wm2KCQm/3ynNlI+nTgPdfmAfmrl54XPx+gaIqty8S/XWNCCTvLJWaCb0e5eKqnzrcTuhMDOdawqoYIA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2797,13 +2336,6 @@ packages: os: [linux] libc: [musl] - '@oxc-transform/binding-linux-arm64-musl@0.133.0': - resolution: {integrity: sha512-Ec7xJdDrnukgiz20E3iDNzAIgx1XXn8cVVsNNUpgEIAvNlXZaocqlQT8Zalk0Lv3fbkxcJ+9BuWB0ndBRHQtzg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [musl] - '@oxc-transform/binding-linux-ppc64-gnu@0.132.0': resolution: {integrity: sha512-eKEeG6SLtj01iDvi5QgMNzyEXt/K2BNWafZ0jGECmvqTWWaO2l4qBxUW+X+sAXp5vZBoT2WO3ZnshvIWXWjtKw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2811,13 +2343,6 @@ packages: os: [linux] libc: [glibc] - '@oxc-transform/binding-linux-ppc64-gnu@0.133.0': - resolution: {integrity: sha512-6YX38grimcigz20eYpyz6e4c9rDKzwK3i+tcDpgwYj0bWreaAOwrABmSmKplPJOorkDVlbT69wPCN+d11irBQw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ppc64] - os: [linux] - libc: [glibc] - '@oxc-transform/binding-linux-riscv64-gnu@0.132.0': resolution: {integrity: sha512-Kz6tg1Msra7+2iGV8K5xANLO2SmpP6n+91/Yy+JJh9EagU4hvMm7loReszzz2bwhs6Xs4HPrglxIngMdqnHpXw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2825,13 +2350,6 @@ packages: os: [linux] libc: [glibc] - '@oxc-transform/binding-linux-riscv64-gnu@0.133.0': - resolution: {integrity: sha512-WxMIzItRJR66lgaAyyqj0FFwLMpcuCV9mTFcUMQpIz8+Hey1Enk8xuv+7QpSsqCR5zRlwNr092dsFkz5cbvtrw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [riscv64] - os: [linux] - libc: [glibc] - '@oxc-transform/binding-linux-riscv64-musl@0.132.0': resolution: {integrity: sha512-dtUSp80ElrxUhfBNmFWGkFQQ51j3tRoZkKBXxEWh+hb+S6bbEdZCW/VuCYo/gCTH3DywwyTeWiG+dtZfJiHKvg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2839,13 +2357,6 @@ packages: os: [linux] libc: [musl] - '@oxc-transform/binding-linux-riscv64-musl@0.133.0': - resolution: {integrity: sha512-+x6dnO87986rjVNjcF0tg8wVS0e/SH8nzLa/X0Wsh7jtEniN7buvR8iqZm8pnsfaZ8DH5F4GCSZpoPRrd9jJ6w==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [riscv64] - os: [linux] - libc: [musl] - '@oxc-transform/binding-linux-s390x-gnu@0.132.0': resolution: {integrity: sha512-9qVyCbYSs8dwVPpqKKWxuUAnLJ1+LyC5A4oNMZTzymRhuQr3coqAP/XWfJ8LlhQqI9GvhK0SWCOK0iM3HFUAnA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2853,13 +2364,6 @@ packages: os: [linux] libc: [glibc] - '@oxc-transform/binding-linux-s390x-gnu@0.133.0': - resolution: {integrity: sha512-oEyQudXIwWM/+v0vZzPbAi25YMWyvjtQYYjuSrhMEQwe7ZEMDXscX7U1j6alrVdZq2DtCMeror3X/Dv7p/JUwg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [s390x] - os: [linux] - libc: [glibc] - '@oxc-transform/binding-linux-x64-gnu@0.132.0': resolution: {integrity: sha512-dUtJkDCYndDaxcuiSMyRoSb7sXmTbcJ61rDsUjIakghP6BkKwH57lyHYvSUhT1ZswXWwCjf3ksxlT8nA0iU6ag==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2867,13 +2371,6 @@ packages: os: [linux] libc: [glibc] - '@oxc-transform/binding-linux-x64-gnu@0.133.0': - resolution: {integrity: sha512-G8P/OadKTbyUHz5TK63sDDtUHwn2SXG/o0oGo4GGTzBu70xmUSN5/ZUgpyl6ypAmbshoyw8nC7+msb3BjzHxaA==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [glibc] - '@oxc-transform/binding-linux-x64-musl@0.132.0': resolution: {integrity: sha512-I7BkkktnrriiO7o1dF3RDgKZoSmFKX9IE0W2LE1WdfmpZcAa3fbv5BW6oVbzk40iD29hWSP69A65WT9l6dxuzg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2881,71 +2378,35 @@ packages: os: [linux] libc: [musl] - '@oxc-transform/binding-linux-x64-musl@0.133.0': - resolution: {integrity: sha512-Oi/fyOzZ+aytmmsRND5pGgvux4n++v9cG4qNFiXj7qFwSqBKWZHBq7cJLXqbH1I81pyI3kvU1Za+1qk3afXuwg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [musl] - '@oxc-transform/binding-openharmony-arm64@0.132.0': resolution: {integrity: sha512-yiXaRYqgySJguURNZUFLDzSI1NTkP1jJKrowr8lQCKwY5N8DsESbQJ1RpSlEbeXGiy201puA+QC2fdr+ywQM/A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxc-transform/binding-openharmony-arm64@0.133.0': - resolution: {integrity: sha512-/ZElgq+/tcga27X2G2AUpxcYX0baX94Gz658w6Zz2P+6Kr06bfYSrdtC0P7oPrbu3Gy/6kpiSoJPgZy8R2IjYQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [openharmony] - '@oxc-transform/binding-wasm32-wasi@0.132.0': resolution: {integrity: sha512-KNago0Mv+zl2yl5hK2G9V4Yb7Tgpn+z6lgzgaHXkGp7S+iuUtN3av+QqPCD/J+Odq6EjjyXJrFPfmyjbXXbf4Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@oxc-transform/binding-wasm32-wasi@0.133.0': - resolution: {integrity: sha512-GANcoEa8Nzza7saxdb4qWO24U6jk4nK6G+g87lGp8TTU45CUvWf1Igdze2+NrebgiwOy6F1/h6Esag4DM3JTtQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [wasm32] - '@oxc-transform/binding-win32-arm64-msvc@0.132.0': resolution: {integrity: sha512-3fprECrLHwPP809a1SRzszDxp8Fpp8IOg0V2EO49wS+3JmRFOo090h5c37faZvym5VnRZ12DH2tkT6ZVXwlOsA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxc-transform/binding-win32-arm64-msvc@0.133.0': - resolution: {integrity: sha512-2+uDo/+ZvGQu10J8xryg/l5PdBt2vXPtf+0aIosVKJavqCaKcBDdo95OUaEulx0bqvoytAQ4yyz2gcPZ40mjcQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [win32] - '@oxc-transform/binding-win32-ia32-msvc@0.132.0': resolution: {integrity: sha512-n616QqZ3hXasHytVoFjo6pLzIfo6hQwBEir0kOcaObKaAw0ZbncIe1h5a6IMnCOJGLP30WwnhwLW20tIV78MAA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxc-transform/binding-win32-ia32-msvc@0.133.0': - resolution: {integrity: sha512-zpPIZ1S3JHmSEFyyGyPYCwhOiNLyfaPifYxK8BQY21JXyHglu/wUr3/ESFrXb+XegEy/iBlWbzr3FzPtcq1MUw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [ia32] - os: [win32] - '@oxc-transform/binding-win32-x64-msvc@0.132.0': resolution: {integrity: sha512-P7A4Cz/0C0Oxa2zH/oCruzA/5EHr5RRz0x6KXYz3wwhS+dFqIBxP9yo8FKjXhKXHRKa+M+QHo+bqYiqqlVsEQg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxc-transform/binding-win32-x64-msvc@0.133.0': - resolution: {integrity: sha512-cADrfLvc/VeyvpvQS+t5ktqfyqyyGANZC5NHp++JAElacfXqq/+k8bYkjqMWzNZ3HxkJtL1qDHfZZCA9+4hlSQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [win32] - '@package-json/types@0.0.12': resolution: {integrity: sha512-uu43FGU34B5VM9mCNjXCwLaGHYjXdNincqKLaraaCW+7S2+SmiBg1Nv8bPnmschrIfZmfKNY9f3fC376MRrObw==} @@ -3246,9 +2707,6 @@ packages: cpu: [x64] os: [win32] - '@rolldown/pluginutils@1.0.0-beta.27': - resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} - '@rolldown/pluginutils@1.0.0-beta.45': resolution: {integrity: sha512-Le9ulGCrD8ggInzWw/k2J8QcbPz7eGIOWqfJ2L+1R0Opm7n6J37s2hiDWlh6LJN0Lk9L5sUzMvRHKW7UxBZsQA==} @@ -3659,18 +3117,6 @@ packages: '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} - '@types/babel__core@7.20.5': - resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - - '@types/babel__generator@7.27.0': - resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} - - '@types/babel__template@7.4.4': - resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - - '@types/babel__traverse@7.28.0': - resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} - '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} @@ -3973,12 +3419,6 @@ packages: engines: {node: '>=20'} hasBin: true - '@vitejs/plugin-react@4.7.0': - resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} - engines: {node: ^14.18.0 || >=16.0.0} - peerDependencies: - vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 - '@vitejs/plugin-vue-jsx@5.1.5': resolution: {integrity: sha512-jIAsvHOEtWpslLOI2MeElGFxH7M8pM83BU/Tor4RLyiwH0FM4nUW3xdvbw20EeU9wc5IspQwMq225K3CMnJEpA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -3986,13 +3426,6 @@ packages: vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 vue: ^3.0.0 - '@vitejs/plugin-vue@5.2.4': - resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==} - engines: {node: ^18.0.0 || >=20.0.0} - peerDependencies: - vite: ^5.0.0 || ^6.0.0 - vue: ^3.2.25 - '@vitejs/plugin-vue@6.0.7': resolution: {integrity: sha512-km+p+XdSz9Sxm5rqUbqcSfZYaAniKxWBj1KURl+Jr7UaPvvX7BmaWMdP69I5rrFDeQGyxAG7NXdc57vz+snhWg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -4191,10 +3624,6 @@ packages: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} - accepts@1.3.8: - resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} - engines: {node: '>= 0.6'} - accepts@2.0.0: resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} engines: {node: '>= 0.6'} @@ -4278,9 +3707,6 @@ packages: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} - array-flatten@1.1.1: - resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} - array-includes@3.1.9: resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} engines: {node: '>= 0.4'} @@ -4437,10 +3863,6 @@ packages: birpc@4.0.0: resolution: {integrity: sha512-LShSxJP0KTmd101b6DRyGBj57LZxSDYWKitQNW/mi8GRMvZb078Uf9+pveax1DrVL89vm7mWe+TovdI/UDOuPw==} - body-parser@1.20.6: - resolution: {integrity: sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - body-parser@2.3.0: resolution: {integrity: sha512-2cGmJupaNgg+QUwVLAucDuWuoMZ6EX9iHDRswZ5lsNYEmwPaRknMPCLZz07yTzVq/83p4o/wzbDZbBrTvGGTIw==} engines: {node: '>=18'} @@ -4516,9 +3938,6 @@ packages: caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} - caniuse-api@4.0.0: - resolution: {integrity: sha512-B0hQ1OLyJuHTQSOWXvwibWqM6DCoqJdvBA6X1S/53bd4XU7LJ1yurIPlrsouol3mw1jh9pGI4ivubSpmJeIqCA==} - caniuse-lite@1.0.30001799: resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==} @@ -4611,10 +4030,6 @@ packages: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} - content-disposition@0.5.4: - resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} - engines: {node: '>= 0.6'} - content-disposition@1.1.0: resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} engines: {node: '>=18'} @@ -4642,16 +4057,6 @@ packages: cookie-es@3.1.1: resolution: {integrity: sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg==} - cookie-parser@1.4.7: - resolution: {integrity: sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==} - engines: {node: '>= 0.8.0'} - - cookie-signature@1.0.6: - resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} - - cookie-signature@1.0.7: - resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==} - cookie-signature@1.2.2: resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} engines: {node: '>=6.6.0'} @@ -4745,36 +4150,18 @@ packages: peerDependencies: postcss: ^8.5.13 - cssnano-preset-default@8.0.2: - resolution: {integrity: sha512-+jQAqIKCqMmBjZs7741XkilU93ITZ/EW8gjAkMmujdCzfDkfjrDBv2VqkSu29Fzeig/0rZ3S9IAwfPLlmXEUfQ==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - cssnano-utils@5.0.3: resolution: {integrity: sha512-ynIREMICLxkxm7e9bCR9sh75s4Q5drICi0ua1yxo5jH2XPBqSKkl4dOh4EbFqtUmnTMhRffHgYL0EKKkMjtJTg==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - cssnano-utils@6.0.1: - resolution: {integrity: sha512-zk65GIxA8tCjqVk7nTm1mE+ZKxtnxAvU5JSUaBLXbAr3ZF7IOvz3fbPOnEDvZKhnS7GOIitXTS5BgehLzNoc8Q==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - cssnano@7.1.9: resolution: {integrity: sha512-uPR75+5Dk/WJ/YSPR1/YDHdwMM9c5FsaARljfKWgeCKLKOtJ0we21xy/RcCjn53fZnD/f6yYEIZ8pu18+GnbNQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - cssnano@8.0.2: - resolution: {integrity: sha512-K+a76gA1v0/CsYgcsE95HGGyIuPKxpQSetwSwz4nHEM8fFXqSkzq2JzEXFL8v5+CCjxzVVVhPcTK3Oo8SaF/xA==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - csso@5.0.5: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} @@ -4828,14 +4215,6 @@ packages: sqlite3: optional: true - debug@2.6.9: - resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -4899,10 +4278,6 @@ packages: destr@2.0.5: resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} - destroy@1.2.0: - resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} - engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -5258,10 +4633,6 @@ packages: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} - express@4.22.2: - resolution: {integrity: sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==} - engines: {node: '>= 0.10.0'} - express@5.2.1: resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} engines: {node: '>= 18'} @@ -5334,10 +4705,6 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - finalhandler@1.3.2: - resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==} - engines: {node: '>= 0.8'} - finalhandler@2.1.1: resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} engines: {node: '>= 18.0.0'} @@ -5374,10 +4741,6 @@ packages: fraction.js@5.3.4: resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} - fresh@0.5.2: - resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} - engines: {node: '>= 0.6'} - fresh@2.0.0: resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} engines: {node: '>= 0.8'} @@ -5583,10 +4946,6 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} - iconv-lite@0.4.24: - resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} - engines: {node: '>=0.10.0'} - iconv-lite@0.6.3: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} @@ -6095,10 +5454,6 @@ packages: mdn-data@2.27.1: resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} - media-typer@0.3.0: - resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} - engines: {node: '>= 0.6'} - media-typer@1.1.0: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} @@ -6106,9 +5461,6 @@ packages: memory-cache@0.2.0: resolution: {integrity: sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA==} - merge-descriptors@1.0.3: - resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} - merge-descriptors@2.0.0: resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} engines: {node: '>=18'} @@ -6120,35 +5472,18 @@ packages: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} - methods@1.1.2: - resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} - engines: {node: '>= 0.6'} - micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} - mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - mime-db@1.54.0: resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} engines: {node: '>= 0.6'} - mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - mime-types@3.0.2: resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} engines: {node: '>=18'} - mime@1.6.0: - resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} - engines: {node: '>=4'} - hasBin: true - mime@4.1.0: resolution: {integrity: sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==} engines: {node: '>=16'} @@ -6222,9 +5557,6 @@ packages: resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} - ms@2.0.0: - resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -6252,10 +5584,6 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - negotiator@0.6.3: - resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} - engines: {node: '>= 0.6'} - negotiator@1.0.0: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} @@ -6363,19 +5691,6 @@ packages: '@types/node': optional: true - nuxt@4.4.8: - resolution: {integrity: sha512-r/DGE4cNkEDclOw9tbJ18zqu+ix3me+7QCfumPdl5lBXGWgCajskzuq/HzDkHKfIZsn7ACVEjMLRNA2teh++bQ==} - engines: {node: ^22.12.0 || ^24.11.0 || >=26.0.0} - hasBin: true - peerDependencies: - '@parcel/watcher': ^2.1.0 - '@types/node': '>=18.12.0' - peerDependenciesMeta: - '@parcel/watcher': - optional: true - '@types/node': - optional: true - nypm@0.6.7: resolution: {integrity: sha512-s3ds97SD5pd1dULE+tHUk1DrV0cSHOnsfpcdGATJ8JpBo21DoKqN9exTH4/2nhPQNOLomBdTFMicN94S4DrZrQ==} engines: {node: '>=18'} @@ -6453,26 +5768,14 @@ packages: resolution: {integrity: sha512-7h3fOlgDwkIYxxKfGwCNejaLhH90Pvx+fttdPN7nRbWHxm6QSYcxW3IKjfxQVUeg+z1X2HZdOSY3rHkVqgxH1g==} engines: {node: ^20.19.0 || >=22.12.0} - oxc-minify@0.133.0: - resolution: {integrity: sha512-6bNsYU+5WNIaNHB16zHnL24cUaJuKiPzUvjENoMale3+U8ZBMbGYgdgt//frx0ge7UcgEGIpqtukGGNPT0kxfQ==} - engines: {node: ^20.19.0 || >=22.12.0} - oxc-parser@0.132.0: resolution: {integrity: sha512-+0LAPHaqtfQlvWdpaAa09SmOaZZgP8C552xosEkGJ4+ruEwP1Vgx+sqBgcBCNfR6KDCmagGOZTde8wmAvcI/Hg==} engines: {node: ^20.19.0 || >=22.12.0} - oxc-parser@0.133.0: - resolution: {integrity: sha512-661RSx+ZcjBmjBYid+Fpp/2F5EbtildpeoZh5HdgnGs+jZ03nqQEQW8yGkt4BGyOC3OMPDQQRl8M5kqD2/g6jw==} - engines: {node: ^20.19.0 || >=22.12.0} - oxc-transform@0.132.0: resolution: {integrity: sha512-DmP0+4kzpXoMvv08qPCD4aI6mAIzrEq15Yt9e6wXCNtOz6jEDHPpueusDa2/pvjRAqtNV37YxUUeX7cfCI4dpA==} engines: {node: ^20.19.0 || >=22.12.0} - oxc-transform@0.133.0: - resolution: {integrity: sha512-9lt2b+hkG6yqe0fUDMHhMk7rgI9uTjNxU9wauQiYnHzc4kZI8JP/OhBqXTIJQTrqRJ8CkSH3O5AhQ13ke28yNg==} - engines: {node: ^20.19.0 || >=22.12.0} - oxc-walker@1.0.0: resolution: {integrity: sha512-eMsHflAGfOskpWxtp9xP/f5b96XLEU8ifTd2gOOCkdux9HMxKGy5S1ru0Gh1B3aPu+YbfmWUUVkcb7MrZz3XyQ==} peerDependencies: @@ -6536,9 +5839,6 @@ packages: resolution: {integrity: sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==} engines: {node: 18 || 20 || >=22} - path-to-regexp@0.1.13: - resolution: {integrity: sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==} - path-to-regexp@8.4.2: resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} @@ -6605,144 +5905,72 @@ packages: peerDependencies: postcss: ^8.5.13 - postcss-colormin@8.0.1: - resolution: {integrity: sha512-qBY4ABQ6d8/mk5RRZHwMllrZMxeMey3azVY2dZUEk+RgiUC4ARdPR3/AITzNqqKTbvW/3y/MJKinDrzwqn8RDQ==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-convert-values@7.0.12: resolution: {integrity: sha512-xurKu5qqk4viR3Cp3p4xBR4KfnZm4w4ys6+UBwBmeuBSNkH7+DtLnYOYnOffgtE4yx8sH9S1VZ6RAAvROXzP2Q==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-convert-values@8.0.1: - resolution: {integrity: sha512-IdOSIX3BzfMvCc1TAHIha2gfy17xnb5vfML8e2BIKARnFOghksESfaSAB/3CXgyLfMozZAbTRPVQF5dbuKOidw==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-discard-comments@7.0.8: resolution: {integrity: sha512-CvvS5S9WrXblFXCEJ9nVo+4z+eA7zSC7Z88V1HEJuwlQhlFnYTIjg1xJY+BCUiG2bvICap2tXii4mP22BD108Q==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-discard-comments@8.0.1: - resolution: {integrity: sha512-FDvzm3tXlEsQBO2XQgnta5ugsAqwBrgWH+j5QgXpegEIDYA0VPnZg2aP7LtmWtC49POskeIhXesFiU/k3NyFHA==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-discard-duplicates@7.0.4: resolution: {integrity: sha512-VBNn1+EuMZkeGVVtz0gRfbNGtx9IFgAsAV+E2pHtXPrp4qfGBkhTIiAuE/wrb+Y6Pakg9NewAlfTpYIFAWODtw==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-discard-duplicates@8.0.1: - resolution: {integrity: sha512-stTDXkI8YkCUfADurQhp03oq5ynsgSx6Qrw5B1swds6oTHtAeOZ9I0SHGK8cY/VpWUsIYFDWMs3IWf9jIEfFvA==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-discard-empty@7.0.3: resolution: {integrity: sha512-M2pyjQCU+/7cMHVtL6bKTHjv0lZnPLMpicgr67Dlth7AbuV9gjVTtUqaRwn6Pp6BwSDspUzhz8SaUrRykJU5Dw==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-discard-empty@8.0.1: - resolution: {integrity: sha512-Zv4fM1Yfhk71tbt6gfiptbL6jDHi+7apSnaMeaO9n1uET+1embrXQw5m93Zp5x28UyQSuv+AVkFY193jdwZ33w==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-discard-overridden@7.0.3: resolution: {integrity: sha512-aNovXo9UsZuRNLzHJtp13lHIvinDPfiXBPePpXkSjCbgp++iU2FqE+YxvjIsg6EdyPZsASFbfu+JcBFVsErXIQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-discard-overridden@8.0.1: - resolution: {integrity: sha512-ykt4fvrC7yYGzbxKyqBVjDCbsjF/11JgWK8enrdkobRyqqEtb/uDUCbKOGdvrK8X7BrShW8Lv5cCRNbdkNHGkQ==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-merge-longhand@7.0.7: resolution: {integrity: sha512-b3mfYUxR388u5Pt0HPcVIUtUDn/k15UfTY9M+ORW+meCR6JLNxoZffiYvXyOYQoRYQNZyX/UFkMCM/mNHxe1qA==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-merge-longhand@8.0.1: - resolution: {integrity: sha512-huTfSYgQ13O81SFvAuOi7GWnO48vvybjj3xF+X3qUoPjzvvaLpJH5DcUqqXcwOEulZUcvaV4s0V9WtWs+IAQPA==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-merge-rules@7.0.11: resolution: {integrity: sha512-SJUPM18g2BmPhf8BVlbwqWz4aK3pLu6u6xjfwEzra7xL6IBR10sUaiB++EzqcVfadPHrKBSMlNdP+XieykhI+Q==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-merge-rules@8.0.1: - resolution: {integrity: sha512-o3rk4UpnPNg469tklYwbR/NtvKc/f/wJiVDTnNQ/EFPw/LeiPOHUCvV1GIBQIZHGrBAYdPjToK6a+ojYprsrxQ==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-minify-font-values@7.0.3: resolution: {integrity: sha512-yilG/VOaNI74IylQvAQQxm3/wZVBkXyYUqNUAdxqwtbWUXPsbK1q8Ms0mL83v+f8YicgcyfYCRZtWACUdYajpA==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-minify-font-values@8.0.1: - resolution: {integrity: sha512-L8Nzs/PRlBSPrLdY/7rAiU5ZN5800+2J/4LRbfyG8SJnPljmgMaXVmQiCklvRS+yObfVRNtvmk/Ean/eoYcSeg==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-minify-gradients@7.0.5: resolution: {integrity: sha512-YraROyQRg3BI1+Hg8E05B/JPdnTm8EDSVu4P2BxdM+CRiOyfmou809+chGIqo6fQqwjPGQ947nbGncSjmTU1WQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-minify-gradients@8.0.1: - resolution: {integrity: sha512-qf+4s/hZMqTwpWN2teqf6+1yvR/SZK5HgHqXYuACeJXV7ABe7AXtBEomgxagUzcN4bSnmqBh5vnIml0dYqykYg==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-minify-params@7.0.9: resolution: {integrity: sha512-R8itbB8BhlpoYyBm1ou0dD+vJnQ3F6adQipR4UnkCHUwlo+S9WXJaDRg1RHjC8YVAtIdrQzSWvJl40HnGDTKjA==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-minify-params@8.0.1: - resolution: {integrity: sha512-L0h3H59deFfFg0wQN1NVaS/8E/LfGvaMuZKGO7siwlG995zo3OshtQyRkqKdVqcBwAORBvZ1nDZrKPLRapYkQw==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-minify-selectors@7.1.2: resolution: {integrity: sha512-aQtrEWKwqafNlExcKHQvPGsXR2+vlUqqJtf5XsCQcgsSb5PL4wlujWBYDJuWsP4UnQX1YHDHU8qRlD+1PzTQ+Q==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-minify-selectors@8.0.2: - resolution: {integrity: sha512-3icdxc/zght5UAizdwqZBDE2KOWHf1jMQCxET6iLACeNlRxfTPyXS0/COpGk8CQ2cECyaEKTRUd/i/k8Gxmz4g==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-nested@7.0.2: resolution: {integrity: sha512-5osppouFc0VR9/VYzYxO03VaDa3e8F23Kfd6/9qcZTUI8P58GIYlArOET2Wq0ywSl2o2PjELhYOFI4W7l5QHKw==} engines: {node: '>=18.0'} @@ -6755,144 +5983,72 @@ packages: peerDependencies: postcss: ^8.5.13 - postcss-normalize-charset@8.0.1: - resolution: {integrity: sha512-xzqr36F8UeIZOvOHsf3aul+RVJCADvSwuwpMLgizqKjisHZpBfztgW0XFLBfJvz9pJgaStaOXAtGb0zLqT6B0w==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-normalize-display-values@7.0.3: resolution: {integrity: sha512-ldsCX0QIt05pKIOobZtVQ48wXJecr+czw4+e1/YjVhLMqslShgpVxgPtI2CefURR8oyVoYaU/l829MMwExDMLw==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-normalize-display-values@8.0.1: - resolution: {integrity: sha512-ZDWOijOK1FFMlpgiQCUO9fCNKd7HJ9L7z9HWEq4iyubnUFWzdTSwm/LcrMbNW6iZ1oAtqeLYA0WA3xHszOI08g==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-normalize-positions@7.0.4: resolution: {integrity: sha512-VEvlpeGd3Ju1Hqa/oN4jaP3+ms4laYwkEL9N9u+B6k54PZjXbW1n6wI+aVprf1BQXlCYpS5+1pl/7/vHiKgARg==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-normalize-positions@8.0.1: - resolution: {integrity: sha512-uuivan2poSqbE48ST4do20dGaFUeXey9/H8rhHzoyVHB2I6BmkoVLZ/C9+BRjUlpaAFYVOoDY7epkiidzaYbvA==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-normalize-repeat-style@7.0.4: resolution: {integrity: sha512-6mPKlY/8cSaDHxX502wERADarJsccwlky6yIrOapHH2ZgfoKAV94SbiTKfKEs4EEpdazuc3J72WsqeYk7hp9+Q==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-normalize-repeat-style@8.0.1: - resolution: {integrity: sha512-q2hq5fmKxk29K6DjKA3nZ17Q2dtjhLYFNmFweKALmooUqx6UWAHF1bBoWTu/EqlJ88josb82A/J0Atj9LJUmpQ==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-normalize-string@7.0.3: resolution: {integrity: sha512-HnEQPUchi1eznmDKEYrKUTqrprEq97SrpUYClgUkv7V2zRODD9DFoUsYU+m9ZOetmD5ku7fEMZB/lwy8IT6xVQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-normalize-string@8.0.1: - resolution: {integrity: sha512-+Wf+kQJhm1WgSGEAuUaswE9rdpR9QbrKRVemcVHs6rhOoOTVIdAbgaicftfYA6vLM346P8onRzkEVbFN29ktKQ==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-normalize-timing-functions@7.0.3: resolution: {integrity: sha512-zmEzHdvpZBZu0OKlbJSfgASQvaayyAoVuWtvyr34IJ/LyS+DaOKvvR3EvFJ9RWWtNIx+CMvO125OVophaxNYew==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-normalize-timing-functions@8.0.1: - resolution: {integrity: sha512-W8/tvwRlm3T+yjGkg0IRTF4bvHj0vILYr/LOogCrJKHz2ey2HFRwfsAA8Bk9N4BGR7z7WmmDu/KzzwhJ6FoGPQ==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-normalize-unicode@7.0.9: resolution: {integrity: sha512-DRAdWfeh/TjmhLJsw91vdiWCnUod9iwvM7xyS02/nF/sLsCR3A8l3pztrSUrWG8DSBqfX7yEk9FM0USaVJ2mSg==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-normalize-unicode@8.0.1: - resolution: {integrity: sha512-Ad0YHNRBp4WHEOYUM/4wL/8MoL2fimEF8se/0q+Rt/owMzYpbxsypC1P8fN/oluwoRmRKdNVX7X2oycEobPWcQ==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-normalize-url@7.0.3: resolution: {integrity: sha512-CL93wmloq5qsffmFv+bw24MIRbmhHrp53qoh1LDAb/5TtjWEXI/np4xcP/Gw9oWCb2XyWnqHYLDUwiKRoJBA1Q==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-normalize-url@8.0.1: - resolution: {integrity: sha512-tkYcip6pCDY806xuxpJYqMW2M3/623jzGFJmz3m5Us47q8P28+gbRZxaea3Rr/CmwwLUiVlh+BTGYwQ6gvaP8A==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-normalize-whitespace@7.0.3: resolution: {integrity: sha512-FdHjjn+Ht5Z2ZRjNOmeCbNq6lq09sUYKpmlF/Aq0XjVNSLTL6fmHlA/3swN2wP2caY9GV/tjSDcIIyS7aN7W0A==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-normalize-whitespace@8.0.1: - resolution: {integrity: sha512-XzORadNfSrKWDZZpgAEHPKINKx8r9r9RIfE9c70g/HThdpbmPHhDYCodHSVESDxmKeySAYw1p4liuBCf7j6LyA==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-ordered-values@7.0.4: resolution: {integrity: sha512-nubSi49hDHQk4E8KIj+IbLY8Bg+8OcSUEhgyolgM+atnOvXjV7EjaR6bac4YGZoFyPa9mWoAF3EaYbWdFkKqVg==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-ordered-values@8.0.1: - resolution: {integrity: sha512-OLXq5lR1yk3KWQ1FPK6aWjFFdktHE9f9kb8cnt4LmIw7w30DnzgD9+sOVYJc5HenkWCX8i1MJhhFwmqc/GYqLg==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-reduce-initial@7.0.9: resolution: {integrity: sha512-ztTNPdIxXTxtBcG03E9u8v44M4ElXbMIRT7pf2onlquGula0Y83nKKxqM22FA/hMgkfCjN7ohevkVlaNwI8iOQ==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-reduce-initial@8.0.1: - resolution: {integrity: sha512-+aQsR6+61KRoIfcFNLP3v9RM7+0iYOTtPnjl1wr6JqMW1zx6S+t2ktHRefXwacFdHIDj5+ETG0KY7K3+SGQ4Nw==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-reduce-transforms@7.0.3: resolution: {integrity: sha512-FXsnN9ZwcZTT8Yf8cAHA8qIGUXcX6WfLd9JoYhrdDfmvsVhhfqkkv7m4AC3rwFOfz+GzkUa87OCKF9dUcicd+g==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-reduce-transforms@8.0.1: - resolution: {integrity: sha512-x71slHVykiFi5RuKEXM0wgYpY2PngC78x6R8TnZhHF3lhqt+u/w3MGwYLX+2t5O87ssRiMfEAhQH+3J4QwVzCw==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-selector-parser@7.1.4: resolution: {integrity: sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==} engines: {node: '>=4'} @@ -6903,24 +6059,12 @@ packages: peerDependencies: postcss: ^8.5.13 - postcss-svgo@8.0.1: - resolution: {integrity: sha512-HpnvWii7W0/FPrsejJa6ZTi0kNtTJP/Iba7CUMPX0xPV6QpnndOp+SDP74tFtgjA2cYKYNWJPOlmLXMsvi/9yA==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-unique-selectors@7.0.7: resolution: {integrity: sha512-d+sCkaRnSefghOUdH8CMJZV9yUQhj2ojpe8Nw/lA+LV1UOfeleGkLTl6XdCFFSai9UJ+DJPb69FFuqthXYsY8w==} engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} peerDependencies: postcss: ^8.5.13 - postcss-unique-selectors@8.0.1: - resolution: {integrity: sha512-+xvKI5+/Cl8yYQwxDV39Uhuc4WV951xngFvPPjiPj2NIbIfm6vbbRTXblyw0FioLkIoGlw+7qUcY1h2YhaZYgw==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} @@ -7013,10 +6157,6 @@ packages: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} - raw-body@2.5.3: - resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==} - engines: {node: '>= 0.8'} - raw-body@3.0.2: resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} engines: {node: '>= 0.10'} @@ -7035,10 +6175,6 @@ packages: react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} - react-refresh@0.17.0: - resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} - engines: {node: '>=0.10.0'} - react-router@7.15.1: resolution: {integrity: sha512-R8rl9HhgikFYoPJymnUtPXWbnDb3oget6lQnfIoupbt61aT9aOhRkDsY2XRhZRyX1Z/8a5sL74fXmFNm3NRK5A==} engines: {node: '>=20.0.0'} @@ -7244,10 +6380,6 @@ packages: engines: {node: '>=10'} hasBin: true - send@0.19.2: - resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==} - engines: {node: '>= 0.8.0'} - send@1.2.1: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} @@ -7269,10 +6401,6 @@ packages: serve-placeholder@2.0.2: resolution: {integrity: sha512-/TMG8SboeiQbZJWRlfTCqMs2DD3SZgWp0kDQePz9yUuCnDfDh/92gf7/PxGhzXTKBIPASIHxFcZndoNbp6QOLQ==} - serve-static@1.16.3: - resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==} - engines: {node: '>= 0.8.0'} - serve-static@2.2.1: resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} engines: {node: '>= 18'} @@ -7501,12 +6629,6 @@ packages: peerDependencies: postcss: ^8.5.13 - stylehacks@8.0.1: - resolution: {integrity: sha512-Gv095oTD0N+BdJALNFDsxZpETHZLTxbOl5RyIO7y6VAE6sR3z0MnV3Nix7N0IATNldNTrkvSASp2KR1Yt526HA==} - engines: {node: ^22.11.0 || ^24.11.0 || >=26.0} - peerDependencies: - postcss: ^8.5.15 - stylis@4.2.0: resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} @@ -7671,10 +6793,6 @@ packages: resolution: {integrity: sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==} engines: {node: '>=20'} - type-is@1.6.18: - resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} - engines: {node: '>= 0.6'} - type-is@2.1.0: resolution: {integrity: sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==} engines: {node: '>= 18'} @@ -7802,9 +6920,6 @@ packages: resolution: {integrity: sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg==} engines: {node: ^20.19.0 || >=22.12.0} - unrouting@0.1.7: - resolution: {integrity: sha512-+0hfD+CVWtD636rc5Fn9VEjjTEDhdqgMpbwAuVoUmydSHDaMNiFW93SJG4LV++RoGSEAyvQN5uABAscYpDphpQ==} - unrs-resolver@1.12.2: resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==} @@ -7901,10 +7016,6 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - utils-merge@1.0.1: - resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} - engines: {node: '>= 0.4.0'} - uuid@11.1.1: resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==} hasBin: true @@ -7965,37 +7076,6 @@ packages: vue-tsc: optional: true - vite-plugin-checker@0.14.4: - resolution: {integrity: sha512-Tw0U9UgHIRiZ+Yoe4Gh0RrYoBiCVmO9j4tomVdYr0KUjUsqXMPhqW8ouoSWmOzGp5Iimipbl3bNXZcK7OeP7Qg==} - engines: {node: '>=20.19.0'} - peerDependencies: - '@biomejs/biome': '>=2.4.12' - eslint: '>=9.39.4' - meow: ^13.2.0 || ^14.0.0 - optionator: ^0.9.4 - oxlint: '>=1' - stylelint: '>=16.26.1' - typescript: '*' - vite: '>=5.4.21' - vue-tsc: ~2.2.10 || ^3.0.0 - peerDependenciesMeta: - '@biomejs/biome': - optional: true - eslint: - optional: true - meow: - optional: true - optionator: - optional: true - oxlint: - optional: true - stylelint: - optional: true - typescript: - optional: true - vue-tsc: - optional: true - vite-plugin-inspect@11.4.1: resolution: {integrity: sha512-ShOFe2PURXGvRS5OrgmOLZOCwDTD7dEBVt0tMpFPKb9AsvqXKCRGM8QgKrUbRbJYFXScHvDPpGRd28rYidC0tA==} engines: {node: '>=14'} @@ -8592,16 +7672,6 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 - - '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7)': - dependencies: - '@babel/core': 7.29.7 - '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-typescript@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 @@ -8702,18 +7772,6 @@ snapshots: transitivePeerDependencies: - magicast - '@dxup/nuxt@0.4.1(magicast@0.5.3)(typescript@6.0.3)': - dependencies: - '@dxup/unimport': 0.1.2 - '@nuxt/kit': 4.4.8(magicast@0.5.3) - chokidar: 5.0.0 - pathe: 2.0.3 - tinyglobby: 0.2.17 - optionalDependencies: - typescript: 6.0.3 - transitivePeerDependencies: - - magicast - '@dxup/unimport@0.1.2': {} '@emnapi/core@1.10.0': @@ -9384,44 +8442,6 @@ snapshots: - magicast - supports-color - '@nuxt/cli@3.36.0(@nuxt/schema@4.4.8)(cac@6.7.14)(magicast@0.5.3)': - dependencies: - '@bomb.sh/tab': 0.0.16(cac@6.7.14)(citty@0.2.2) - '@clack/prompts': 1.6.0 - c12: 3.3.4(magicast@0.5.3) - citty: 0.2.2 - confbox: 0.2.4 - consola: 3.4.2 - debug: 4.4.3 - defu: 6.1.7 - exsolve: 1.0.8 - fuse.js: 7.4.2 - fzf: 0.5.2 - giget: 3.3.0 - jiti: 2.7.0 - listhen: 1.10.0(srvx@0.11.17) - nypm: 0.6.7 - ofetch: 1.5.1 - ohash: 2.0.11 - pathe: 2.0.3 - perfect-debounce: 2.1.0 - pkg-types: 2.3.1 - scule: 1.3.0 - semver: 7.8.5 - srvx: 0.11.17 - std-env: 4.1.0 - tinyclip: 0.1.14 - tinyexec: 1.2.4 - ufo: 1.6.4 - youch: 4.1.1 - optionalDependencies: - '@nuxt/schema': 4.4.8 - transitivePeerDependencies: - - cac - - commander - - magicast - - supports-color - '@nuxt/devalue@2.0.2': {} '@nuxt/devtools-kit@2.6.4(magicast@0.3.5)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))': @@ -9544,47 +8564,6 @@ snapshots: - utf-8-validate - vue - '@nuxt/devtools@3.2.4(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3))': - dependencies: - '@nuxt/devtools-kit': 3.2.4(magicast@0.5.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) - '@nuxt/devtools-wizard': 3.2.4 - '@nuxt/kit': 4.4.8(magicast@0.5.3) - '@vue/devtools-core': 8.1.3(vue@3.5.38(typescript@6.0.3)) - '@vue/devtools-kit': 8.1.3 - birpc: 4.0.0 - consola: 3.4.2 - destr: 2.0.5 - error-stack-parser-es: 1.0.5 - execa: 8.0.1 - fast-npm-meta: 1.5.1 - get-port-please: 3.2.0 - hookable: 6.1.1 - image-meta: 0.2.2 - is-installed-globally: 1.0.0 - launch-editor: 2.14.1 - local-pkg: 1.2.1 - magicast: 0.5.3 - nypm: 0.6.7 - ohash: 2.0.11 - pathe: 2.0.3 - perfect-debounce: 2.1.0 - pkg-types: 2.3.1 - semver: 7.8.5 - simple-git: 3.36.0 - sirv: 3.0.2 - structured-clone-es: 2.0.0 - tinyglobby: 0.2.17 - vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vite-plugin-inspect: 11.4.1(@nuxt/kit@4.4.8(magicast@0.5.3))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)) - vite-plugin-vue-tracer: 1.4.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) - which: 6.0.1 - ws: 8.21.0 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - - vue - '@nuxt/kit@3.21.7(magicast@0.3.5)': dependencies: c12: 3.3.4(magicast@0.3.5) @@ -9753,76 +8732,6 @@ snapshots: - uploadthing - xml2js - '@nuxt/nitro-server@4.4.8(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(db0@0.3.4)(ioredis@5.11.1)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(oxc-parser@0.133.0)(rolldown@1.1.3)(typescript@6.0.3)': - dependencies: - '@nuxt/devalue': 2.0.2 - '@nuxt/kit': 4.4.8(magicast@0.5.3) - '@unhead/vue': 2.1.15(vue@3.5.38(typescript@6.0.3)) - '@vue/shared': 3.5.38 - consola: 3.4.2 - defu: 6.1.7 - destr: 2.0.5 - devalue: 5.8.1 - errx: 0.1.0 - escape-string-regexp: 5.0.0 - exsolve: 1.0.8 - h3: 1.15.11 - impound: 1.1.5 - klona: 2.0.6 - mocked-exports: 0.1.1 - nitropack: 2.13.4(oxc-parser@0.133.0)(rolldown@1.1.3) - nuxt: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) - nypm: 0.6.7 - ohash: 2.0.11 - pathe: 2.0.3 - rou3: 0.8.1 - std-env: 4.1.0 - ufo: 1.6.4 - unctx: 2.5.0 - unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1) - vue: 3.5.38(typescript@6.0.3) - vue-bundle-renderer: 2.2.0 - vue-devtools-stub: 0.1.0 - optionalDependencies: - '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@electric-sql/pglite' - - '@libsql/client' - - '@netlify/blobs' - - '@planetscale/database' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bare-abort-controller - - bare-buffer - - better-sqlite3 - - db0 - - drizzle-orm - - encoding - - idb-keyval - - ioredis - - magicast - - mysql2 - - oxc-parser - - react-native-b4a - - rolldown - - sqlite3 - - srvx - - supports-color - - typescript - - uploadthing - - xml2js - '@nuxt/schema@3.21.7': dependencies: '@vue/shared': 3.5.38 @@ -9831,14 +8740,6 @@ snapshots: pkg-types: 2.3.1 std-env: 4.1.0 - '@nuxt/schema@4.4.8': - dependencies: - '@vue/shared': 3.5.38 - defu: 6.1.7 - pathe: 2.0.3 - pkg-types: 2.3.1 - std-env: 4.1.0 - '@nuxt/telemetry@2.8.0(@nuxt/kit@3.21.7(magicast@0.5.3))': dependencies: '@nuxt/kit': 3.21.7(magicast@0.5.3) @@ -9848,15 +8749,6 @@ snapshots: rc9: 3.0.1 std-env: 4.1.0 - '@nuxt/telemetry@2.8.0(@nuxt/kit@4.4.8(magicast@0.5.3))': - dependencies: - '@nuxt/kit': 4.4.8(magicast@0.5.3) - citty: 0.2.2 - consola: 3.4.2 - ofetch: 2.0.0-alpha.3 - rc9: 3.0.1 - std-env: 4.1.0 - '@nuxt/test-utils@3.17.2(@playwright/test@1.60.0)(@types/node@24.7.2)(@vue/test-utils@2.4.6)(jiti@2.7.0)(jsdom@27.0.1)(lightningcss@1.32.0)(magicast@0.5.3)(playwright-core@1.60.0)(terser@5.48.0)(typescript@5.9.3)(vitest@4.1.8)(yaml@2.9.0)': dependencies: '@nuxt/kit': 3.21.7(magicast@0.5.3) @@ -9969,163 +8861,56 @@ snapshots: - vue-tsc - yaml - '@nuxt/vite-builder@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@types/node@24.7.2)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.32.0)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3))(yaml@2.9.0)': - dependencies: - '@nuxt/kit': 4.4.8(magicast@0.5.3) - '@rollup/plugin-replace': 6.0.3(rollup@4.62.2) - '@vitejs/plugin-vue': 6.0.7(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) - '@vitejs/plugin-vue-jsx': 5.1.5(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) - autoprefixer: 10.5.0(postcss@8.5.15) - consola: 3.4.2 - cssnano: 8.0.2(postcss@8.5.15) - defu: 6.1.7 - escape-string-regexp: 5.0.0 - exsolve: 1.0.8 - get-port-please: 3.2.0 - jiti: 2.7.0 - knitwork: 1.3.0 - magic-string: 0.30.21 - mlly: 1.8.2 - mocked-exports: 0.1.1 - nuxt: 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0) - nypm: 0.6.7 - pathe: 2.0.3 - pkg-types: 2.3.1 - postcss: 8.5.15 - seroval: 1.5.4 - std-env: 4.1.0 - ufo: 1.6.4 - unenv: 2.0.0-rc.24 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) - vite-node: 5.3.0(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) - vite-plugin-checker: 0.14.4(eslint@9.39.4(jiti@2.7.0))(optionator@0.9.4)(typescript@6.0.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0)) - vue: 3.5.38(typescript@6.0.3) - vue-bundle-renderer: 2.2.0 - optionalDependencies: - '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) - rolldown: 1.1.3 - rollup-plugin-visualizer: 7.0.1(rolldown@1.1.3)(rollup@4.62.2) - transitivePeerDependencies: - - '@biomejs/biome' - - '@types/node' - - eslint - - less - - lightningcss - - magicast - - meow - - optionator - - oxlint - - rollup - - sass - - sass-embedded - - stylelint - - stylus - - sugarss - - supports-color - - terser - - tsx - - typescript - - vue-tsc - - yaml - '@one-ini/wasm@0.1.1': {} '@oxc-minify/binding-android-arm-eabi@0.132.0': optional: true - '@oxc-minify/binding-android-arm-eabi@0.133.0': - optional: true - '@oxc-minify/binding-android-arm64@0.132.0': optional: true - '@oxc-minify/binding-android-arm64@0.133.0': - optional: true - '@oxc-minify/binding-darwin-arm64@0.132.0': optional: true - '@oxc-minify/binding-darwin-arm64@0.133.0': - optional: true - '@oxc-minify/binding-darwin-x64@0.132.0': optional: true - '@oxc-minify/binding-darwin-x64@0.133.0': - optional: true - '@oxc-minify/binding-freebsd-x64@0.132.0': optional: true - '@oxc-minify/binding-freebsd-x64@0.133.0': - optional: true - '@oxc-minify/binding-linux-arm-gnueabihf@0.132.0': optional: true - '@oxc-minify/binding-linux-arm-gnueabihf@0.133.0': - optional: true - '@oxc-minify/binding-linux-arm-musleabihf@0.132.0': optional: true - '@oxc-minify/binding-linux-arm-musleabihf@0.133.0': - optional: true - '@oxc-minify/binding-linux-arm64-gnu@0.132.0': optional: true - '@oxc-minify/binding-linux-arm64-gnu@0.133.0': - optional: true - '@oxc-minify/binding-linux-arm64-musl@0.132.0': optional: true - '@oxc-minify/binding-linux-arm64-musl@0.133.0': - optional: true - '@oxc-minify/binding-linux-ppc64-gnu@0.132.0': optional: true - '@oxc-minify/binding-linux-ppc64-gnu@0.133.0': - optional: true - '@oxc-minify/binding-linux-riscv64-gnu@0.132.0': optional: true - '@oxc-minify/binding-linux-riscv64-gnu@0.133.0': - optional: true - '@oxc-minify/binding-linux-riscv64-musl@0.132.0': optional: true - '@oxc-minify/binding-linux-riscv64-musl@0.133.0': - optional: true - '@oxc-minify/binding-linux-s390x-gnu@0.132.0': optional: true - '@oxc-minify/binding-linux-s390x-gnu@0.133.0': - optional: true - '@oxc-minify/binding-linux-x64-gnu@0.132.0': optional: true - '@oxc-minify/binding-linux-x64-gnu@0.133.0': - optional: true - '@oxc-minify/binding-linux-x64-musl@0.132.0': optional: true - '@oxc-minify/binding-linux-x64-musl@0.133.0': - optional: true - '@oxc-minify/binding-openharmony-arm64@0.132.0': optional: true - '@oxc-minify/binding-openharmony-arm64@0.133.0': - optional: true - '@oxc-minify/binding-wasm32-wasi@0.132.0': dependencies: '@emnapi/core': 1.10.0 @@ -10133,127 +8918,63 @@ snapshots: '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@oxc-minify/binding-wasm32-wasi@0.133.0': - dependencies: - '@emnapi/core': 1.10.0 - '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - optional: true - '@oxc-minify/binding-win32-arm64-msvc@0.132.0': optional: true - '@oxc-minify/binding-win32-arm64-msvc@0.133.0': - optional: true - '@oxc-minify/binding-win32-ia32-msvc@0.132.0': optional: true - '@oxc-minify/binding-win32-ia32-msvc@0.133.0': - optional: true - - '@oxc-minify/binding-win32-x64-msvc@0.132.0': - optional: true - - '@oxc-minify/binding-win32-x64-msvc@0.133.0': + '@oxc-minify/binding-win32-x64-msvc@0.132.0': optional: true '@oxc-parser/binding-android-arm-eabi@0.132.0': optional: true - '@oxc-parser/binding-android-arm-eabi@0.133.0': - optional: true - '@oxc-parser/binding-android-arm64@0.132.0': optional: true - '@oxc-parser/binding-android-arm64@0.133.0': - optional: true - '@oxc-parser/binding-darwin-arm64@0.132.0': optional: true - '@oxc-parser/binding-darwin-arm64@0.133.0': - optional: true - '@oxc-parser/binding-darwin-x64@0.132.0': optional: true - '@oxc-parser/binding-darwin-x64@0.133.0': - optional: true - '@oxc-parser/binding-freebsd-x64@0.132.0': optional: true - '@oxc-parser/binding-freebsd-x64@0.133.0': - optional: true - '@oxc-parser/binding-linux-arm-gnueabihf@0.132.0': optional: true - '@oxc-parser/binding-linux-arm-gnueabihf@0.133.0': - optional: true - '@oxc-parser/binding-linux-arm-musleabihf@0.132.0': optional: true - '@oxc-parser/binding-linux-arm-musleabihf@0.133.0': - optional: true - '@oxc-parser/binding-linux-arm64-gnu@0.132.0': optional: true - '@oxc-parser/binding-linux-arm64-gnu@0.133.0': - optional: true - '@oxc-parser/binding-linux-arm64-musl@0.132.0': optional: true - '@oxc-parser/binding-linux-arm64-musl@0.133.0': - optional: true - '@oxc-parser/binding-linux-ppc64-gnu@0.132.0': optional: true - '@oxc-parser/binding-linux-ppc64-gnu@0.133.0': - optional: true - '@oxc-parser/binding-linux-riscv64-gnu@0.132.0': optional: true - '@oxc-parser/binding-linux-riscv64-gnu@0.133.0': - optional: true - '@oxc-parser/binding-linux-riscv64-musl@0.132.0': optional: true - '@oxc-parser/binding-linux-riscv64-musl@0.133.0': - optional: true - '@oxc-parser/binding-linux-s390x-gnu@0.132.0': optional: true - '@oxc-parser/binding-linux-s390x-gnu@0.133.0': - optional: true - '@oxc-parser/binding-linux-x64-gnu@0.132.0': optional: true - '@oxc-parser/binding-linux-x64-gnu@0.133.0': - optional: true - '@oxc-parser/binding-linux-x64-musl@0.132.0': optional: true - '@oxc-parser/binding-linux-x64-musl@0.133.0': - optional: true - '@oxc-parser/binding-openharmony-arm64@0.132.0': optional: true - '@oxc-parser/binding-openharmony-arm64@0.133.0': - optional: true - '@oxc-parser/binding-wasm32-wasi@0.132.0': dependencies: '@emnapi/core': 1.10.0 @@ -10261,35 +8982,17 @@ snapshots: '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@oxc-parser/binding-wasm32-wasi@0.133.0': - dependencies: - '@emnapi/core': 1.10.0 - '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - optional: true - '@oxc-parser/binding-win32-arm64-msvc@0.132.0': optional: true - '@oxc-parser/binding-win32-arm64-msvc@0.133.0': - optional: true - '@oxc-parser/binding-win32-ia32-msvc@0.132.0': optional: true - '@oxc-parser/binding-win32-ia32-msvc@0.133.0': - optional: true - '@oxc-parser/binding-win32-x64-msvc@0.132.0': optional: true - '@oxc-parser/binding-win32-x64-msvc@0.133.0': - optional: true - '@oxc-project/types@0.132.0': {} - '@oxc-project/types@0.133.0': {} - '@oxc-project/types@0.137.0': {} '@oxc-project/types@0.95.0': {} @@ -10297,99 +9000,51 @@ snapshots: '@oxc-transform/binding-android-arm-eabi@0.132.0': optional: true - '@oxc-transform/binding-android-arm-eabi@0.133.0': - optional: true - '@oxc-transform/binding-android-arm64@0.132.0': optional: true - '@oxc-transform/binding-android-arm64@0.133.0': - optional: true - '@oxc-transform/binding-darwin-arm64@0.132.0': optional: true - '@oxc-transform/binding-darwin-arm64@0.133.0': - optional: true - '@oxc-transform/binding-darwin-x64@0.132.0': optional: true - '@oxc-transform/binding-darwin-x64@0.133.0': - optional: true - '@oxc-transform/binding-freebsd-x64@0.132.0': optional: true - '@oxc-transform/binding-freebsd-x64@0.133.0': - optional: true - '@oxc-transform/binding-linux-arm-gnueabihf@0.132.0': optional: true - '@oxc-transform/binding-linux-arm-gnueabihf@0.133.0': - optional: true - '@oxc-transform/binding-linux-arm-musleabihf@0.132.0': optional: true - '@oxc-transform/binding-linux-arm-musleabihf@0.133.0': - optional: true - '@oxc-transform/binding-linux-arm64-gnu@0.132.0': optional: true - '@oxc-transform/binding-linux-arm64-gnu@0.133.0': - optional: true - '@oxc-transform/binding-linux-arm64-musl@0.132.0': optional: true - '@oxc-transform/binding-linux-arm64-musl@0.133.0': - optional: true - '@oxc-transform/binding-linux-ppc64-gnu@0.132.0': optional: true - '@oxc-transform/binding-linux-ppc64-gnu@0.133.0': - optional: true - '@oxc-transform/binding-linux-riscv64-gnu@0.132.0': optional: true - '@oxc-transform/binding-linux-riscv64-gnu@0.133.0': - optional: true - '@oxc-transform/binding-linux-riscv64-musl@0.132.0': optional: true - '@oxc-transform/binding-linux-riscv64-musl@0.133.0': - optional: true - '@oxc-transform/binding-linux-s390x-gnu@0.132.0': optional: true - '@oxc-transform/binding-linux-s390x-gnu@0.133.0': - optional: true - '@oxc-transform/binding-linux-x64-gnu@0.132.0': optional: true - '@oxc-transform/binding-linux-x64-gnu@0.133.0': - optional: true - '@oxc-transform/binding-linux-x64-musl@0.132.0': optional: true - '@oxc-transform/binding-linux-x64-musl@0.133.0': - optional: true - '@oxc-transform/binding-openharmony-arm64@0.132.0': optional: true - '@oxc-transform/binding-openharmony-arm64@0.133.0': - optional: true - '@oxc-transform/binding-wasm32-wasi@0.132.0': dependencies: '@emnapi/core': 1.10.0 @@ -10397,31 +9052,15 @@ snapshots: '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@oxc-transform/binding-wasm32-wasi@0.133.0': - dependencies: - '@emnapi/core': 1.10.0 - '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - optional: true - '@oxc-transform/binding-win32-arm64-msvc@0.132.0': optional: true - '@oxc-transform/binding-win32-arm64-msvc@0.133.0': - optional: true - '@oxc-transform/binding-win32-ia32-msvc@0.132.0': optional: true - '@oxc-transform/binding-win32-ia32-msvc@0.133.0': - optional: true - '@oxc-transform/binding-win32-x64-msvc@0.132.0': optional: true - '@oxc-transform/binding-win32-x64-msvc@0.133.0': - optional: true - '@package-json/types@0.0.12': {} '@parcel/watcher-android-arm64@2.5.6': @@ -10606,8 +9245,6 @@ snapshots: '@rolldown/binding-win32-x64-msvc@1.1.3': optional: true - '@rolldown/pluginutils@1.0.0-beta.27': {} - '@rolldown/pluginutils@1.0.0-beta.45': {} '@rolldown/pluginutils@1.0.1': {} @@ -10988,27 +9625,6 @@ snapshots: '@types/aria-query@5.0.4': {} - '@types/babel__core@7.20.5': - dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 - '@types/babel__generator': 7.27.0 - '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.28.0 - - '@types/babel__generator@7.27.0': - dependencies: - '@babel/types': 7.29.7 - - '@types/babel__template@7.4.4': - dependencies: - '@babel/parser': 7.29.7 - '@babel/types': 7.29.7 - - '@types/babel__traverse@7.28.0': - dependencies: - '@babel/types': 7.29.7 - '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 @@ -11237,12 +9853,6 @@ snapshots: unhead: 2.1.15 vue: 3.5.38(typescript@5.9.3) - '@unhead/vue@2.1.15(vue@3.5.38(typescript@6.0.3))': - dependencies: - hookable: 6.1.1 - unhead: 2.1.15 - vue: 3.5.38(typescript@6.0.3) - '@unrs/resolver-binding-android-arm-eabi@1.12.2': optional: true @@ -11332,18 +9942,6 @@ snapshots: - rollup - supports-color - '@vitejs/plugin-react@4.7.0(vite@6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))': - dependencies: - '@babel/core': 7.29.7 - '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7) - '@rolldown/pluginutils': 1.0.0-beta.27 - '@types/babel__core': 7.20.5 - react-refresh: 0.17.0 - vite: 6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) - transitivePeerDependencies: - - supports-color - '@vitejs/plugin-vue-jsx@5.1.5(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3))': dependencies: '@babel/core': 7.29.7 @@ -11356,35 +9954,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue-jsx@5.1.5(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3))': - dependencies: - '@babel/core': 7.29.7 - '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-typescript': 7.29.7(@babel/core@7.29.7) - '@rolldown/pluginutils': 1.0.1 - '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.29.7) - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) - vue: 3.5.38(typescript@6.0.3) - transitivePeerDependencies: - - supports-color - - '@vitejs/plugin-vue@5.2.4(vite@6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.30(typescript@6.0.3))': - dependencies: - vite: 6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) - vue: 3.5.30(typescript@6.0.3) - '@vitejs/plugin-vue@6.0.7(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.1 vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) vue: 3.5.38(typescript@5.9.3) - '@vitejs/plugin-vue@6.0.7(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3))': - dependencies: - '@rolldown/pluginutils': 1.0.1 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) - vue: 3.5.38(typescript@6.0.3) - '@vitest/browser-playwright@4.1.8(playwright@1.60.0)(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8)': dependencies: '@vitest/browser': 4.1.8(vite@8.1.0(@types/node@22.15.3)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vitest@4.1.8) @@ -11534,16 +10109,6 @@ snapshots: optionalDependencies: vue: 3.5.38(typescript@5.9.3) - '@vue-macros/common@3.1.2(vue@3.5.38(typescript@6.0.3))': - dependencies: - '@vue/compiler-sfc': 3.5.38 - ast-kit: 2.2.0 - local-pkg: 1.2.1 - magic-string-ast: 1.0.3 - unplugin-utils: 0.3.1 - optionalDependencies: - vue: 3.5.38(typescript@6.0.3) - '@vue/babel-helper-vue-transform-on@2.0.1': {} '@vue/babel-plugin-jsx@2.0.1(@babel/core@7.29.7)': @@ -11657,12 +10222,6 @@ snapshots: '@vue/devtools-shared': 8.1.3 vue: 3.5.38(typescript@5.9.3) - '@vue/devtools-core@8.1.3(vue@3.5.38(typescript@6.0.3))': - dependencies: - '@vue/devtools-kit': 8.1.3 - '@vue/devtools-shared': 8.1.3 - vue: 3.5.38(typescript@6.0.3) - '@vue/devtools-kit@7.7.9': dependencies: '@vue/devtools-shared': 7.7.9 @@ -11734,24 +10293,12 @@ snapshots: '@vue/shared': 3.5.30 vue: 3.5.30(typescript@5.9.3) - '@vue/server-renderer@3.5.30(vue@3.5.30(typescript@6.0.3))': - dependencies: - '@vue/compiler-ssr': 3.5.30 - '@vue/shared': 3.5.30 - vue: 3.5.30(typescript@6.0.3) - '@vue/server-renderer@3.5.38(vue@3.5.38(typescript@5.9.3))': dependencies: '@vue/compiler-ssr': 3.5.38 '@vue/shared': 3.5.38 vue: 3.5.38(typescript@5.9.3) - '@vue/server-renderer@3.5.38(vue@3.5.38(typescript@6.0.3))': - dependencies: - '@vue/compiler-ssr': 3.5.38 - '@vue/shared': 3.5.38 - vue: 3.5.38(typescript@6.0.3) - '@vue/shared@3.5.30': {} '@vue/shared@3.5.38': {} @@ -11769,11 +10316,6 @@ snapshots: dependencies: event-target-shim: 5.0.1 - accepts@1.3.8: - dependencies: - mime-types: 2.1.35 - negotiator: 0.6.3 - accepts@2.0.0: dependencies: mime-types: 3.0.2 @@ -11858,8 +10400,6 @@ snapshots: call-bound: 1.0.4 is-array-buffer: 3.0.5 - array-flatten@1.1.1: {} - array-includes@3.1.9: dependencies: call-bind: 1.0.9 @@ -12018,23 +10558,6 @@ snapshots: birpc@4.0.0: {} - body-parser@1.20.6: - dependencies: - bytes: 3.1.2 - content-type: 1.0.5 - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - http-errors: 2.0.1 - iconv-lite: 0.4.24 - on-finished: 2.4.1 - qs: 6.15.2 - raw-body: 2.5.3 - type-is: 1.6.18 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - body-parser@2.3.0: dependencies: bytes: 3.1.2 @@ -12153,11 +10676,6 @@ snapshots: lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 - caniuse-api@4.0.0: - dependencies: - browserslist: 4.28.2 - caniuse-lite: 1.0.30001799 - caniuse-lite@1.0.30001799: {} chai@6.2.2: {} @@ -12234,10 +10752,6 @@ snapshots: consola@3.4.2: {} - content-disposition@0.5.4: - dependencies: - safe-buffer: 5.2.1 - content-disposition@1.1.0: {} content-type@1.0.5: {} @@ -12254,15 +10768,6 @@ snapshots: cookie-es@3.1.1: {} - cookie-parser@1.4.7: - dependencies: - cookie: 0.7.2 - cookie-signature: 1.0.6 - - cookie-signature@1.0.6: {} - - cookie-signature@1.0.7: {} - cookie-signature@1.2.2: {} cookie@0.6.0: {} @@ -12376,59 +10881,16 @@ snapshots: postcss-svgo: 7.1.3(postcss@8.5.15) postcss-unique-selectors: 7.0.7(postcss@8.5.15) - cssnano-preset-default@8.0.2(postcss@8.5.15): - dependencies: - browserslist: 4.28.2 - cssnano-utils: 6.0.1(postcss@8.5.15) - postcss: 8.5.15 - postcss-calc: 10.1.1(postcss@8.5.15) - postcss-colormin: 8.0.1(postcss@8.5.15) - postcss-convert-values: 8.0.1(postcss@8.5.15) - postcss-discard-comments: 8.0.1(postcss@8.5.15) - postcss-discard-duplicates: 8.0.1(postcss@8.5.15) - postcss-discard-empty: 8.0.1(postcss@8.5.15) - postcss-discard-overridden: 8.0.1(postcss@8.5.15) - postcss-merge-longhand: 8.0.1(postcss@8.5.15) - postcss-merge-rules: 8.0.1(postcss@8.5.15) - postcss-minify-font-values: 8.0.1(postcss@8.5.15) - postcss-minify-gradients: 8.0.1(postcss@8.5.15) - postcss-minify-params: 8.0.1(postcss@8.5.15) - postcss-minify-selectors: 8.0.2(postcss@8.5.15) - postcss-normalize-charset: 8.0.1(postcss@8.5.15) - postcss-normalize-display-values: 8.0.1(postcss@8.5.15) - postcss-normalize-positions: 8.0.1(postcss@8.5.15) - postcss-normalize-repeat-style: 8.0.1(postcss@8.5.15) - postcss-normalize-string: 8.0.1(postcss@8.5.15) - postcss-normalize-timing-functions: 8.0.1(postcss@8.5.15) - postcss-normalize-unicode: 8.0.1(postcss@8.5.15) - postcss-normalize-url: 8.0.1(postcss@8.5.15) - postcss-normalize-whitespace: 8.0.1(postcss@8.5.15) - postcss-ordered-values: 8.0.1(postcss@8.5.15) - postcss-reduce-initial: 8.0.1(postcss@8.5.15) - postcss-reduce-transforms: 8.0.1(postcss@8.5.15) - postcss-svgo: 8.0.1(postcss@8.5.15) - postcss-unique-selectors: 8.0.1(postcss@8.5.15) - cssnano-utils@5.0.3(postcss@8.5.15): dependencies: postcss: 8.5.15 - cssnano-utils@6.0.1(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - cssnano@7.1.9(postcss@8.5.15): dependencies: cssnano-preset-default: 7.0.17(postcss@8.5.15) lilconfig: 3.1.3 postcss: 8.5.15 - cssnano@8.0.2(postcss@8.5.15): - dependencies: - cssnano-preset-default: 8.0.2(postcss@8.5.15) - lilconfig: 3.1.3 - postcss: 8.5.15 - csso@5.0.5: dependencies: css-tree: 2.2.1 @@ -12469,10 +10931,6 @@ snapshots: db0@0.3.4: {} - debug@2.6.9: - dependencies: - ms: 2.0.0 - debug@4.4.3: dependencies: ms: 2.1.3 @@ -12518,8 +10976,6 @@ snapshots: destr@2.0.5: {} - destroy@1.2.0: {} - detect-libc@2.1.2: {} devalue@5.8.1: {} @@ -13062,42 +11518,6 @@ snapshots: expect-type@1.3.0: {} - express@4.22.2: - dependencies: - accepts: 1.3.8 - array-flatten: 1.1.1 - body-parser: 1.20.6 - content-disposition: 0.5.4 - content-type: 1.0.5 - cookie: 0.7.2 - cookie-signature: 1.0.7 - debug: 2.6.9 - depd: 2.0.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - finalhandler: 1.3.2 - fresh: 0.5.2 - http-errors: 2.0.1 - merge-descriptors: 1.0.3 - methods: 1.1.2 - on-finished: 2.4.1 - parseurl: 1.3.3 - path-to-regexp: 0.1.13 - proxy-addr: 2.0.7 - qs: 6.15.2 - range-parser: 1.2.1 - safe-buffer: 5.2.1 - send: 0.19.2 - serve-static: 1.16.3 - setprototypeof: 1.2.0 - statuses: 2.0.2 - type-is: 1.6.18 - utils-merge: 1.0.1 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - express@5.2.1: dependencies: accepts: 2.0.0 @@ -13192,18 +11612,6 @@ snapshots: dependencies: to-regex-range: 5.0.1 - finalhandler@1.3.2: - dependencies: - debug: 2.6.9 - encodeurl: 2.0.0 - escape-html: 1.0.3 - on-finished: 2.4.1 - parseurl: 1.3.3 - statuses: 2.0.2 - unpipe: 1.0.0 - transitivePeerDependencies: - - supports-color - finalhandler@2.1.1: dependencies: debug: 4.4.3 @@ -13248,8 +11656,6 @@ snapshots: fraction.js@5.3.4: {} - fresh@0.5.2: {} - fresh@2.0.0: {} fsevents@2.3.2: @@ -13464,10 +11870,6 @@ snapshots: human-signals@5.0.0: {} - iconv-lite@0.4.24: - dependencies: - safer-buffer: 2.1.2 - iconv-lite@0.6.3: dependencies: safer-buffer: 2.1.2 @@ -13968,41 +12370,27 @@ snapshots: mdn-data@2.27.1: {} - media-typer@0.3.0: {} - media-typer@1.1.0: {} memory-cache@0.2.0: {} - merge-descriptors@1.0.3: {} - merge-descriptors@2.0.0: {} merge-stream@2.0.0: {} merge2@1.4.1: {} - methods@1.1.2: {} - micromatch@4.0.8: dependencies: braces: 3.0.3 picomatch: 2.3.2 - mime-db@1.52.0: {} - mime-db@1.54.0: {} - mime-types@2.1.35: - dependencies: - mime-db: 1.52.0 - mime-types@3.0.2: dependencies: mime-db: 1.54.0 - mime@1.6.0: {} - mime@4.1.0: {} mimic-fn@4.0.0: {} @@ -14068,8 +12456,6 @@ snapshots: mrmime@2.0.1: {} - ms@2.0.0: {} - ms@2.1.3: {} muggle-string@0.4.1: {} @@ -14084,11 +12470,9 @@ snapshots: natural-compare@1.4.0: {} - negotiator@0.6.3: {} - negotiator@1.0.0: {} - next@15.5.18(@playwright/test@1.60.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): + next@15.5.18(@babel/core@7.29.7)(@playwright/test@1.60.0)(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: '@next/env': 15.5.18 '@swc/helpers': 0.5.15 @@ -14096,7 +12480,7 @@ snapshots: postcss: 8.4.31 react: 19.2.3 react-dom: 19.2.3(react@19.2.3) - styled-jsx: 5.1.6(react@19.2.3) + styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.3) optionalDependencies: '@next/swc-darwin-arm64': 15.5.18 '@next/swc-darwin-x64': 15.5.18 @@ -14217,111 +12601,6 @@ snapshots: - supports-color - uploadthing - nitropack@2.13.4(oxc-parser@0.133.0)(rolldown@1.1.3): - dependencies: - '@cloudflare/kv-asset-handler': 0.4.2 - '@rollup/plugin-alias': 6.0.0(rollup@4.62.2) - '@rollup/plugin-commonjs': 29.0.3(rollup@4.62.2) - '@rollup/plugin-inject': 5.0.5(rollup@4.62.2) - '@rollup/plugin-json': 6.1.0(rollup@4.62.2) - '@rollup/plugin-node-resolve': 16.0.3(rollup@4.62.2) - '@rollup/plugin-replace': 6.0.3(rollup@4.62.2) - '@rollup/plugin-terser': 1.0.0(rollup@4.62.2) - '@vercel/nft': 1.10.2(rollup@4.62.2) - archiver: 7.0.1 - c12: 3.3.4(magicast@0.5.3) - chokidar: 5.0.0 - citty: 0.2.2 - compatx: 0.2.0 - confbox: 0.2.4 - consola: 3.4.2 - cookie-es: 2.0.1 - croner: 10.0.1 - crossws: 0.3.5 - db0: 0.3.4 - defu: 6.1.7 - destr: 2.0.5 - dot-prop: 10.1.0 - esbuild: 0.28.1 - escape-string-regexp: 5.0.0 - etag: 1.8.1 - exsolve: 1.0.8 - globby: 16.2.0 - gzip-size: 7.0.0 - h3: 1.15.11 - hookable: 5.5.3 - httpxy: 0.5.3 - ioredis: 5.11.1 - jiti: 2.7.0 - klona: 2.0.6 - knitwork: 1.3.0 - listhen: 1.10.0(srvx@0.11.17) - magic-string: 0.30.21 - magicast: 0.5.3 - mime: 4.1.0 - mlly: 1.8.2 - node-fetch-native: 1.6.7 - node-mock-http: 1.0.4 - ofetch: 1.5.1 - ohash: 2.0.11 - pathe: 2.0.3 - perfect-debounce: 2.1.0 - pkg-types: 2.3.1 - pretty-bytes: 7.1.0 - radix3: 1.1.2 - rollup: 4.62.2 - rollup-plugin-visualizer: 7.0.1(rolldown@1.1.3)(rollup@4.62.2) - scule: 1.3.0 - semver: 7.8.5 - serve-placeholder: 2.0.2 - serve-static: 2.2.1 - source-map: 0.7.6 - std-env: 4.1.0 - ufo: 1.6.4 - ultrahtml: 1.6.0 - uncrypto: 0.1.3 - unctx: 2.5.0 - unenv: 2.0.0-rc.24 - unimport: 6.3.0(oxc-parser@0.133.0)(rolldown@1.1.3) - unplugin-utils: 0.3.1 - unstorage: 1.17.5(db0@0.3.4)(ioredis@5.11.1) - untyped: 2.0.0 - unwasm: 0.5.3 - youch: 4.1.1 - youch-core: 0.3.3 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@electric-sql/pglite' - - '@libsql/client' - - '@netlify/blobs' - - '@planetscale/database' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - aws4fetch - - bare-abort-controller - - bare-buffer - - better-sqlite3 - - drizzle-orm - - encoding - - idb-keyval - - mysql2 - - oxc-parser - - react-native-b4a - - rolldown - - sqlite3 - - srvx - - supports-color - - uploadthing - node-addon-api@7.1.1: {} node-exports-info@1.6.0: @@ -14386,136 +12665,13 @@ snapshots: consola: 3.4.2 cookie-es: 2.0.1 defu: 6.1.7 - destr: 2.0.5 - devalue: 5.8.1 - errx: 0.1.0 - escape-string-regexp: 5.0.0 - exsolve: 1.0.8 - h3: 1.15.11 - hookable: 5.5.3 - ignore: 7.0.5 - impound: 1.1.5 - jiti: 2.7.0 - klona: 2.0.6 - knitwork: 1.3.0 - magic-string: 0.30.21 - mlly: 1.8.2 - nanotar: 0.3.0 - nypm: 0.6.7 - ofetch: 1.5.1 - ohash: 2.0.11 - on-change: 6.0.2 - oxc-minify: 0.132.0 - oxc-parser: 0.132.0 - oxc-transform: 0.132.0 - oxc-walker: 1.0.0(oxc-parser@0.132.0)(rolldown@1.1.3) - pathe: 2.0.3 - perfect-debounce: 2.1.0 - pkg-types: 2.3.1 - rou3: 0.8.1 - scule: 1.3.0 - semver: 7.8.5 - std-env: 4.1.0 - tinyglobby: 0.2.17 - ufo: 1.6.4 - ultrahtml: 1.6.0 - uncrypto: 0.1.3 - unctx: 2.5.0 - unimport: 6.3.0(oxc-parser@0.132.0)(rolldown@1.1.3) - unplugin: 3.0.0 - unplugin-vue-router: 0.19.2(@vue/compiler-sfc@3.5.38)(vue-router@4.6.4(vue@3.5.38(typescript@5.9.3)))(vue@3.5.38(typescript@5.9.3)) - untyped: 2.0.0 - vue: 3.5.38(typescript@5.9.3) - vue-router: 4.6.4(vue@3.5.30(typescript@5.9.3)) - optionalDependencies: - '@parcel/watcher': 2.5.6 - '@types/node': 24.7.2 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@biomejs/biome' - - '@capacitor/preferences' - - '@deno/kv' - - '@electric-sql/pglite' - - '@libsql/client' - - '@netlify/blobs' - - '@planetscale/database' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/functions' - - '@vercel/kv' - - '@vitejs/devtools' - - '@vue/compiler-sfc' - - aws4fetch - - bare-abort-controller - - bare-buffer - - better-sqlite3 - - bufferutil - - cac - - commander - - db0 - - drizzle-orm - - encoding - - eslint - - idb-keyval - - ioredis - - less - - lightningcss - - magicast - - meow - - mysql2 - - optionator - - oxlint - - react-native-b4a - - rolldown - - rollup - - rollup-plugin-visualizer - - sass - - sass-embedded - - sqlite3 - - srvx - - stylelint - - stylus - - sugarss - - supports-color - - terser - - tsx - - typescript - - uploadthing - - utf-8-validate - - vite - - vls - - vti - - vue-tsc - - xml2js - - yaml - - nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0): - dependencies: - '@dxup/nuxt': 0.4.1(magicast@0.5.3)(typescript@6.0.3) - '@nuxt/cli': 3.36.0(@nuxt/schema@4.4.8)(cac@6.7.14)(magicast@0.5.3) - '@nuxt/devtools': 3.2.4(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) - '@nuxt/kit': 4.4.8(magicast@0.5.3) - '@nuxt/nitro-server': 4.4.8(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(db0@0.3.4)(ioredis@5.11.1)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(oxc-parser@0.133.0)(rolldown@1.1.3)(typescript@6.0.3) - '@nuxt/schema': 4.4.8 - '@nuxt/telemetry': 2.8.0(@nuxt/kit@4.4.8(magicast@0.5.3)) - '@nuxt/vite-builder': 4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@types/node@24.7.2)(eslint@9.39.4(jiti@2.7.0))(lightningcss@1.32.0)(magicast@0.5.3)(nuxt@4.4.8(@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7))(@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7))(@parcel/watcher@2.5.6)(@types/node@24.7.2)(@vue/compiler-sfc@3.5.38)(cac@6.7.14)(db0@0.3.4)(eslint@9.39.4(jiti@2.7.0))(ioredis@5.11.1)(lightningcss@1.32.0)(magicast@0.5.3)(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@6.0.3)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(yaml@2.9.0))(optionator@0.9.4)(rolldown@1.1.3)(rollup-plugin-visualizer@7.0.1(rolldown@1.1.3)(rollup@4.62.2))(rollup@4.62.2)(terser@5.48.0)(typescript@6.0.3)(vue@3.5.38(typescript@6.0.3))(yaml@2.9.0) - '@unhead/vue': 2.1.15(vue@3.5.38(typescript@6.0.3)) - '@vue/shared': 3.5.38 - chokidar: 5.0.0 - compatx: 0.2.0 - consola: 3.4.2 - cookie-es: 3.1.1 - defu: 6.1.7 + destr: 2.0.5 devalue: 5.8.1 errx: 0.1.0 escape-string-regexp: 5.0.0 exsolve: 1.0.8 - hookable: 6.1.1 + h3: 1.15.11 + hookable: 5.5.3 ignore: 7.0.5 impound: 1.1.5 jiti: 2.7.0 @@ -14528,13 +12684,12 @@ snapshots: ofetch: 1.5.1 ohash: 2.0.11 on-change: 6.0.2 - oxc-minify: 0.133.0 - oxc-parser: 0.133.0 - oxc-transform: 0.133.0 - oxc-walker: 1.0.0(oxc-parser@0.133.0)(rolldown@1.1.3) + oxc-minify: 0.132.0 + oxc-parser: 0.132.0 + oxc-transform: 0.132.0 + oxc-walker: 1.0.0(oxc-parser@0.132.0)(rolldown@1.1.3) pathe: 2.0.3 perfect-debounce: 2.1.0 - picomatch: 4.0.4 pkg-types: 2.3.1 rou3: 0.8.1 scule: 1.3.0 @@ -14545,13 +12700,12 @@ snapshots: ultrahtml: 1.6.0 uncrypto: 0.1.3 unctx: 2.5.0 - unhead: 2.1.15 - unimport: 6.3.0(oxc-parser@0.133.0)(rolldown@1.1.3) + unimport: 6.3.0(oxc-parser@0.132.0)(rolldown@1.1.3) unplugin: 3.0.0 - unrouting: 0.1.7 + unplugin-vue-router: 0.19.2(@vue/compiler-sfc@3.5.38)(vue-router@4.6.4(vue@3.5.38(typescript@5.9.3)))(vue@3.5.38(typescript@5.9.3)) untyped: 2.0.0 - vue: 3.5.38(typescript@6.0.3) - vue-router: 5.1.0(@vue/compiler-sfc@3.5.38)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)) + vue: 3.5.38(typescript@5.9.3) + vue-router: 4.6.4(vue@3.5.30(typescript@5.9.3)) optionalDependencies: '@parcel/watcher': 2.5.6 '@types/node': 24.7.2 @@ -14562,18 +12716,13 @@ snapshots: - '@azure/identity' - '@azure/keyvault-secrets' - '@azure/storage-blob' - - '@babel/plugin-proposal-decorators' - - '@babel/plugin-syntax-jsx' - - '@babel/plugin-syntax-typescript' - '@biomejs/biome' - '@capacitor/preferences' - '@deno/kv' - '@electric-sql/pglite' - '@libsql/client' - '@netlify/blobs' - - '@pinia/colada' - '@planetscale/database' - - '@rollup/plugin-babel' - '@upstash/redis' - '@vercel/blob' - '@vercel/functions' @@ -14600,7 +12749,6 @@ snapshots: - mysql2 - optionator - oxlint - - pinia - react-native-b4a - rolldown - rollup @@ -14619,6 +12767,8 @@ snapshots: - uploadthing - utf-8-validate - vite + - vls + - vti - vue-tsc - xml2js - yaml @@ -14738,29 +12888,6 @@ snapshots: '@oxc-minify/binding-win32-ia32-msvc': 0.132.0 '@oxc-minify/binding-win32-x64-msvc': 0.132.0 - oxc-minify@0.133.0: - optionalDependencies: - '@oxc-minify/binding-android-arm-eabi': 0.133.0 - '@oxc-minify/binding-android-arm64': 0.133.0 - '@oxc-minify/binding-darwin-arm64': 0.133.0 - '@oxc-minify/binding-darwin-x64': 0.133.0 - '@oxc-minify/binding-freebsd-x64': 0.133.0 - '@oxc-minify/binding-linux-arm-gnueabihf': 0.133.0 - '@oxc-minify/binding-linux-arm-musleabihf': 0.133.0 - '@oxc-minify/binding-linux-arm64-gnu': 0.133.0 - '@oxc-minify/binding-linux-arm64-musl': 0.133.0 - '@oxc-minify/binding-linux-ppc64-gnu': 0.133.0 - '@oxc-minify/binding-linux-riscv64-gnu': 0.133.0 - '@oxc-minify/binding-linux-riscv64-musl': 0.133.0 - '@oxc-minify/binding-linux-s390x-gnu': 0.133.0 - '@oxc-minify/binding-linux-x64-gnu': 0.133.0 - '@oxc-minify/binding-linux-x64-musl': 0.133.0 - '@oxc-minify/binding-openharmony-arm64': 0.133.0 - '@oxc-minify/binding-wasm32-wasi': 0.133.0 - '@oxc-minify/binding-win32-arm64-msvc': 0.133.0 - '@oxc-minify/binding-win32-ia32-msvc': 0.133.0 - '@oxc-minify/binding-win32-x64-msvc': 0.133.0 - oxc-parser@0.132.0: dependencies: '@oxc-project/types': 0.132.0 @@ -14786,31 +12913,6 @@ snapshots: '@oxc-parser/binding-win32-ia32-msvc': 0.132.0 '@oxc-parser/binding-win32-x64-msvc': 0.132.0 - oxc-parser@0.133.0: - dependencies: - '@oxc-project/types': 0.133.0 - optionalDependencies: - '@oxc-parser/binding-android-arm-eabi': 0.133.0 - '@oxc-parser/binding-android-arm64': 0.133.0 - '@oxc-parser/binding-darwin-arm64': 0.133.0 - '@oxc-parser/binding-darwin-x64': 0.133.0 - '@oxc-parser/binding-freebsd-x64': 0.133.0 - '@oxc-parser/binding-linux-arm-gnueabihf': 0.133.0 - '@oxc-parser/binding-linux-arm-musleabihf': 0.133.0 - '@oxc-parser/binding-linux-arm64-gnu': 0.133.0 - '@oxc-parser/binding-linux-arm64-musl': 0.133.0 - '@oxc-parser/binding-linux-ppc64-gnu': 0.133.0 - '@oxc-parser/binding-linux-riscv64-gnu': 0.133.0 - '@oxc-parser/binding-linux-riscv64-musl': 0.133.0 - '@oxc-parser/binding-linux-s390x-gnu': 0.133.0 - '@oxc-parser/binding-linux-x64-gnu': 0.133.0 - '@oxc-parser/binding-linux-x64-musl': 0.133.0 - '@oxc-parser/binding-openharmony-arm64': 0.133.0 - '@oxc-parser/binding-wasm32-wasi': 0.133.0 - '@oxc-parser/binding-win32-arm64-msvc': 0.133.0 - '@oxc-parser/binding-win32-ia32-msvc': 0.133.0 - '@oxc-parser/binding-win32-x64-msvc': 0.133.0 - oxc-transform@0.132.0: optionalDependencies: '@oxc-transform/binding-android-arm-eabi': 0.132.0 @@ -14834,29 +12936,6 @@ snapshots: '@oxc-transform/binding-win32-ia32-msvc': 0.132.0 '@oxc-transform/binding-win32-x64-msvc': 0.132.0 - oxc-transform@0.133.0: - optionalDependencies: - '@oxc-transform/binding-android-arm-eabi': 0.133.0 - '@oxc-transform/binding-android-arm64': 0.133.0 - '@oxc-transform/binding-darwin-arm64': 0.133.0 - '@oxc-transform/binding-darwin-x64': 0.133.0 - '@oxc-transform/binding-freebsd-x64': 0.133.0 - '@oxc-transform/binding-linux-arm-gnueabihf': 0.133.0 - '@oxc-transform/binding-linux-arm-musleabihf': 0.133.0 - '@oxc-transform/binding-linux-arm64-gnu': 0.133.0 - '@oxc-transform/binding-linux-arm64-musl': 0.133.0 - '@oxc-transform/binding-linux-ppc64-gnu': 0.133.0 - '@oxc-transform/binding-linux-riscv64-gnu': 0.133.0 - '@oxc-transform/binding-linux-riscv64-musl': 0.133.0 - '@oxc-transform/binding-linux-s390x-gnu': 0.133.0 - '@oxc-transform/binding-linux-x64-gnu': 0.133.0 - '@oxc-transform/binding-linux-x64-musl': 0.133.0 - '@oxc-transform/binding-openharmony-arm64': 0.133.0 - '@oxc-transform/binding-wasm32-wasi': 0.133.0 - '@oxc-transform/binding-win32-arm64-msvc': 0.133.0 - '@oxc-transform/binding-win32-ia32-msvc': 0.133.0 - '@oxc-transform/binding-win32-x64-msvc': 0.133.0 - oxc-walker@1.0.0(oxc-parser@0.132.0)(rolldown@1.1.3): dependencies: magic-regexp: 0.11.0 @@ -14864,13 +12943,6 @@ snapshots: oxc-parser: 0.132.0 rolldown: 1.1.3 - oxc-walker@1.0.0(oxc-parser@0.133.0)(rolldown@1.1.3): - dependencies: - magic-regexp: 0.11.0 - optionalDependencies: - oxc-parser: 0.133.0 - rolldown: 1.1.3 - p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -14918,8 +12990,6 @@ snapshots: lru-cache: 11.5.1 minipass: 7.1.3 - path-to-regexp@0.1.13: {} - path-to-regexp@8.4.2: {} path-type@4.0.0: {} @@ -14976,72 +13046,35 @@ snapshots: postcss: 8.5.15 postcss-value-parser: 4.2.0 - postcss-colormin@8.0.1(postcss@8.5.15): - dependencies: - '@colordx/core': 5.4.3 - browserslist: 4.28.2 - caniuse-api: 4.0.0 - postcss: 8.5.15 - postcss-value-parser: 4.2.0 - postcss-convert-values@7.0.12(postcss@8.5.15): dependencies: browserslist: 4.28.2 postcss: 8.5.15 postcss-value-parser: 4.2.0 - postcss-convert-values@8.0.1(postcss@8.5.15): - dependencies: - browserslist: 4.28.2 - postcss: 8.5.15 - postcss-value-parser: 4.2.0 - postcss-discard-comments@7.0.8(postcss@8.5.15): dependencies: postcss: 8.5.15 postcss-selector-parser: 7.1.4 - postcss-discard-comments@8.0.1(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - postcss-selector-parser: 7.1.4 - postcss-discard-duplicates@7.0.4(postcss@8.5.15): dependencies: postcss: 8.5.15 - postcss-discard-duplicates@8.0.1(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - postcss-discard-empty@7.0.3(postcss@8.5.15): dependencies: postcss: 8.5.15 - postcss-discard-empty@8.0.1(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - postcss-discard-overridden@7.0.3(postcss@8.5.15): dependencies: postcss: 8.5.15 - postcss-discard-overridden@8.0.1(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - postcss-merge-longhand@7.0.7(postcss@8.5.15): dependencies: postcss: 8.5.15 postcss-value-parser: 4.2.0 stylehacks: 7.0.11(postcss@8.5.15) - postcss-merge-longhand@8.0.1(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - postcss-value-parser: 4.2.0 - stylehacks: 8.0.1(postcss@8.5.15) - postcss-merge-rules@7.0.11(postcss@8.5.15): dependencies: browserslist: 4.28.2 @@ -15050,24 +13083,11 @@ snapshots: postcss: 8.5.15 postcss-selector-parser: 7.1.4 - postcss-merge-rules@8.0.1(postcss@8.5.15): - dependencies: - browserslist: 4.28.2 - caniuse-api: 4.0.0 - cssnano-utils: 6.0.1(postcss@8.5.15) - postcss: 8.5.15 - postcss-selector-parser: 7.1.4 - postcss-minify-font-values@7.0.3(postcss@8.5.15): dependencies: postcss: 8.5.15 postcss-value-parser: 4.2.0 - postcss-minify-font-values@8.0.1(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - postcss-value-parser: 4.2.0 - postcss-minify-gradients@7.0.5(postcss@8.5.15): dependencies: '@colordx/core': 5.4.3 @@ -15075,13 +13095,6 @@ snapshots: postcss: 8.5.15 postcss-value-parser: 4.2.0 - postcss-minify-gradients@8.0.1(postcss@8.5.15): - dependencies: - '@colordx/core': 5.4.3 - cssnano-utils: 6.0.1(postcss@8.5.15) - postcss: 8.5.15 - postcss-value-parser: 4.2.0 - postcss-minify-params@7.0.9(postcss@8.5.15): dependencies: browserslist: 4.28.2 @@ -15089,13 +13102,6 @@ snapshots: postcss: 8.5.15 postcss-value-parser: 4.2.0 - postcss-minify-params@8.0.1(postcss@8.5.15): - dependencies: - browserslist: 4.28.2 - cssnano-utils: 6.0.1(postcss@8.5.15) - postcss: 8.5.15 - postcss-value-parser: 4.2.0 - postcss-minify-selectors@7.1.2(postcss@8.5.15): dependencies: browserslist: 4.28.2 @@ -15104,14 +13110,6 @@ snapshots: postcss: 8.5.15 postcss-selector-parser: 7.1.4 - postcss-minify-selectors@8.0.2(postcss@8.5.15): - dependencies: - browserslist: 4.28.2 - caniuse-api: 4.0.0 - cssesc: 3.0.0 - postcss: 8.5.15 - postcss-selector-parser: 7.1.4 - postcss-nested@7.0.2(postcss@8.5.15): dependencies: postcss: 8.5.15 @@ -15121,126 +13119,64 @@ snapshots: dependencies: postcss: 8.5.15 - postcss-normalize-charset@8.0.1(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - postcss-normalize-display-values@7.0.3(postcss@8.5.15): dependencies: postcss: 8.5.15 postcss-value-parser: 4.2.0 - postcss-normalize-display-values@8.0.1(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - postcss-value-parser: 4.2.0 - postcss-normalize-positions@7.0.4(postcss@8.5.15): dependencies: postcss: 8.5.15 postcss-value-parser: 4.2.0 - postcss-normalize-positions@8.0.1(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - postcss-value-parser: 4.2.0 - postcss-normalize-repeat-style@7.0.4(postcss@8.5.15): dependencies: postcss: 8.5.15 postcss-value-parser: 4.2.0 - postcss-normalize-repeat-style@8.0.1(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - postcss-value-parser: 4.2.0 - postcss-normalize-string@7.0.3(postcss@8.5.15): dependencies: postcss: 8.5.15 postcss-value-parser: 4.2.0 - postcss-normalize-string@8.0.1(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - postcss-value-parser: 4.2.0 - postcss-normalize-timing-functions@7.0.3(postcss@8.5.15): dependencies: postcss: 8.5.15 postcss-value-parser: 4.2.0 - postcss-normalize-timing-functions@8.0.1(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - postcss-value-parser: 4.2.0 - postcss-normalize-unicode@7.0.9(postcss@8.5.15): dependencies: browserslist: 4.28.2 postcss: 8.5.15 postcss-value-parser: 4.2.0 - postcss-normalize-unicode@8.0.1(postcss@8.5.15): - dependencies: - browserslist: 4.28.2 - postcss: 8.5.15 - postcss-value-parser: 4.2.0 - postcss-normalize-url@7.0.3(postcss@8.5.15): dependencies: postcss: 8.5.15 postcss-value-parser: 4.2.0 - postcss-normalize-url@8.0.1(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - postcss-value-parser: 4.2.0 - postcss-normalize-whitespace@7.0.3(postcss@8.5.15): dependencies: postcss: 8.5.15 postcss-value-parser: 4.2.0 - postcss-normalize-whitespace@8.0.1(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - postcss-value-parser: 4.2.0 - postcss-ordered-values@7.0.4(postcss@8.5.15): dependencies: cssnano-utils: 5.0.3(postcss@8.5.15) postcss: 8.5.15 postcss-value-parser: 4.2.0 - postcss-ordered-values@8.0.1(postcss@8.5.15): - dependencies: - cssnano-utils: 6.0.1(postcss@8.5.15) - postcss: 8.5.15 - postcss-value-parser: 4.2.0 - postcss-reduce-initial@7.0.9(postcss@8.5.15): dependencies: browserslist: 4.28.2 caniuse-api: 3.0.0 postcss: 8.5.15 - postcss-reduce-initial@8.0.1(postcss@8.5.15): - dependencies: - browserslist: 4.28.2 - caniuse-api: 4.0.0 - postcss: 8.5.15 - postcss-reduce-transforms@7.0.3(postcss@8.5.15): dependencies: postcss: 8.5.15 postcss-value-parser: 4.2.0 - postcss-reduce-transforms@8.0.1(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - postcss-value-parser: 4.2.0 - postcss-selector-parser@7.1.4: dependencies: cssesc: 3.0.0 @@ -15252,22 +13188,11 @@ snapshots: postcss-value-parser: 4.2.0 svgo: 4.0.1 - postcss-svgo@8.0.1(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - postcss-value-parser: 4.2.0 - svgo: 4.0.1 - postcss-unique-selectors@7.0.7(postcss@8.5.15): dependencies: postcss: 8.5.15 postcss-selector-parser: 7.1.4 - postcss-unique-selectors@8.0.1(postcss@8.5.15): - dependencies: - postcss: 8.5.15 - postcss-selector-parser: 7.1.4 - postcss-value-parser@4.2.0: {} postcss@8.4.31: @@ -15349,13 +13274,6 @@ snapshots: range-parser@1.2.1: {} - raw-body@2.5.3: - dependencies: - bytes: 3.1.2 - http-errors: 2.0.1 - iconv-lite: 0.4.24 - unpipe: 1.0.0 - raw-body@3.0.2: dependencies: bytes: 3.1.2 @@ -15377,8 +13295,6 @@ snapshots: react-is@17.0.2: {} - react-refresh@0.17.0: {} - react-router@7.15.1(react-dom@19.2.3(react@19.2.3))(react@19.2.3): dependencies: cookie: 1.1.1 @@ -15651,24 +13567,6 @@ snapshots: semver@7.8.5: {} - send@0.19.2: - dependencies: - debug: 2.6.9 - depd: 2.0.0 - destroy: 1.2.0 - encodeurl: 2.0.0 - escape-html: 1.0.3 - etag: 1.8.1 - fresh: 0.5.2 - http-errors: 2.0.1 - mime: 1.6.0 - ms: 2.1.3 - on-finished: 2.4.1 - range-parser: 1.2.1 - statuses: 2.0.2 - transitivePeerDependencies: - - supports-color - send@1.2.1: dependencies: debug: 4.4.3 @@ -15697,15 +13595,6 @@ snapshots: dependencies: defu: 6.1.5 - serve-static@1.16.3: - dependencies: - encodeurl: 2.0.0 - escape-html: 1.0.3 - parseurl: 1.3.3 - send: 0.19.2 - transitivePeerDependencies: - - supports-color - serve-static@2.2.1: dependencies: encodeurl: 2.0.0 @@ -15984,10 +13873,12 @@ snapshots: structured-clone-es@2.0.0: {} - styled-jsx@5.1.6(react@19.2.3): + styled-jsx@5.1.6(@babel/core@7.29.7)(react@19.2.3): dependencies: client-only: 0.0.1 react: 19.2.3 + optionalDependencies: + '@babel/core': 7.29.7 stylehacks@7.0.11(postcss@8.5.15): dependencies: @@ -15995,12 +13886,6 @@ snapshots: postcss: 8.5.15 postcss-selector-parser: 7.1.4 - stylehacks@8.0.1(postcss@8.5.15): - dependencies: - browserslist: 4.28.2 - postcss: 8.5.15 - postcss-selector-parser: 7.1.4 - stylis@4.2.0: {} superjson@2.2.6: @@ -16196,11 +14081,6 @@ snapshots: dependencies: tagged-tag: 1.0.0 - type-is@1.6.18: - dependencies: - media-typer: 0.3.0 - mime-types: 2.1.35 - type-is@2.1.0: dependencies: content-type: 2.0.0 @@ -16347,26 +14227,6 @@ snapshots: oxc-parser: 0.132.0 rolldown: 1.1.3 - unimport@6.3.0(oxc-parser@0.133.0)(rolldown@1.1.3): - dependencies: - acorn: 8.17.0 - escape-string-regexp: 5.0.0 - estree-walker: 3.0.3 - local-pkg: 1.2.1 - magic-string: 0.30.21 - mlly: 1.8.2 - pathe: 2.0.3 - picomatch: 4.0.4 - pkg-types: 2.3.1 - scule: 1.3.0 - strip-literal: 3.1.0 - tinyglobby: 0.2.17 - unplugin: 3.0.0 - unplugin-utils: 0.3.1 - optionalDependencies: - oxc-parser: 0.133.0 - rolldown: 1.1.3 - unpipe@1.0.0: {} unplugin-utils@0.3.1: @@ -16417,11 +14277,6 @@ snapshots: picomatch: 4.0.4 webpack-virtual-modules: 0.6.2 - unrouting@0.1.7: - dependencies: - escape-string-regexp: 5.0.0 - ufo: 1.6.4 - unrs-resolver@1.12.2: dependencies: napi-postinstall: 0.3.4 @@ -16504,8 +14359,6 @@ snapshots: util-deprecate@1.0.2: {} - utils-merge@1.0.1: {} - uuid@11.1.1: {} vary@1.1.2: {} @@ -16557,21 +14410,6 @@ snapshots: optionator: 0.9.4 typescript: 5.9.3 - vite-plugin-checker@0.14.4(eslint@9.39.4(jiti@2.7.0))(optionator@0.9.4)(typescript@6.0.3)(vite@7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0)): - dependencies: - '@babel/code-frame': 7.29.7 - chokidar: 5.0.0 - npm-run-path: 6.0.0 - picocolors: 1.1.1 - picomatch: 4.0.4 - proper-lockfile: 4.1.2 - tiny-invariant: 1.3.3 - vite: 7.3.5(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0) - optionalDependencies: - eslint: 9.39.4(jiti@2.7.0) - optionator: 0.9.4 - typescript: 6.0.3 - vite-plugin-inspect@11.4.1(@nuxt/kit@3.21.7(magicast@0.5.3))(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0)): dependencies: ansis: 4.3.1 @@ -16622,16 +14460,6 @@ snapshots: vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) vue: 3.5.38(typescript@5.9.3) - vite-plugin-vue-tracer@1.4.0(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)): - dependencies: - estree-walker: 3.0.3 - exsolve: 1.0.8 - magic-string: 0.30.21 - pathe: 2.0.3 - source-map-js: 1.2.1 - vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vue: 3.5.38(typescript@6.0.3) - vite@6.4.3(@types/node@24.7.2)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.48.0)(yaml@2.9.0): dependencies: esbuild: 0.25.12 @@ -16835,30 +14663,6 @@ snapshots: '@vue/compiler-sfc': 3.5.38 vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vue-router@5.1.0(@vue/compiler-sfc@3.5.38)(vite@8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0))(vue@3.5.38(typescript@6.0.3)): - dependencies: - '@babel/generator': 8.0.0 - '@vue-macros/common': 3.1.2(vue@3.5.38(typescript@6.0.3)) - '@vue/devtools-api': 8.1.3 - ast-walker-scope: 0.9.0 - chokidar: 5.0.0 - json5: 2.2.3 - local-pkg: 1.2.1 - magic-string: 0.30.21 - mlly: 1.8.2 - muggle-string: 0.4.1 - pathe: 2.0.3 - picomatch: 4.0.4 - scule: 1.3.0 - tinyglobby: 0.2.17 - unplugin: 3.0.0 - unplugin-utils: 0.3.1 - vue: 3.5.38(typescript@6.0.3) - yaml: 2.9.0 - optionalDependencies: - '@vue/compiler-sfc': 3.5.38 - vite: 8.1.0(@types/node@24.7.2)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) - vue-sfc-transformer@0.1.17(@vue/compiler-core@3.5.38)(esbuild@0.28.1)(vue@3.5.30(typescript@5.9.3)): dependencies: '@babel/parser': 7.29.7 @@ -16876,16 +14680,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - vue@3.5.30(typescript@6.0.3): - dependencies: - '@vue/compiler-dom': 3.5.30 - '@vue/compiler-sfc': 3.5.30 - '@vue/runtime-dom': 3.5.30 - '@vue/server-renderer': 3.5.30(vue@3.5.30(typescript@6.0.3)) - '@vue/shared': 3.5.30 - optionalDependencies: - typescript: 6.0.3 - vue@3.5.38(typescript@5.9.3): dependencies: '@vue/compiler-dom': 3.5.38 @@ -16896,16 +14690,6 @@ snapshots: optionalDependencies: typescript: 5.9.3 - vue@3.5.38(typescript@6.0.3): - dependencies: - '@vue/compiler-dom': 3.5.38 - '@vue/compiler-sfc': 3.5.38 - '@vue/runtime-dom': 3.5.38 - '@vue/server-renderer': 3.5.38(vue@3.5.38(typescript@6.0.3)) - '@vue/shared': 3.5.38 - optionalDependencies: - typescript: 6.0.3 - w3c-xmlserializer@5.0.0: dependencies: xml-name-validator: 5.0.0 From 4ca45b9813ff3535c1fbdd142923afa9b54d89c5 Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Thu, 16 Jul 2026 13:41:04 +0530 Subject: [PATCH 27/31] refactor: restructure sveltekit sample to match upstream convention (samples//quickstart) --- pnpm-workspace.yaml | 2 +- samples/sveltekit/.env.example | 13 ----- samples/sveltekit/README.md | 55 ------------------- samples/sveltekit/quickstart/.env.example | 4 ++ samples/sveltekit/{ => quickstart}/.gitignore | 0 samples/sveltekit/{ => quickstart}/.npmrc | 0 .../{ => quickstart}/.prettierignore | 0 .../sveltekit/{ => quickstart}/.prettierrc | 0 .../{ => quickstart}/.vscode/extensions.json | 0 samples/sveltekit/quickstart/README.md | 51 +++++++++++++++++ .../sveltekit/{ => quickstart}/package.json | 2 +- .../sveltekit/{ => quickstart}/src/app.d.ts | 0 .../sveltekit/{ => quickstart}/src/app.html | 0 .../{ => quickstart}/src/hooks.server.ts | 0 .../{ => quickstart}/src/lib/AppShell.svelte | 0 .../src/lib/assets/favicon.svg | 0 .../{ => quickstart}/src/lib/index.ts | 0 .../src/routes/+layout.server.ts | 0 .../src/routes/+layout.svelte | 0 .../{ => quickstart}/src/routes/+page.svelte | 0 .../src/routes/api/auth/callback/+server.ts | 0 .../src/routes/api/auth/signin/+server.ts | 0 .../src/routes/api/auth/signout/+server.ts | 0 .../src/routes/callback/+page.svelte | 0 .../src/routes/protected/+page.server.ts | 0 .../src/routes/protected/+page.svelte | 0 .../{ => quickstart}/static/robots.txt | 0 .../sveltekit/{ => quickstart}/tsconfig.json | 0 .../sveltekit/{ => quickstart}/vite.config.ts | 0 29 files changed, 57 insertions(+), 70 deletions(-) delete mode 100644 samples/sveltekit/.env.example delete mode 100644 samples/sveltekit/README.md create mode 100644 samples/sveltekit/quickstart/.env.example rename samples/sveltekit/{ => quickstart}/.gitignore (100%) rename samples/sveltekit/{ => quickstart}/.npmrc (100%) rename samples/sveltekit/{ => quickstart}/.prettierignore (100%) rename samples/sveltekit/{ => quickstart}/.prettierrc (100%) rename samples/sveltekit/{ => quickstart}/.vscode/extensions.json (100%) create mode 100644 samples/sveltekit/quickstart/README.md rename samples/sveltekit/{ => quickstart}/package.json (95%) rename samples/sveltekit/{ => quickstart}/src/app.d.ts (100%) rename samples/sveltekit/{ => quickstart}/src/app.html (100%) rename samples/sveltekit/{ => quickstart}/src/hooks.server.ts (100%) rename samples/sveltekit/{ => quickstart}/src/lib/AppShell.svelte (100%) rename samples/sveltekit/{ => quickstart}/src/lib/assets/favicon.svg (100%) rename samples/sveltekit/{ => quickstart}/src/lib/index.ts (100%) rename samples/sveltekit/{ => quickstart}/src/routes/+layout.server.ts (100%) rename samples/sveltekit/{ => quickstart}/src/routes/+layout.svelte (100%) rename samples/sveltekit/{ => quickstart}/src/routes/+page.svelte (100%) rename samples/sveltekit/{ => quickstart}/src/routes/api/auth/callback/+server.ts (100%) rename samples/sveltekit/{ => quickstart}/src/routes/api/auth/signin/+server.ts (100%) rename samples/sveltekit/{ => quickstart}/src/routes/api/auth/signout/+server.ts (100%) rename samples/sveltekit/{ => quickstart}/src/routes/callback/+page.svelte (100%) rename samples/sveltekit/{ => quickstart}/src/routes/protected/+page.server.ts (100%) rename samples/sveltekit/{ => quickstart}/src/routes/protected/+page.svelte (100%) rename samples/sveltekit/{ => quickstart}/static/robots.txt (100%) rename samples/sveltekit/{ => quickstart}/tsconfig.json (100%) rename samples/sveltekit/{ => quickstart}/vite.config.ts (100%) diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 80ca3d6..df44214 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,6 +1,6 @@ packages: - packages/* - - samples/* + - samples/*/* minimumReleaseAgeExclude: # JUSTIFICATION: Temporary exclusion due to recent release. TODO: Remove after the time passes. diff --git a/samples/sveltekit/.env.example b/samples/sveltekit/.env.example deleted file mode 100644 index a132425..0000000 --- a/samples/sveltekit/.env.example +++ /dev/null @@ -1,13 +0,0 @@ -# ThunderID IdP base URL (e.g. https://localhost:8090) -THUNDERID_BASE_URL= - -# OAuth client credentials (from ThunderID console) -THUNDERID_CLIENT_ID= -THUNDERID_CLIENT_SECRET= - -# Application UUID (from ThunderID console; required for branding, sign-up flow) -THUNDERID_APPLICATION_ID= - -# HS256 key for signing session JWT cookies (min 32 chars) -# Generate with: openssl rand -base64 32 -THUNDERID_SESSION_SECRET= diff --git a/samples/sveltekit/README.md b/samples/sveltekit/README.md deleted file mode 100644 index cb0017e..0000000 --- a/samples/sveltekit/README.md +++ /dev/null @@ -1,55 +0,0 @@ -# SvelteKit B2C Sample App - -B2C single-page app demonstrating the `@thunderid/sveltekit` SDK with SvelteKit SSR support. Shows sign-in with redirect, a protected route that redirects unauthenticated users, user profile display, and sign-out. - -## Prerequisites - -- Node.js 20+ -- pnpm 10+ -- A running ThunderID IdP instance (e.g. `https://localhost:8090`) -- An OAuth2 client registered in the ThunderID console with: - - Redirect URI: `http://localhost:5173/api/auth/callback` - - Post-Logout Redirect URI: `http://localhost:5173/` - -## Environment Setup - -Copy `.env.example` to `.env` and fill in the values: - -```sh -cp .env.example .env -``` - -| Variable | Description | -|----------|-------------| -| `THUNDERID_BASE_URL` | ThunderID IdP base URL (e.g. `https://localhost:8090`) | -| `THUNDERID_CLIENT_ID` | OAuth2 client ID from the ThunderID console | -| `THUNDERID_CLIENT_SECRET` | OAuth2 client secret | -| `THUNDERID_APPLICATION_ID` | Application UUID from the ThunderID console | -| `THUNDERID_SESSION_SECRET` | HS256 key for session cookies (generate with `openssl rand -base64 32`) | - -## Running - -```sh -pnpm install -pnpm dev -``` - -The app starts at `http://localhost:5173`. - -## What It Demonstrates - -1. **Sign-in** — unauthenticated users see a sign-in button; clicking it redirects to ThunderID -2. **Callback handling** — the `/callback` route exchanges the authorization code for tokens -3. **Protected route** — `/protected` requires a valid session; unauthenticated users are redirected to sign-in -4. **User profile** — displays the authenticated user's name, email, and avatar -5. **Token management** — buttons to fetch access/ID tokens and user info -6. **Sign-out** — terminates the session and returns to the unauthenticated state -7. **SDK events** — logs SDK lifecycle events (sign-in, sign-out, token refresh) in an expandable panel -8. **Language switcher** — switch UI language via the SDK's i18n support - -## Building - -```sh -pnpm build -pnpm preview -``` diff --git a/samples/sveltekit/quickstart/.env.example b/samples/sveltekit/quickstart/.env.example new file mode 100644 index 0000000..31a1a54 --- /dev/null +++ b/samples/sveltekit/quickstart/.env.example @@ -0,0 +1,4 @@ +THUNDERID_BASE_URL=https://localhost:8090 +THUNDERID_CLIENT_ID=your-client-id-here +THUNDERID_CLIENT_SECRET=your-client-secret-here +THUNDERID_SESSION_SECRET=your-session-secret-here diff --git a/samples/sveltekit/.gitignore b/samples/sveltekit/quickstart/.gitignore similarity index 100% rename from samples/sveltekit/.gitignore rename to samples/sveltekit/quickstart/.gitignore diff --git a/samples/sveltekit/.npmrc b/samples/sveltekit/quickstart/.npmrc similarity index 100% rename from samples/sveltekit/.npmrc rename to samples/sveltekit/quickstart/.npmrc diff --git a/samples/sveltekit/.prettierignore b/samples/sveltekit/quickstart/.prettierignore similarity index 100% rename from samples/sveltekit/.prettierignore rename to samples/sveltekit/quickstart/.prettierignore diff --git a/samples/sveltekit/.prettierrc b/samples/sveltekit/quickstart/.prettierrc similarity index 100% rename from samples/sveltekit/.prettierrc rename to samples/sveltekit/quickstart/.prettierrc diff --git a/samples/sveltekit/.vscode/extensions.json b/samples/sveltekit/quickstart/.vscode/extensions.json similarity index 100% rename from samples/sveltekit/.vscode/extensions.json rename to samples/sveltekit/quickstart/.vscode/extensions.json diff --git a/samples/sveltekit/quickstart/README.md b/samples/sveltekit/quickstart/README.md new file mode 100644 index 0000000..f70c857 --- /dev/null +++ b/samples/sveltekit/quickstart/README.md @@ -0,0 +1,51 @@ +# ThunderID SvelteKit Quickstart + +A minimal SvelteKit application demonstrating ThunderID authentication with OAuth 2.0, PKCE, and JWT support using the `@thunderid/sveltekit` SDK. + +## Prerequisites + +- Node.js 18+ +- pnpm +- A ThunderID application (see [Import ThunderID Resources](#import-thunderid-resources) below) + +## Import ThunderID Resources + +This sample ships with a `thunderid-config/` directory containing a declarative YAML file that creates the required user type and application in one step. + +1. Open `thunderid-config/thunderid.env` and set your preferred values: + ```bash + SVELTEKIT_QUICKSTART_CLIENT_ID=SVELTEKIT_QUICKSTART + SVELTEKIT_QUICKSTART_REDIRECT_URIS=["http://localhost:5173"] + ``` +2. Import via the ThunderID Console (https://localhost:8090/console): + - **First-time login**: a welcome screen appears with an **Open** button to upload the YAML file directly. + - **Later**: access the same welcome screen from the user profile menu in the top-right corner of the console. + +This creates the `Customer` user type and the `sveltekit-quickstart` application under the default organization unit. + +## Setup + +1. Copy the environment template: + ```sh + cp .env.example .env + ``` + +2. Fill in your ThunderID credentials in `.env`, using the values you set in `thunderid-config/thunderid.env`: + ``` + THUNDERID_BASE_URL=https://localhost:8090 + THUNDERID_CLIENT_ID=SVELTEKIT_QUICKSTART + THUNDERID_CLIENT_SECRET= + THUNDERID_SESSION_SECRET= + ``` + +3. Install dependencies and start the dev server: + ```sh + pnpm install + pnpm dev + ``` + +The app will be available at `http://localhost:5173`. + +## Docs + +Full SDK reference: [thunderid.dev/docs](https://thunderid.dev/docs) diff --git a/samples/sveltekit/package.json b/samples/sveltekit/quickstart/package.json similarity index 95% rename from samples/sveltekit/package.json rename to samples/sveltekit/quickstart/package.json index d3a5d97..30b7987 100644 --- a/samples/sveltekit/package.json +++ b/samples/sveltekit/quickstart/package.json @@ -1,5 +1,5 @@ { - "name": "sveltekit-b2c", + "name": "sveltekit-quickstart", "private": true, "version": "0.0.1", "type": "module", diff --git a/samples/sveltekit/src/app.d.ts b/samples/sveltekit/quickstart/src/app.d.ts similarity index 100% rename from samples/sveltekit/src/app.d.ts rename to samples/sveltekit/quickstart/src/app.d.ts diff --git a/samples/sveltekit/src/app.html b/samples/sveltekit/quickstart/src/app.html similarity index 100% rename from samples/sveltekit/src/app.html rename to samples/sveltekit/quickstart/src/app.html diff --git a/samples/sveltekit/src/hooks.server.ts b/samples/sveltekit/quickstart/src/hooks.server.ts similarity index 100% rename from samples/sveltekit/src/hooks.server.ts rename to samples/sveltekit/quickstart/src/hooks.server.ts diff --git a/samples/sveltekit/src/lib/AppShell.svelte b/samples/sveltekit/quickstart/src/lib/AppShell.svelte similarity index 100% rename from samples/sveltekit/src/lib/AppShell.svelte rename to samples/sveltekit/quickstart/src/lib/AppShell.svelte diff --git a/samples/sveltekit/src/lib/assets/favicon.svg b/samples/sveltekit/quickstart/src/lib/assets/favicon.svg similarity index 100% rename from samples/sveltekit/src/lib/assets/favicon.svg rename to samples/sveltekit/quickstart/src/lib/assets/favicon.svg diff --git a/samples/sveltekit/src/lib/index.ts b/samples/sveltekit/quickstart/src/lib/index.ts similarity index 100% rename from samples/sveltekit/src/lib/index.ts rename to samples/sveltekit/quickstart/src/lib/index.ts diff --git a/samples/sveltekit/src/routes/+layout.server.ts b/samples/sveltekit/quickstart/src/routes/+layout.server.ts similarity index 100% rename from samples/sveltekit/src/routes/+layout.server.ts rename to samples/sveltekit/quickstart/src/routes/+layout.server.ts diff --git a/samples/sveltekit/src/routes/+layout.svelte b/samples/sveltekit/quickstart/src/routes/+layout.svelte similarity index 100% rename from samples/sveltekit/src/routes/+layout.svelte rename to samples/sveltekit/quickstart/src/routes/+layout.svelte diff --git a/samples/sveltekit/src/routes/+page.svelte b/samples/sveltekit/quickstart/src/routes/+page.svelte similarity index 100% rename from samples/sveltekit/src/routes/+page.svelte rename to samples/sveltekit/quickstart/src/routes/+page.svelte diff --git a/samples/sveltekit/src/routes/api/auth/callback/+server.ts b/samples/sveltekit/quickstart/src/routes/api/auth/callback/+server.ts similarity index 100% rename from samples/sveltekit/src/routes/api/auth/callback/+server.ts rename to samples/sveltekit/quickstart/src/routes/api/auth/callback/+server.ts diff --git a/samples/sveltekit/src/routes/api/auth/signin/+server.ts b/samples/sveltekit/quickstart/src/routes/api/auth/signin/+server.ts similarity index 100% rename from samples/sveltekit/src/routes/api/auth/signin/+server.ts rename to samples/sveltekit/quickstart/src/routes/api/auth/signin/+server.ts diff --git a/samples/sveltekit/src/routes/api/auth/signout/+server.ts b/samples/sveltekit/quickstart/src/routes/api/auth/signout/+server.ts similarity index 100% rename from samples/sveltekit/src/routes/api/auth/signout/+server.ts rename to samples/sveltekit/quickstart/src/routes/api/auth/signout/+server.ts diff --git a/samples/sveltekit/src/routes/callback/+page.svelte b/samples/sveltekit/quickstart/src/routes/callback/+page.svelte similarity index 100% rename from samples/sveltekit/src/routes/callback/+page.svelte rename to samples/sveltekit/quickstart/src/routes/callback/+page.svelte diff --git a/samples/sveltekit/src/routes/protected/+page.server.ts b/samples/sveltekit/quickstart/src/routes/protected/+page.server.ts similarity index 100% rename from samples/sveltekit/src/routes/protected/+page.server.ts rename to samples/sveltekit/quickstart/src/routes/protected/+page.server.ts diff --git a/samples/sveltekit/src/routes/protected/+page.svelte b/samples/sveltekit/quickstart/src/routes/protected/+page.svelte similarity index 100% rename from samples/sveltekit/src/routes/protected/+page.svelte rename to samples/sveltekit/quickstart/src/routes/protected/+page.svelte diff --git a/samples/sveltekit/static/robots.txt b/samples/sveltekit/quickstart/static/robots.txt similarity index 100% rename from samples/sveltekit/static/robots.txt rename to samples/sveltekit/quickstart/static/robots.txt diff --git a/samples/sveltekit/tsconfig.json b/samples/sveltekit/quickstart/tsconfig.json similarity index 100% rename from samples/sveltekit/tsconfig.json rename to samples/sveltekit/quickstart/tsconfig.json diff --git a/samples/sveltekit/vite.config.ts b/samples/sveltekit/quickstart/vite.config.ts similarity index 100% rename from samples/sveltekit/vite.config.ts rename to samples/sveltekit/quickstart/vite.config.ts From 12e6a8120c7eb5cb5d6f5e2841da84dc139f4150 Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Thu, 16 Jul 2026 13:42:50 +0530 Subject: [PATCH 28/31] feat: add thunderid-config to sveltekit sample --- .../thunderid-config/thunderid-config.yaml | 128 ++++++++++++++++++ .../quickstart/thunderid-config/thunderid.env | 2 + 2 files changed, 130 insertions(+) create mode 100644 samples/sveltekit/quickstart/thunderid-config/thunderid-config.yaml create mode 100644 samples/sveltekit/quickstart/thunderid-config/thunderid.env diff --git a/samples/sveltekit/quickstart/thunderid-config/thunderid-config.yaml b/samples/sveltekit/quickstart/thunderid-config/thunderid-config.yaml new file mode 100644 index 0000000..7393fac --- /dev/null +++ b/samples/sveltekit/quickstart/thunderid-config/thunderid-config.yaml @@ -0,0 +1,128 @@ +# resource_type: user_type +id: 019e3a5c-04ea-7d18-8ae6-f828b1f3d60f +category: user +name: Customer +ouHandle: default +allowSelfRegistration: true +systemAttributes: + display: username +schema: { + "username": { + "type": "string", + "displayName": "Username", + "required": true, + "unique": true + }, + "password": { + "type": "string", + "displayName": "Password", + "required": false, + "credential": true + }, + "email": { + "type": "string", + "displayName": "Email", + "required": true, + "unique": true, + "regex": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" + }, + "given_name": { + "type": "string", + "displayName": "First Name", + "required": false + }, + "family_name": { + "type": "string", + "displayName": "Last Name", + "required": false + }, + "name": { + "type": "string", + "displayName": "Full Name", + "required": false + }, + "mobile_number": { + "type": "string", + "displayName": "Mobile Number", + "required": false + } + } + +--- +# resource_type: application +id: 2971eb55-7673-4bd0-9ef8-2ed8b53e388f +ouHandle: default +name: sveltekit-quickstart +description: Sample SvelteKit application using the @thunderid/sveltekit SDK +url: http://localhost:5173 +logoUrl: emoji:🟠 +authFlowHandle: default-basic-flow +isRegistrationFlowEnabled: true +isRecoveryFlowEnabled: false +assertion: + validityPeriod: 3600 +allowedUserTypes: + - Customer +inboundAuthConfig: + - type: oauth2 + config: + clientId: {{.SVELTEKIT_QUICKSTART_CLIENT_ID}} + redirectUris: + {{- range .SVELTEKIT_QUICKSTART_REDIRECT_URIS}} + - {{.}} + {{- end}} + grantTypes: + - authorization_code + responseTypes: + - code + tokenEndpointAuthMethod: client_secret_post + pkceRequired: true + publicClient: false + requirePushedAuthorizationRequests: false + token: + accessToken: + validityPeriod: 3600 + userAttributes: + - given_name + - family_name + - email + - groups + - name + idToken: + validityPeriod: 3600 + userAttributes: + - given_name + - family_name + - email + - groups + - name + responseType: JWT + userInfo: + responseType: JSON + userAttributes: + - given_name + - family_name + - email + - groups + - name + scopeClaims: + email: + - email + - email_verified + group: + - groups + phone: + - phone_number + - phone_number_verified + profile: + - name + - given_name + - family_name + - picture + +--- +# resource_type: server_config +name: cors +value: + allowedOrigins: + - "http://localhost:5173" diff --git a/samples/sveltekit/quickstart/thunderid-config/thunderid.env b/samples/sveltekit/quickstart/thunderid-config/thunderid.env new file mode 100644 index 0000000..b3cd029 --- /dev/null +++ b/samples/sveltekit/quickstart/thunderid-config/thunderid.env @@ -0,0 +1,2 @@ +SVELTEKIT_QUICKSTART_CLIENT_ID=SVELTEKIT_QUICKSTART +SVELTEKIT_QUICKSTART_REDIRECT_URIS=["http://localhost:5173"] From 9b4b427cdea85f8b63d2085c3d2ff530cbf19954 Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Thu, 16 Jul 2026 13:51:44 +0530 Subject: [PATCH 29/31] docs: update README with @thunderid/sveltekit package --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a064d93..749ef9f 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ JavaScript SDKs for ThunderID. Provides authentication and user management for b | [`@thunderid/nextjs`](packages/nextjs) | ![npm](https://img.shields.io/npm/v/@thunderid/nextjs) | Next.js SDK | | [`@thunderid/react-router`](packages/react-router) | ![npm](https://img.shields.io/npm/v/@thunderid/react-router) | React Router integration | | [`@thunderid/vue`](packages/vue) | ![npm](https://img.shields.io/npm/v/@thunderid/vue) | Vue SDK | -| [`@thunderid/svelte`](packages/svelte) | ![npm](https://img.shields.io/npm/v/@thunderid/svelte) | Svelte SDK | +| [`@thunderid/sveltekit`](packages/sveltekit) | ![npm](https://img.shields.io/npm/v/@thunderid/sveltekit) | SvelteKit SDK | | [`@thunderid/nuxt`](packages/nuxt) | ![npm](https://img.shields.io/npm/v/@thunderid/nuxt) | Nuxt SDK | | [`@thunderid/express`](packages/express) | ![npm](https://img.shields.io/npm/v/@thunderid/express) | Express.js SDK | | [`@thunderid/tanstack-router`](packages/tanstack-router) | ![npm](https://img.shields.io/npm/v/@thunderid/tanstack-router) | TanStack Router integration | From 13d402d6d5fa781ecd4d878ec3ed37a3bcc187d0 Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Thu, 16 Jul 2026 14:13:12 +0530 Subject: [PATCH 30/31] docs: add README for @thunderid/sveltekit --- packages/sveltekit/README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 packages/sveltekit/README.md diff --git a/packages/sveltekit/README.md b/packages/sveltekit/README.md new file mode 100644 index 0000000..0c5531b --- /dev/null +++ b/packages/sveltekit/README.md @@ -0,0 +1,21 @@ +![ThunderID SvelteKit SDK](https://raw.githubusercontent.com/thunder-id/thunderid/refs/heads/main/docs/static/assets/images/readme/repo-banner-sveltekit-sdk.png) + +SvelteKit SDK for ThunderID. Provides hooks, components, and server-side utilities for authentication in SvelteKit applications. + +## Installation + +```bash +npm install @thunderid/sveltekit +``` + +```bash +pnpm add @thunderid/sveltekit +``` + +```bash +yarn add @thunderid/sveltekit +``` + +## License + +This project is licensed under the [Apache License 2.0](https://github.com/thunder-id/thunderid/blob/main/LICENSE). From e5501db735731605e9a44797be14ef1026ba6d68 Mon Sep 17 00:00:00 2001 From: Chamal1120 Date: Thu, 16 Jul 2026 19:04:40 +0530 Subject: [PATCH 31/31] docs: add quick start guide to sveltekit README --- packages/sveltekit/README.md | 62 ++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/packages/sveltekit/README.md b/packages/sveltekit/README.md index 0c5531b..b59412b 100644 --- a/packages/sveltekit/README.md +++ b/packages/sveltekit/README.md @@ -16,6 +16,68 @@ pnpm add @thunderid/sveltekit yarn add @thunderid/sveltekit ``` +## Quick Start + +### 1. Configure the server hook + +```ts +// src/hooks.server.ts +import {createThunderIDHandle} from '@thunderid/sveltekit/server'; + +export const handle = createThunderIDHandle({ + baseUrl: 'https://localhost:8090', + clientId: 'YOUR_CLIENT_ID', + clientSecret: 'YOUR_CLIENT_SECRET', + sessionSecret: 'YOUR_SESSION_SECRET', +}); +``` + +### 2. Load session data on the server + +```ts +// src/routes/+layout.server.ts +import {loadThunderID} from '@thunderid/sveltekit/server'; +import type {LayoutServerLoad} from './$types'; + +export const load: LayoutServerLoad = (event) => { + return {thunderid: loadThunderID(event)}; +}; +``` + +### 3. Wrap your app with the provider + +```svelte + + + + + {@render children()} + +``` + +### 4. Use components and hooks + +```svelte + + + + +

Welcome, {tid.user?.name}!

+ +
+ + + + +``` + ## License This project is licensed under the [Apache License 2.0](https://github.com/thunder-id/thunderid/blob/main/LICENSE).