mirror of
https://github.com/BradNut/musicle-svelte
synced 2025-09-08 17:40:21 +00:00
Adding APIs, schemas for base roles etc.
This commit is contained in:
parent
12d8384fa4
commit
be1a0aecfe
41 changed files with 1441 additions and 94 deletions
1
.nvmrc
Normal file
1
.nvmrc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
22
|
||||||
|
|
@ -14,10 +14,10 @@ export default defineConfig({
|
||||||
dialect: 'postgresql',
|
dialect: 'postgresql',
|
||||||
casing: 'snake_case',
|
casing: 'snake_case',
|
||||||
dbCredentials: {
|
dbCredentials: {
|
||||||
url: process.env.DATABASE_URL!
|
url: process.env.DATABASE_URL ?? '',
|
||||||
},
|
},
|
||||||
migrations: {
|
migrations: {
|
||||||
table: 'migrations',
|
table: 'migrations',
|
||||||
schema: 'public'
|
schema: 'public',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,9 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^1.9.4",
|
"@biomejs/biome": "^1.9.4",
|
||||||
"@chromatic-com/storybook": "^3.2.3",
|
"@chromatic-com/storybook": "^3.2.3",
|
||||||
|
"@dicebear/collection": "^9.2.2",
|
||||||
|
"@dicebear/converter": "^9.2.2",
|
||||||
|
"@dicebear/core": "^9.2.2",
|
||||||
"@faker-js/faker": "^9.3.0",
|
"@faker-js/faker": "^9.3.0",
|
||||||
"@playwright/test": "^1.45.3",
|
"@playwright/test": "^1.45.3",
|
||||||
"@storybook/addon-essentials": "^8.4.7",
|
"@storybook/addon-essentials": "^8.4.7",
|
||||||
|
|
@ -35,6 +38,8 @@
|
||||||
"@sveltejs/enhanced-img": "^0.4.4",
|
"@sveltejs/enhanced-img": "^0.4.4",
|
||||||
"@sveltejs/kit": "^2.9.0",
|
"@sveltejs/kit": "^2.9.0",
|
||||||
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
||||||
|
"@tanstack/svelte-query": "^5.62.9",
|
||||||
|
"@tanstack/svelte-query-devtools": "^5.62.9",
|
||||||
"@types/cookie": "^1.0.0",
|
"@types/cookie": "^1.0.0",
|
||||||
"@types/node": "^22.10.2",
|
"@types/node": "^22.10.2",
|
||||||
"@types/pg": "^8.11.10",
|
"@types/pg": "^8.11.10",
|
||||||
|
|
@ -48,7 +53,6 @@
|
||||||
"storybook": "^8.4.7",
|
"storybook": "^8.4.7",
|
||||||
"svelte": "^5.16.0",
|
"svelte": "^5.16.0",
|
||||||
"svelte-check": "^4.0.0",
|
"svelte-check": "^4.0.0",
|
||||||
"svelte-headless-table": "^0.18.3",
|
|
||||||
"svelte-meta-tags": "^4.0.4",
|
"svelte-meta-tags": "^4.0.4",
|
||||||
"svelte-preprocess": "^6.0.3",
|
"svelte-preprocess": "^6.0.3",
|
||||||
"svelte-sequential-preprocessor": "^2.0.2",
|
"svelte-sequential-preprocessor": "^2.0.2",
|
||||||
|
|
|
||||||
649
pnpm-lock.yaml
649
pnpm-lock.yaml
|
|
@ -123,6 +123,15 @@ importers:
|
||||||
'@chromatic-com/storybook':
|
'@chromatic-com/storybook':
|
||||||
specifier: ^3.2.3
|
specifier: ^3.2.3
|
||||||
version: 3.2.3(react@18.3.1)(storybook@8.4.7)
|
version: 3.2.3(react@18.3.1)(storybook@8.4.7)
|
||||||
|
'@dicebear/collection':
|
||||||
|
specifier: ^9.2.2
|
||||||
|
version: 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/converter':
|
||||||
|
specifier: ^9.2.2
|
||||||
|
version: 9.2.2
|
||||||
|
'@dicebear/core':
|
||||||
|
specifier: ^9.2.2
|
||||||
|
version: 9.2.2
|
||||||
'@faker-js/faker':
|
'@faker-js/faker':
|
||||||
specifier: ^9.3.0
|
specifier: ^9.3.0
|
||||||
version: 9.3.0
|
version: 9.3.0
|
||||||
|
|
@ -162,6 +171,12 @@ importers:
|
||||||
'@sveltejs/vite-plugin-svelte':
|
'@sveltejs/vite-plugin-svelte':
|
||||||
specifier: ^5.0.3
|
specifier: ^5.0.3
|
||||||
version: 5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1))
|
version: 5.0.3(svelte@5.16.0)(vite@6.0.6(@types/node@22.10.2)(jiti@1.21.7)(yaml@2.6.1))
|
||||||
|
'@tanstack/svelte-query':
|
||||||
|
specifier: ^5.62.9
|
||||||
|
version: 5.62.9(svelte@5.16.0)
|
||||||
|
'@tanstack/svelte-query-devtools':
|
||||||
|
specifier: ^5.62.9
|
||||||
|
version: 5.62.9(@tanstack/svelte-query@5.62.9(svelte@5.16.0))(svelte@5.16.0)
|
||||||
'@types/cookie':
|
'@types/cookie':
|
||||||
specifier: ^1.0.0
|
specifier: ^1.0.0
|
||||||
version: 1.0.0
|
version: 1.0.0
|
||||||
|
|
@ -201,9 +216,6 @@ importers:
|
||||||
svelte-check:
|
svelte-check:
|
||||||
specifier: ^4.0.0
|
specifier: ^4.0.0
|
||||||
version: 4.1.1(picomatch@4.0.2)(svelte@5.16.0)(typescript@5.7.2)
|
version: 4.1.1(picomatch@4.0.2)(svelte@5.16.0)(typescript@5.7.2)
|
||||||
svelte-headless-table:
|
|
||||||
specifier: ^0.18.3
|
|
||||||
version: 0.18.3(svelte@5.16.0)
|
|
||||||
svelte-meta-tags:
|
svelte-meta-tags:
|
||||||
specifier: ^4.0.4
|
specifier: ^4.0.4
|
||||||
version: 4.0.4(svelte@5.16.0)(typescript@5.7.2)
|
version: 4.0.4(svelte@5.16.0)(typescript@5.7.2)
|
||||||
|
|
@ -342,6 +354,200 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0
|
storybook: ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0
|
||||||
|
|
||||||
|
'@dicebear/adventurer-neutral@9.2.2':
|
||||||
|
resolution: {integrity: sha512-XVAjhUWjav6luTZ7txz8zVJU/H0DiUy4uU1Z7IO5MDO6kWvum+If1+0OUgEWYZwM+RDI7rt2CgVP910DyZGd1w==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/adventurer@9.2.2':
|
||||||
|
resolution: {integrity: sha512-WjBXCP9EXbUul2zC3BS2/R3/4diw1uh/lU4jTEnujK1mhqwIwanFboIMzQsasNNL/xf+m3OHN7MUNJfHZ1fLZA==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/avataaars-neutral@9.2.2':
|
||||||
|
resolution: {integrity: sha512-pRj16P27dFDBI3LtdiHUDwIXIGndHAbZf5AxaMkn6/+0X93mVQ/btVJDXyW0G96WCsyC88wKAWr6/KJotPxU6Q==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/avataaars@9.2.2':
|
||||||
|
resolution: {integrity: sha512-WqJPQEt0OhBybTpI0TqU1uD1pSk9M2+VPIwvBye/dXo46b+0jHGpftmxjQwk6tX8z0+mRko8pwV5n+cWht1/+w==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/big-ears-neutral@9.2.2':
|
||||||
|
resolution: {integrity: sha512-IPHt8fi3dv9cyfBJBZ4s8T+PhFCrQvOCf91iRHBT3iOLNPdyZpI5GNLmGiV0XMAvIDP5NvA5+f6wdoBLhYhbDA==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/big-ears@9.2.2':
|
||||||
|
resolution: {integrity: sha512-hz4UXdPq4qqZpu0YVvlqM4RDFhk5i0WgPcuwj/MOLlgTjuj63uHUhCQSk6ZiW1DQOs12qpwUBMGWVHxBRBas9g==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/big-smile@9.2.2':
|
||||||
|
resolution: {integrity: sha512-D4td0GL8or1nTNnXvZqkEXlzyqzGPWs3znOnm1HIohtFTeIwXm72Ob2lNDsaQJSJvXmVlwaQQ0CCTvyCl8Stjw==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/bottts-neutral@9.2.2':
|
||||||
|
resolution: {integrity: sha512-lSgpqmSJtlnyxVuUgNdBwyzuA0O9xa5zRJtz7x2KyWbicXir5iYdX0MVMCkp1EDvlcxm9rGJsclktugOyakTlw==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/bottts@9.2.2':
|
||||||
|
resolution: {integrity: sha512-wugFkzw8JNWV1nftq/Wp/vmQsLAXDxrMtRK3AoMODuUpSVoP3EHRUfKS043xggOsQFvoj0HZ7kadmhn0AMLf5A==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/collection@9.2.2':
|
||||||
|
resolution: {integrity: sha512-vZAmXhPWCK3sf8Fj9/QflFC6XOLroJOT5K1HdnzHaPboEvffUQideGCrrEamnJtlH0iF0ZDXh8gqmwy2fu+yHA==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/converter@9.2.2':
|
||||||
|
resolution: {integrity: sha512-704MOowKUOGI88tOJIYFSkd2HXbkNtPdjMrPTx7j+7gAqImCBZ298KVUk0TCo36VCUfN0EtgngUuBZUxyPTadQ==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
|
||||||
|
'@dicebear/core@9.2.2':
|
||||||
|
resolution: {integrity: sha512-ROhgHG249dPtcXgBHcqPEsDeAPRPRD/9d+tZCjLYyueO+cXDlIA8dUlxpwIVcOuZFvCyW6RJtqo8BhNAi16pIQ==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
|
||||||
|
'@dicebear/croodles-neutral@9.2.2':
|
||||||
|
resolution: {integrity: sha512-/4mNirxoQ+z1kHXnpDRbJ1JV1ZgXogeTeNp0MaFYxocCgHfJ7ckNM23EE1I7akoo9pqPxrKlaeNzGAjKHdS9vA==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/croodles@9.2.2':
|
||||||
|
resolution: {integrity: sha512-OzvAXQWsOgMwL3Sl+lBxCubqSOWoBJpC78c4TKnNTS21rR63TtXUyVdLLzgKVN4YHRnvMgtPf8F/W9YAgIDK4w==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/dylan@9.2.2':
|
||||||
|
resolution: {integrity: sha512-s7e3XliC1YXP+Wykj+j5kwdOWFRXFzYHYk/PB4oZ1F3sJandXiG0HS4chaNu4EoP0yZgKyFMUVTGZx+o6tMaYg==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/fun-emoji@9.2.2':
|
||||||
|
resolution: {integrity: sha512-M+rYTpB3lfwz18f+/i+ggNwNWUoEj58SJqXJ1wr7Jh/4E5uL+NmJg9JGwYNaVtGbCFrKAjSaILNUWGQSFgMfog==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/glass@9.2.2':
|
||||||
|
resolution: {integrity: sha512-imCMxcg+XScHYtQq2MUv1lCzhQSCUglMlPSezKEpXhTxgbgUpmGlSGVkOfmX5EEc7SQowKkF1W/1gNk6CXvBaQ==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/icons@9.2.2':
|
||||||
|
resolution: {integrity: sha512-Tqq2OVCdS7J02DNw58xwlgLGl40sWEckbqXT3qRvIF63FfVq+wQZBGuhuiyAURcSgvsc3h2oQeYFi9iXh7HTOA==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/identicon@9.2.2':
|
||||||
|
resolution: {integrity: sha512-POVKFulIrcuZf3rdAgxYaSm2XUg/TJg3tg9zq9150reEGPpzWR7ijyJ03dzAADPzS3DExfdYVT9+z3JKwwJnTQ==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/initials@9.2.2':
|
||||||
|
resolution: {integrity: sha512-/xNnsEmsstWjmF77htAOuwOMhFlP6eBVXgcgFlTl/CCH/Oc6H7t0vwX1he8KLQBBzjGpvJcvIAn4Wh9rE4D5/A==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/lorelei-neutral@9.2.2':
|
||||||
|
resolution: {integrity: sha512-Eys9Os6nt2Xll7Mvu66CfRR2YggTopWcmFcRZ9pPdohS96kT0MsLI2iTcfZXQ51K8hvT3IbwoGc86W8n0cDxAQ==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/lorelei@9.2.2':
|
||||||
|
resolution: {integrity: sha512-koXqVr/vcWUPo00VP5H6Czsit+uF1tmwd2NK7Q/e34/9Sd1f4QLLxHjjBNm/iNjCI1+UNTOvZ2Qqu0N5eo7Flw==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/micah@9.2.2':
|
||||||
|
resolution: {integrity: sha512-NCajcJV5yw8uMKiACp694w1T/UyYme2CUEzyTzWHgWnQ+drAuCcH8gpAoLWd67viNdQB/MTpNlaelUgTjmI4AQ==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/miniavs@9.2.2':
|
||||||
|
resolution: {integrity: sha512-vvkWXttdw+KHF3j+9qcUFzK+P0nbNnImGjvN48wwkPIh2h08WWFq0MnoOls4IHwUJC4GXBjWtiyVoCxz6hhtOA==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/notionists-neutral@9.2.2':
|
||||||
|
resolution: {integrity: sha512-AhOzk+lz6kB4uxGun8AJhV+W1nttnMlxmxd+5KbQ/txCIziYIaeD3il44wsAGegEpGFvAZyMYtR/jjfHcem3TA==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/notionists@9.2.2':
|
||||||
|
resolution: {integrity: sha512-Z9orRaHoj7Y9Ap4wEu8XOrFACsG1KbbBQUPV1R50uh6AHwsyNrm4cS84ICoGLvxgLNHHOae3YCjd8aMu2z19zg==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/open-peeps@9.2.2':
|
||||||
|
resolution: {integrity: sha512-6PeQDHYyjvKrGSl/gP+RE5dSYAQGKpcGnM65HorgyTIugZK7STo0W4hvEycedupZ3MCCEH8x/XyiChKM2sHXog==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/personas@9.2.2':
|
||||||
|
resolution: {integrity: sha512-705+ObNLC0w1fcgE/Utav+8bqO+Esu53TXegpX5j7trGEoIMf2bThqJGHuhknZ3+T2az3Wr89cGyOGlI0KLzLA==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/pixel-art-neutral@9.2.2':
|
||||||
|
resolution: {integrity: sha512-CdUY77H6Aj7dKLW3hdkv7tu0XQJArUjaWoXihQxlhl3oVYplWaoyu9omYy5pl8HTqs8YgVTGljjMXYoFuK0JUw==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/pixel-art@9.2.2':
|
||||||
|
resolution: {integrity: sha512-BvbFdrpzQl04+Y9UsWP63YGug+ENGC7GMG88qbEFWxb/IqRavGa4H3D0T4Zl2PSLiw7f2Ctv98bsCQZ1PtCznQ==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/rings@9.2.2':
|
||||||
|
resolution: {integrity: sha512-eD1J1k364Arny+UlvGrk12HP/XGG6WxPSm4BarFqdJGSV45XOZlwqoi7FlcMr9r9yvE/nGL8OizbwMYusEEdjw==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/shapes@9.2.2':
|
||||||
|
resolution: {integrity: sha512-e741NNWBa7fg0BjomxXa0fFPME2XCIR0FA+VHdq9AD2taTGHEPsg5x1QJhCRdK6ww85yeu3V3ucpZXdSrHVw5Q==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
|
'@dicebear/thumbs@9.2.2':
|
||||||
|
resolution: {integrity: sha512-FkPLDNu7n5kThLSk7lR/0cz/NkUqgGdZGfLZv6fLkGNGtv6W+e2vZaO7HCXVwIgJ+II+kImN41zVIZ6Jlll7pQ==}
|
||||||
|
engines: {node: '>=18.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
'@dicebear/core': ^9.0.0
|
||||||
|
|
||||||
'@drizzle-team/brocli@0.10.2':
|
'@drizzle-team/brocli@0.10.2':
|
||||||
resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==}
|
resolution: {integrity: sha512-z33Il7l5dKjUgGULTqBsQBQwckHh5AbIuxhdsIxDDiZAzBOrZO6q9ogcWC65kU382AfynTfgNumVcNIjuIua6w==}
|
||||||
|
|
||||||
|
|
@ -1349,6 +1555,9 @@ packages:
|
||||||
resolution: {integrity: sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==}
|
resolution: {integrity: sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
'@photostructure/tz-lookup@11.0.0':
|
||||||
|
resolution: {integrity: sha512-QMV5/dWtY/MdVPXZs/EApqzyhnqDq1keYEqpS+Xj2uidyaqw2Nk/fWcsszdruIXjdqp1VoWNzsgrO6bUHU1mFw==}
|
||||||
|
|
||||||
'@pkgjs/parseargs@0.11.0':
|
'@pkgjs/parseargs@0.11.0':
|
||||||
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
|
@ -1365,6 +1574,82 @@ packages:
|
||||||
resolution: {integrity: sha512-B4iV6QxW//Fn17+qF1EMZRmoThIUJlCtcO85yoRDJnMyHeAthjz4ig9OTkfGGXKtQhcdPX0me75gU5K9J897+w==}
|
resolution: {integrity: sha512-B4iV6QxW//Fn17+qF1EMZRmoThIUJlCtcO85yoRDJnMyHeAthjz4ig9OTkfGGXKtQhcdPX0me75gU5K9J897+w==}
|
||||||
engines: {node: '>=18.16.0'}
|
engines: {node: '>=18.16.0'}
|
||||||
|
|
||||||
|
'@resvg/resvg-js-android-arm-eabi@2.6.2':
|
||||||
|
resolution: {integrity: sha512-FrJibrAk6v29eabIPgcTUMPXiEz8ssrAk7TXxsiZzww9UTQ1Z5KAbFJs+Z0Ez+VZTYgnE5IQJqBcoSiMebtPHA==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@resvg/resvg-js-android-arm64@2.6.2':
|
||||||
|
resolution: {integrity: sha512-VcOKezEhm2VqzXpcIJoITuvUS/fcjIw5NA/w3tjzWyzmvoCdd+QXIqy3FBGulWdClvp4g+IfUemigrkLThSjAQ==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [android]
|
||||||
|
|
||||||
|
'@resvg/resvg-js-darwin-arm64@2.6.2':
|
||||||
|
resolution: {integrity: sha512-nmok2LnAd6nLUKI16aEB9ydMC6Lidiiq2m1nEBDR1LaaP7FGs4AJ90qDraxX+CWlVuRlvNjyYJTNv8qFjtL9+A==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@resvg/resvg-js-darwin-x64@2.6.2':
|
||||||
|
resolution: {integrity: sha512-GInyZLjgWDfsVT6+SHxQVRwNzV0AuA1uqGsOAW+0th56J7Nh6bHHKXHBWzUrihxMetcFDmQMAX1tZ1fZDYSRsw==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [darwin]
|
||||||
|
|
||||||
|
'@resvg/resvg-js-linux-arm-gnueabihf@2.6.2':
|
||||||
|
resolution: {integrity: sha512-YIV3u/R9zJbpqTTNwTZM5/ocWetDKGsro0SWp70eGEM9eV2MerWyBRZnQIgzU3YBnSBQ1RcxRZvY/UxwESfZIw==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [arm]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@resvg/resvg-js-linux-arm64-gnu@2.6.2':
|
||||||
|
resolution: {integrity: sha512-zc2BlJSim7YR4FZDQ8OUoJg5holYzdiYMeobb9pJuGDidGL9KZUv7SbiD4E8oZogtYY42UZEap7dqkkYuA91pg==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@resvg/resvg-js-linux-arm64-musl@2.6.2':
|
||||||
|
resolution: {integrity: sha512-3h3dLPWNgSsD4lQBJPb4f+kvdOSJHa5PjTYVsWHxLUzH4IFTJUAnmuWpw4KqyQ3NA5QCyhw4TWgxk3jRkQxEKg==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@resvg/resvg-js-linux-x64-gnu@2.6.2':
|
||||||
|
resolution: {integrity: sha512-IVUe+ckIerA7xMZ50duAZzwf1U7khQe2E0QpUxu5MBJNao5RqC0zwV/Zm965vw6D3gGFUl7j4m+oJjubBVoftw==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@resvg/resvg-js-linux-x64-musl@2.6.2':
|
||||||
|
resolution: {integrity: sha512-UOf83vqTzoYQO9SZ0fPl2ZIFtNIz/Rr/y+7X8XRX1ZnBYsQ/tTb+cj9TE+KHOdmlTFBxhYzVkP2lRByCzqi4jQ==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [linux]
|
||||||
|
|
||||||
|
'@resvg/resvg-js-win32-arm64-msvc@2.6.2':
|
||||||
|
resolution: {integrity: sha512-7C/RSgCa+7vqZ7qAbItfiaAWhyRSoD4l4BQAbVDqRRsRgY+S+hgS3in0Rxr7IorKUpGE69X48q6/nOAuTJQxeQ==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [arm64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@resvg/resvg-js-win32-ia32-msvc@2.6.2':
|
||||||
|
resolution: {integrity: sha512-har4aPAlvjnLcil40AC77YDIk6loMawuJwFINEM7n0pZviwMkMvjb2W5ZirsNOZY4aDbo5tLx0wNMREp5Brk+w==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [ia32]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@resvg/resvg-js-win32-x64-msvc@2.6.2':
|
||||||
|
resolution: {integrity: sha512-ZXtYhtUr5SSaBrUDq7DiyjOFJqBVL/dOBN7N/qmi/pO0IgiWW/f/ue3nbvu9joWE5aAKDoIzy/CxsY0suwGosQ==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
cpu: [x64]
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
'@resvg/resvg-js@2.6.2':
|
||||||
|
resolution: {integrity: sha512-xBaJish5OeGmniDj9cW5PRa/PtmuVU3ziqrbr5xJj901ZDN4TosrVaNZpEiLZAxdfnhAe7uQ7QFWfjPe9d9K2Q==}
|
||||||
|
engines: {node: '>= 10'}
|
||||||
|
|
||||||
'@rollup/plugin-commonjs@28.0.2':
|
'@rollup/plugin-commonjs@28.0.2':
|
||||||
resolution: {integrity: sha512-BEFI2EDqzl+vA1rl97IDRZ61AIwGH093d9nz8+dThxJNH8oSoB7MjWvPCX3dkaK1/RCJ/1v/R1XB15FuSs0fQw==}
|
resolution: {integrity: sha512-BEFI2EDqzl+vA1rl97IDRZ61AIwGH093d9nz8+dThxJNH8oSoB7MjWvPCX3dkaK1/RCJ/1v/R1XB15FuSs0fQw==}
|
||||||
engines: {node: '>=16.0.0 || 14 >= 14.17'}
|
engines: {node: '>=16.0.0 || 14 >= 14.17'}
|
||||||
|
|
@ -1761,6 +2046,23 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20'
|
tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20'
|
||||||
|
|
||||||
|
'@tanstack/query-core@5.62.9':
|
||||||
|
resolution: {integrity: sha512-lwePd8hNYhyQ4nM/iRQ+Wz2cDtspGeZZHFZmCzHJ7mfKXt+9S301fULiY2IR2byJYY6Z03T427E5PoVfMexHjw==}
|
||||||
|
|
||||||
|
'@tanstack/query-devtools@5.62.9':
|
||||||
|
resolution: {integrity: sha512-b1NZzDLVf6laJsB1Cfm3ieuYzM+WqoO8qpm9v+3Etwd+Ph4zkhUMiT+wcWj5AhEPsXiRodKYiiW048VDNdBxNg==}
|
||||||
|
|
||||||
|
'@tanstack/svelte-query-devtools@5.62.9':
|
||||||
|
resolution: {integrity: sha512-baESJCUDBIJIRiwfodW/j0BU8c5uADk37A0UfadOU8cWFt9M67zawKltJmut7AoEXbwLWYqTO+3HU2XKCyJEWw==}
|
||||||
|
peerDependencies:
|
||||||
|
'@tanstack/svelte-query': ^5.62.9
|
||||||
|
svelte: ^3.54.0 || ^4.0.0 || ^5.0.0-next.0
|
||||||
|
|
||||||
|
'@tanstack/svelte-query@5.62.9':
|
||||||
|
resolution: {integrity: sha512-2M/CpePioU4IRw1OOdA+/KwA+0swJAb3c0uipFOoWkuT11uC4tTe8UD/lYRQpJYFFn5hML7KYRxVOW0HH60XiA==}
|
||||||
|
peerDependencies:
|
||||||
|
svelte: ^3.54.0 || ^4.0.0 || ^5.0.0-next.0
|
||||||
|
|
||||||
'@testing-library/dom@10.4.0':
|
'@testing-library/dom@10.4.0':
|
||||||
resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==}
|
resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
@ -1800,6 +2102,9 @@ packages:
|
||||||
'@types/jsonwebtoken@9.0.7':
|
'@types/jsonwebtoken@9.0.7':
|
||||||
resolution: {integrity: sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==}
|
resolution: {integrity: sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==}
|
||||||
|
|
||||||
|
'@types/luxon@3.4.2':
|
||||||
|
resolution: {integrity: sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==}
|
||||||
|
|
||||||
'@types/mdx@2.0.13':
|
'@types/mdx@2.0.13':
|
||||||
resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==}
|
resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==}
|
||||||
|
|
||||||
|
|
@ -2027,6 +2332,10 @@ packages:
|
||||||
balanced-match@1.0.2:
|
balanced-match@1.0.2:
|
||||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||||
|
|
||||||
|
batch-cluster@13.0.0:
|
||||||
|
resolution: {integrity: sha512-EreW0Vi8TwovhYUHBXXRA5tthuU2ynGsZFlboyMJHCCUXYa2AjgwnE3ubBOJs2xJLcuXFJbi6c/8pH5+FVj8Og==}
|
||||||
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
before-after-hook@2.2.3:
|
before-after-hook@2.2.3:
|
||||||
resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==}
|
resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==}
|
||||||
|
|
||||||
|
|
@ -2695,6 +3004,17 @@ packages:
|
||||||
eventemitter3@5.0.1:
|
eventemitter3@5.0.1:
|
||||||
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
|
resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==}
|
||||||
|
|
||||||
|
exiftool-vendored.exe@13.0.0:
|
||||||
|
resolution: {integrity: sha512-4zAMuFGgxZkOoyQIzZMHv1HlvgyJK3AkNqjAgm8A8V0UmOZO7yv3pH49cDV1OduzFJqgs6yQ6eG4OGydhKtxlg==}
|
||||||
|
os: [win32]
|
||||||
|
|
||||||
|
exiftool-vendored.pl@13.0.1:
|
||||||
|
resolution: {integrity: sha512-+BRRzjselpWudKR0ltAW5SUt9T82D+gzQN8DdOQUgnSVWWp7oLCeTGBRptbQz+436Ihn/mPzmo/xnf0cv/Qw1A==}
|
||||||
|
os: ['!win32']
|
||||||
|
|
||||||
|
exiftool-vendored@28.8.0:
|
||||||
|
resolution: {integrity: sha512-R7tirJLr9fWuH9JS/KFFLB+O7jNGKuPXGxREc6YybYangEudGb+X8ERsYXk9AifMiAWh/2agNfbgkbcQcF+MxA==}
|
||||||
|
|
||||||
expect-type@1.1.0:
|
expect-type@1.1.0:
|
||||||
resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==}
|
resolution: {integrity: sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==}
|
||||||
engines: {node: '>=12.0.0'}
|
engines: {node: '>=12.0.0'}
|
||||||
|
|
@ -2898,6 +3218,10 @@ packages:
|
||||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
he@1.2.0:
|
||||||
|
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
help-me@5.0.0:
|
help-me@5.0.0:
|
||||||
resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==}
|
resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==}
|
||||||
|
|
||||||
|
|
@ -3188,6 +3512,10 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
svelte: ^3 || ^4 || ^5.0.0-next.42
|
svelte: ^3 || ^4 || ^5.0.0-next.42
|
||||||
|
|
||||||
|
luxon@3.5.0:
|
||||||
|
resolution: {integrity: sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
lz-string@1.5.0:
|
lz-string@1.5.0:
|
||||||
resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
|
resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
@ -4018,16 +4346,6 @@ packages:
|
||||||
svelte: ^4.0.0 || ^5.0.0-next.0
|
svelte: ^4.0.0 || ^5.0.0-next.0
|
||||||
typescript: '>=5.0.0'
|
typescript: '>=5.0.0'
|
||||||
|
|
||||||
svelte-headless-table@0.18.3:
|
|
||||||
resolution: {integrity: sha512-1zVnqXW0dvn6ZceYa94k+ziK+w5Dj9nlWYTQGXBv2JhM0resj9w7CWpclZK1TJwAALfEeH4InPBPO87L5fr+nQ==}
|
|
||||||
peerDependencies:
|
|
||||||
svelte: ^4.0.0
|
|
||||||
|
|
||||||
svelte-keyed@2.0.0:
|
|
||||||
resolution: {integrity: sha512-7TeEn+QbJC2OJrHiuM0T8vMBkms3DNpTE+Ir+NtnVBnBMA78aL4f1ft9t0Hn/pBbD/TnIXi4YfjFRAgtN+DZ5g==}
|
|
||||||
peerDependencies:
|
|
||||||
svelte: ^4.0.0
|
|
||||||
|
|
||||||
svelte-meta-tags@4.0.4:
|
svelte-meta-tags@4.0.4:
|
||||||
resolution: {integrity: sha512-i0vgxGreM3lXTTxLSuPQLE1n56KAFACHWIXRj7fJCTpd/5D16O97Ha/OXDZS4Lsk+D347VEK4LeMoacsftbeKw==}
|
resolution: {integrity: sha512-i0vgxGreM3lXTTxLSuPQLE1n56KAFACHWIXRj7fJCTpd/5D16O97Ha/OXDZS4Lsk+D347VEK4LeMoacsftbeKw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|
@ -4112,11 +4430,6 @@ packages:
|
||||||
typescript:
|
typescript:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
svelte-render@2.0.1:
|
|
||||||
resolution: {integrity: sha512-RpB0SurwXm4xhjvHHtjeqMmvd645FURb79GFOotScOSqnKK5vpqBgoBPGC0pp+E/eZgDSQ9rRAdn/+N4ys1mXQ==}
|
|
||||||
peerDependencies:
|
|
||||||
svelte: ^4.0.0
|
|
||||||
|
|
||||||
svelte-sequential-preprocessor@2.0.2:
|
svelte-sequential-preprocessor@2.0.2:
|
||||||
resolution: {integrity: sha512-DIFm0kSNscVxtBmKkBiygAHB5otoqN1aVmJ3t57jZhJfCB7Np/lUSoTtSrvPFjmlBbMeOsb1VQ06cut1+rBYOg==}
|
resolution: {integrity: sha512-DIFm0kSNscVxtBmKkBiygAHB5otoqN1aVmJ3t57jZhJfCB7Np/lUSoTtSrvPFjmlBbMeOsb1VQ06cut1+rBYOg==}
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
|
|
@ -4126,11 +4439,6 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
svelte: ^3.0.0 || ^4.0.0 || ^5.0.0-next.1
|
svelte: ^3.0.0 || ^4.0.0 || ^5.0.0-next.1
|
||||||
|
|
||||||
svelte-subscribe@2.0.1:
|
|
||||||
resolution: {integrity: sha512-eKXIjLxB4C7eQWPqKEdxcGfNXm2g/qJ67zmEZK/GigCZMfrTR3m7DPY93R6MX+5uoqM1FRYxl8LZ1oy4URWi2A==}
|
|
||||||
peerDependencies:
|
|
||||||
svelte: ^4.0.0
|
|
||||||
|
|
||||||
svelte-toolbelt@0.4.6:
|
svelte-toolbelt@0.4.6:
|
||||||
resolution: {integrity: sha512-k8OUvXBUifHZcAlWeY/HLg/4J0v5m2iOfOhn8fDmjt4AP8ZluaDh9eBFus9lFiLX6O5l6vKqI1dKL5wy7090NQ==}
|
resolution: {integrity: sha512-k8OUvXBUifHZcAlWeY/HLg/4J0v5m2iOfOhn8fDmjt4AP8ZluaDh9eBFus9lFiLX6O5l6vKqI1dKL5wy7090NQ==}
|
||||||
engines: {node: '>=18', pnpm: '>=8.7.0'}
|
engines: {node: '>=18', pnpm: '>=8.7.0'}
|
||||||
|
|
@ -4239,6 +4547,13 @@ packages:
|
||||||
resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==}
|
resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==}
|
||||||
engines: {node: '>=14.0.0'}
|
engines: {node: '>=14.0.0'}
|
||||||
|
|
||||||
|
tmp-promise@3.0.3:
|
||||||
|
resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==}
|
||||||
|
|
||||||
|
tmp@0.2.3:
|
||||||
|
resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==}
|
||||||
|
engines: {node: '>=14.14'}
|
||||||
|
|
||||||
to-regex-range@5.0.1:
|
to-regex-range@5.0.1:
|
||||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||||
engines: {node: '>=8.0'}
|
engines: {node: '>=8.0'}
|
||||||
|
|
@ -4656,6 +4971,171 @@ snapshots:
|
||||||
- '@chromatic-com/playwright'
|
- '@chromatic-com/playwright'
|
||||||
- react
|
- react
|
||||||
|
|
||||||
|
'@dicebear/adventurer-neutral@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/adventurer@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/avataaars-neutral@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/avataaars@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/big-ears-neutral@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/big-ears@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/big-smile@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/bottts-neutral@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/bottts@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/collection@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/adventurer': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/adventurer-neutral': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/avataaars': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/avataaars-neutral': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/big-ears': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/big-ears-neutral': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/big-smile': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/bottts': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/bottts-neutral': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
'@dicebear/croodles': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/croodles-neutral': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/dylan': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/fun-emoji': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/glass': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/icons': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/identicon': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/initials': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/lorelei': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/lorelei-neutral': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/micah': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/miniavs': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/notionists': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/notionists-neutral': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/open-peeps': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/personas': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/pixel-art': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/pixel-art-neutral': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/rings': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/shapes': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
'@dicebear/thumbs': 9.2.2(@dicebear/core@9.2.2)
|
||||||
|
|
||||||
|
'@dicebear/converter@9.2.2':
|
||||||
|
dependencies:
|
||||||
|
'@resvg/resvg-js': 2.6.2
|
||||||
|
exiftool-vendored: 28.8.0
|
||||||
|
sharp: 0.33.5
|
||||||
|
tmp-promise: 3.0.3
|
||||||
|
|
||||||
|
'@dicebear/core@9.2.2':
|
||||||
|
dependencies:
|
||||||
|
'@types/json-schema': 7.0.15
|
||||||
|
|
||||||
|
'@dicebear/croodles-neutral@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/croodles@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/dylan@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/fun-emoji@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/glass@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/icons@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/identicon@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/initials@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/lorelei-neutral@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/lorelei@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/micah@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/miniavs@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/notionists-neutral@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/notionists@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/open-peeps@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/personas@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/pixel-art-neutral@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/pixel-art@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/rings@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/shapes@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
|
'@dicebear/thumbs@9.2.2(@dicebear/core@9.2.2)':
|
||||||
|
dependencies:
|
||||||
|
'@dicebear/core': 9.2.2
|
||||||
|
|
||||||
'@drizzle-team/brocli@0.10.2': {}
|
'@drizzle-team/brocli@0.10.2': {}
|
||||||
|
|
||||||
'@emnapi/runtime@1.3.1':
|
'@emnapi/runtime@1.3.1':
|
||||||
|
|
@ -5505,6 +5985,8 @@ snapshots:
|
||||||
|
|
||||||
'@phc/format@1.0.0': {}
|
'@phc/format@1.0.0': {}
|
||||||
|
|
||||||
|
'@photostructure/tz-lookup@11.0.0': {}
|
||||||
|
|
||||||
'@pkgjs/parseargs@0.11.0':
|
'@pkgjs/parseargs@0.11.0':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
|
@ -5517,6 +5999,57 @@ snapshots:
|
||||||
'@poppinss/macroable@1.0.3':
|
'@poppinss/macroable@1.0.3':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@resvg/resvg-js-android-arm-eabi@2.6.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@resvg/resvg-js-android-arm64@2.6.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@resvg/resvg-js-darwin-arm64@2.6.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@resvg/resvg-js-darwin-x64@2.6.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@resvg/resvg-js-linux-arm-gnueabihf@2.6.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@resvg/resvg-js-linux-arm64-gnu@2.6.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@resvg/resvg-js-linux-arm64-musl@2.6.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@resvg/resvg-js-linux-x64-gnu@2.6.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@resvg/resvg-js-linux-x64-musl@2.6.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@resvg/resvg-js-win32-arm64-msvc@2.6.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@resvg/resvg-js-win32-ia32-msvc@2.6.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@resvg/resvg-js-win32-x64-msvc@2.6.2':
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
'@resvg/resvg-js@2.6.2':
|
||||||
|
optionalDependencies:
|
||||||
|
'@resvg/resvg-js-android-arm-eabi': 2.6.2
|
||||||
|
'@resvg/resvg-js-android-arm64': 2.6.2
|
||||||
|
'@resvg/resvg-js-darwin-arm64': 2.6.2
|
||||||
|
'@resvg/resvg-js-darwin-x64': 2.6.2
|
||||||
|
'@resvg/resvg-js-linux-arm-gnueabihf': 2.6.2
|
||||||
|
'@resvg/resvg-js-linux-arm64-gnu': 2.6.2
|
||||||
|
'@resvg/resvg-js-linux-arm64-musl': 2.6.2
|
||||||
|
'@resvg/resvg-js-linux-x64-gnu': 2.6.2
|
||||||
|
'@resvg/resvg-js-linux-x64-musl': 2.6.2
|
||||||
|
'@resvg/resvg-js-win32-arm64-msvc': 2.6.2
|
||||||
|
'@resvg/resvg-js-win32-ia32-msvc': 2.6.2
|
||||||
|
'@resvg/resvg-js-win32-x64-msvc': 2.6.2
|
||||||
|
|
||||||
'@rollup/plugin-commonjs@28.0.2(rollup@4.29.1)':
|
'@rollup/plugin-commonjs@28.0.2(rollup@4.29.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@rollup/pluginutils': 5.1.4(rollup@4.29.1)
|
'@rollup/pluginutils': 5.1.4(rollup@4.29.1)
|
||||||
|
|
@ -5993,6 +6526,22 @@ snapshots:
|
||||||
postcss-selector-parser: 6.0.10
|
postcss-selector-parser: 6.0.10
|
||||||
tailwindcss: 3.4.17
|
tailwindcss: 3.4.17
|
||||||
|
|
||||||
|
'@tanstack/query-core@5.62.9': {}
|
||||||
|
|
||||||
|
'@tanstack/query-devtools@5.62.9': {}
|
||||||
|
|
||||||
|
'@tanstack/svelte-query-devtools@5.62.9(@tanstack/svelte-query@5.62.9(svelte@5.16.0))(svelte@5.16.0)':
|
||||||
|
dependencies:
|
||||||
|
'@tanstack/query-devtools': 5.62.9
|
||||||
|
'@tanstack/svelte-query': 5.62.9(svelte@5.16.0)
|
||||||
|
esm-env: 1.2.1
|
||||||
|
svelte: 5.16.0
|
||||||
|
|
||||||
|
'@tanstack/svelte-query@5.62.9(svelte@5.16.0)':
|
||||||
|
dependencies:
|
||||||
|
'@tanstack/query-core': 5.62.9
|
||||||
|
svelte: 5.16.0
|
||||||
|
|
||||||
'@testing-library/dom@10.4.0':
|
'@testing-library/dom@10.4.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/code-frame': 7.26.2
|
'@babel/code-frame': 7.26.2
|
||||||
|
|
@ -6032,13 +6581,14 @@ snapshots:
|
||||||
|
|
||||||
'@types/estree@1.0.6': {}
|
'@types/estree@1.0.6': {}
|
||||||
|
|
||||||
'@types/json-schema@7.0.15':
|
'@types/json-schema@7.0.15': {}
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@types/jsonwebtoken@9.0.7':
|
'@types/jsonwebtoken@9.0.7':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.10.2
|
'@types/node': 22.10.2
|
||||||
|
|
||||||
|
'@types/luxon@3.4.2': {}
|
||||||
|
|
||||||
'@types/mdx@2.0.13': {}
|
'@types/mdx@2.0.13': {}
|
||||||
|
|
||||||
'@types/node@22.10.2':
|
'@types/node@22.10.2':
|
||||||
|
|
@ -6287,6 +6837,8 @@ snapshots:
|
||||||
|
|
||||||
balanced-match@1.0.2: {}
|
balanced-match@1.0.2: {}
|
||||||
|
|
||||||
|
batch-cluster@13.0.0: {}
|
||||||
|
|
||||||
before-after-hook@2.2.3: {}
|
before-after-hook@2.2.3: {}
|
||||||
|
|
||||||
better-opn@3.0.2:
|
better-opn@3.0.2:
|
||||||
|
|
@ -6922,6 +7474,23 @@ snapshots:
|
||||||
|
|
||||||
eventemitter3@5.0.1: {}
|
eventemitter3@5.0.1: {}
|
||||||
|
|
||||||
|
exiftool-vendored.exe@13.0.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
exiftool-vendored.pl@13.0.1:
|
||||||
|
optional: true
|
||||||
|
|
||||||
|
exiftool-vendored@28.8.0:
|
||||||
|
dependencies:
|
||||||
|
'@photostructure/tz-lookup': 11.0.0
|
||||||
|
'@types/luxon': 3.4.2
|
||||||
|
batch-cluster: 13.0.0
|
||||||
|
he: 1.2.0
|
||||||
|
luxon: 3.5.0
|
||||||
|
optionalDependencies:
|
||||||
|
exiftool-vendored.exe: 13.0.0
|
||||||
|
exiftool-vendored.pl: 13.0.1
|
||||||
|
|
||||||
expect-type@1.1.0: {}
|
expect-type@1.1.0: {}
|
||||||
|
|
||||||
express-rate-limit@7.5.0(express@4.21.2):
|
express-rate-limit@7.5.0(express@4.21.2):
|
||||||
|
|
@ -7147,6 +7716,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
function-bind: 1.1.2
|
function-bind: 1.1.2
|
||||||
|
|
||||||
|
he@1.2.0: {}
|
||||||
|
|
||||||
help-me@5.0.0: {}
|
help-me@5.0.0: {}
|
||||||
|
|
||||||
hono-pino@0.7.0(hono@4.6.14)(pino@9.6.0):
|
hono-pino@0.7.0(hono@4.6.14)(pino@9.6.0):
|
||||||
|
|
@ -7419,6 +7990,8 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
svelte: 5.16.0
|
svelte: 5.16.0
|
||||||
|
|
||||||
|
luxon@3.5.0: {}
|
||||||
|
|
||||||
lz-string@1.5.0: {}
|
lz-string@1.5.0: {}
|
||||||
|
|
||||||
magic-string@0.30.17:
|
magic-string@0.30.17:
|
||||||
|
|
@ -8270,17 +8843,6 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- picomatch
|
- picomatch
|
||||||
|
|
||||||
svelte-headless-table@0.18.3(svelte@5.16.0):
|
|
||||||
dependencies:
|
|
||||||
svelte: 5.16.0
|
|
||||||
svelte-keyed: 2.0.0(svelte@5.16.0)
|
|
||||||
svelte-render: 2.0.1(svelte@5.16.0)
|
|
||||||
svelte-subscribe: 2.0.1(svelte@5.16.0)
|
|
||||||
|
|
||||||
svelte-keyed@2.0.0(svelte@5.16.0):
|
|
||||||
dependencies:
|
|
||||||
svelte: 5.16.0
|
|
||||||
|
|
||||||
svelte-meta-tags@4.0.4(svelte@5.16.0)(typescript@5.7.2):
|
svelte-meta-tags@4.0.4(svelte@5.16.0)(typescript@5.7.2):
|
||||||
dependencies:
|
dependencies:
|
||||||
schema-dts: 1.1.2(typescript@5.7.2)
|
schema-dts: 1.1.2(typescript@5.7.2)
|
||||||
|
|
@ -8313,11 +8875,6 @@ snapshots:
|
||||||
postcss-load-config: 4.0.2(postcss@8.4.49)
|
postcss-load-config: 4.0.2(postcss@8.4.49)
|
||||||
typescript: 5.7.2
|
typescript: 5.7.2
|
||||||
|
|
||||||
svelte-render@2.0.1(svelte@5.16.0):
|
|
||||||
dependencies:
|
|
||||||
svelte: 5.16.0
|
|
||||||
svelte-subscribe: 2.0.1(svelte@5.16.0)
|
|
||||||
|
|
||||||
svelte-sequential-preprocessor@2.0.2:
|
svelte-sequential-preprocessor@2.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
svelte: 4.2.19
|
svelte: 4.2.19
|
||||||
|
|
@ -8327,10 +8884,6 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
svelte: 5.16.0
|
svelte: 5.16.0
|
||||||
|
|
||||||
svelte-subscribe@2.0.1(svelte@5.16.0):
|
|
||||||
dependencies:
|
|
||||||
svelte: 5.16.0
|
|
||||||
|
|
||||||
svelte-toolbelt@0.4.6(svelte@5.16.0):
|
svelte-toolbelt@0.4.6(svelte@5.16.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
clsx: 2.1.1
|
clsx: 2.1.1
|
||||||
|
|
@ -8502,6 +9055,12 @@ snapshots:
|
||||||
|
|
||||||
tinyspy@3.0.2: {}
|
tinyspy@3.0.2: {}
|
||||||
|
|
||||||
|
tmp-promise@3.0.3:
|
||||||
|
dependencies:
|
||||||
|
tmp: 0.2.3
|
||||||
|
|
||||||
|
tmp@0.2.3: {}
|
||||||
|
|
||||||
to-regex-range@5.0.1:
|
to-regex-range@5.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-number: 7.0.0
|
is-number: 7.0.0
|
||||||
|
|
|
||||||
1
project.inlang/project_id
Normal file
1
project.inlang/project_id
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
eb7d81211e6faa5e843476a33150228f5db02e3c62748d6eff4e45b709691d18
|
||||||
|
|
@ -4,5 +4,7 @@ import { sequence } from '@sveltejs/kit/hooks';
|
||||||
import { startServer } from '$lib/server/api';
|
import { startServer } from '$lib/server/api';
|
||||||
|
|
||||||
const handleParaglide: Handle = i18n.handle();
|
const handleParaglide: Handle = i18n.handle();
|
||||||
|
|
||||||
startServer();
|
startServer();
|
||||||
|
|
||||||
export const handle: Handle = sequence(handleParaglide);
|
export const handle: Handle = sequence(handleParaglide);
|
||||||
|
|
|
||||||
16
src/lib/components/ui/card/card-content.svelte
Normal file
16
src/lib/components/ui/card/card-content.svelte
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { WithElementRef } from "bits-ui";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={ref} class={cn("p-6", className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
||||||
16
src/lib/components/ui/card/card-description.svelte
Normal file
16
src/lib/components/ui/card/card-description.svelte
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { WithElementRef } from "bits-ui";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLParagraphElement>> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<p bind:this={ref} class={cn("text-muted-foreground text-sm", className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</p>
|
||||||
16
src/lib/components/ui/card/card-footer.svelte
Normal file
16
src/lib/components/ui/card/card-footer.svelte
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { WithElementRef } from "bits-ui";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={ref} class={cn("flex items-center p-6 pt-0", className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
||||||
16
src/lib/components/ui/card/card-header.svelte
Normal file
16
src/lib/components/ui/card/card-header.svelte
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { WithElementRef } from "bits-ui";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div bind:this={ref} class={cn("flex flex-col space-y-1.5 p-6 pb-0", className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
||||||
25
src/lib/components/ui/card/card-title.svelte
Normal file
25
src/lib/components/ui/card/card-title.svelte
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { WithElementRef } from "bits-ui";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
level = 3,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> & {
|
||||||
|
level?: 1 | 2 | 3 | 4 | 5 | 6;
|
||||||
|
} = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
role="heading"
|
||||||
|
aria-level={level}
|
||||||
|
bind:this={ref}
|
||||||
|
class={cn("text-2xl font-semibold leading-none tracking-tight", className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
||||||
20
src/lib/components/ui/card/card.svelte
Normal file
20
src/lib/components/ui/card/card.svelte
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { WithElementRef } from "bits-ui";
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
bind:this={ref}
|
||||||
|
class={cn("bg-card text-card-foreground rounded-lg border shadow-sm", className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</div>
|
||||||
22
src/lib/components/ui/card/index.ts
Normal file
22
src/lib/components/ui/card/index.ts
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
import Root from "./card.svelte";
|
||||||
|
import Content from "./card-content.svelte";
|
||||||
|
import Description from "./card-description.svelte";
|
||||||
|
import Footer from "./card-footer.svelte";
|
||||||
|
import Header from "./card-header.svelte";
|
||||||
|
import Title from "./card-title.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Content,
|
||||||
|
Description,
|
||||||
|
Footer,
|
||||||
|
Header,
|
||||||
|
Title,
|
||||||
|
//
|
||||||
|
Root as Card,
|
||||||
|
Content as CardContent,
|
||||||
|
Description as CardDescription,
|
||||||
|
Footer as CardFooter,
|
||||||
|
Header as CardHeader,
|
||||||
|
Title as CardTitle,
|
||||||
|
};
|
||||||
28
src/lib/components/ui/table/index.ts
Normal file
28
src/lib/components/ui/table/index.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import Root from "./table.svelte";
|
||||||
|
import Body from "./table-body.svelte";
|
||||||
|
import Caption from "./table-caption.svelte";
|
||||||
|
import Cell from "./table-cell.svelte";
|
||||||
|
import Footer from "./table-footer.svelte";
|
||||||
|
import Head from "./table-head.svelte";
|
||||||
|
import Header from "./table-header.svelte";
|
||||||
|
import Row from "./table-row.svelte";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Root,
|
||||||
|
Body,
|
||||||
|
Caption,
|
||||||
|
Cell,
|
||||||
|
Footer,
|
||||||
|
Head,
|
||||||
|
Header,
|
||||||
|
Row,
|
||||||
|
//
|
||||||
|
Root as Table,
|
||||||
|
Body as TableBody,
|
||||||
|
Caption as TableCaption,
|
||||||
|
Cell as TableCell,
|
||||||
|
Footer as TableFooter,
|
||||||
|
Head as TableHead,
|
||||||
|
Header as TableHeader,
|
||||||
|
Row as TableRow,
|
||||||
|
};
|
||||||
16
src/lib/components/ui/table/table-body.svelte
Normal file
16
src/lib/components/ui/table/table-body.svelte
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import type { WithElementRef } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLTableSectionElement>> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<tbody bind:this={ref} class={cn("[&_tr:last-child]:border-0", className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</tbody>
|
||||||
16
src/lib/components/ui/table/table-caption.svelte
Normal file
16
src/lib/components/ui/table/table-caption.svelte
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import type { WithElementRef } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLElement>> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<caption bind:this={ref} class={cn("text-muted-foreground mt-4 text-sm", className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</caption>
|
||||||
20
src/lib/components/ui/table/table-cell.svelte
Normal file
20
src/lib/components/ui/table/table-cell.svelte
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLTdAttributes } from "svelte/elements";
|
||||||
|
import type { WithElementRef } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLTdAttributes> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<td
|
||||||
|
bind:this={ref}
|
||||||
|
class={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</td>
|
||||||
16
src/lib/components/ui/table/table-footer.svelte
Normal file
16
src/lib/components/ui/table/table-footer.svelte
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import type { WithElementRef } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLTableSectionElement>> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<tfoot bind:this={ref} class={cn("bg-muted/50 font-medium", className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</tfoot>
|
||||||
23
src/lib/components/ui/table/table-head.svelte
Normal file
23
src/lib/components/ui/table/table-head.svelte
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLThAttributes } from "svelte/elements";
|
||||||
|
import type { WithElementRef } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLThAttributes> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<th
|
||||||
|
bind:this={ref}
|
||||||
|
class={cn(
|
||||||
|
"text-muted-foreground h-12 px-4 text-left align-middle font-medium [&:has([role=checkbox])]:pr-0",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</th>
|
||||||
16
src/lib/components/ui/table/table-header.svelte
Normal file
16
src/lib/components/ui/table/table-header.svelte
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import type { WithElementRef } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLTableSectionElement>> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<thead bind:this={ref} class={cn("[&_tr]:border-b", className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</thead>
|
||||||
23
src/lib/components/ui/table/table-row.svelte
Normal file
23
src/lib/components/ui/table/table-row.svelte
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLAttributes } from "svelte/elements";
|
||||||
|
import type { WithElementRef } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLAttributes<HTMLTableRowElement>> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<tr
|
||||||
|
bind:this={ref}
|
||||||
|
class={cn(
|
||||||
|
"hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...restProps}
|
||||||
|
>
|
||||||
|
{@render children?.()}
|
||||||
|
</tr>
|
||||||
18
src/lib/components/ui/table/table.svelte
Normal file
18
src/lib/components/ui/table/table.svelte
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { HTMLTableAttributes } from "svelte/elements";
|
||||||
|
import type { WithElementRef } from "bits-ui";
|
||||||
|
import { cn } from "$lib/utils.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
ref = $bindable(null),
|
||||||
|
class: className,
|
||||||
|
children,
|
||||||
|
...restProps
|
||||||
|
}: WithElementRef<HTMLTableAttributes> = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="relative w-full overflow-auto">
|
||||||
|
<table bind:this={ref} class={cn("w-full caption-bottom text-sm", className)} {...restProps}>
|
||||||
|
{@render children?.()}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
23
src/lib/components/user-avatar.svelte
Normal file
23
src/lib/components/user-avatar.svelte
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
<script lang="ts" module>
|
||||||
|
import type { HTMLAttributes } from 'svelte/elements';
|
||||||
|
interface Props extends HTMLAttributes<HTMLImageElement> {
|
||||||
|
user: {
|
||||||
|
id: string;
|
||||||
|
avatar: string | null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { createAvatar } from '@dicebear/core';
|
||||||
|
import { funEmoji } from '@dicebear/collection';
|
||||||
|
|
||||||
|
const { user, ...props }: Props = $props();
|
||||||
|
const avatar =
|
||||||
|
user.avatar ||
|
||||||
|
createAvatar(funEmoji, {
|
||||||
|
seed: user.id
|
||||||
|
}).toDataUri();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<img {...props} src={avatar} />
|
||||||
|
|
@ -1 +1,3 @@
|
||||||
|
export * from '../../roles/tables/roles.table';
|
||||||
|
export * from '../../users/tables/user-roles.table';
|
||||||
export * from '../../users/tables/users.table';
|
export * from '../../users/tables/users.table';
|
||||||
|
|
|
||||||
107
src/lib/server/api/mfa/mfa.controller.ts
Normal file
107
src/lib/server/api/mfa/mfa.controller.ts
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
import { requireFullAuth, requireTempAuth } from '$lib/server/api/common/middleware/require-auth.middleware';
|
||||||
|
import { Controller } from '$lib/server/api/common/types/controller';
|
||||||
|
import { verifyTotpDto } from '$lib/server/api/dtos/verify-totp.dto';
|
||||||
|
import { TotpService } from '$lib/server/api/mfa/totp.service';
|
||||||
|
import { RecoveryCodesService } from '$lib/server/api/users/recovery-codes.service';
|
||||||
|
import { UsersService } from '$lib/server/api/users/users.service';
|
||||||
|
import { StatusCodes } from '$lib/utils/status-codes';
|
||||||
|
import { zValidator } from '@hono/zod-validator';
|
||||||
|
import { inject, injectable } from '@needle-di/core';
|
||||||
|
import { decodeBase64 } from '@oslojs/encoding';
|
||||||
|
import { cookieExpiresAt, createSessionTokenCookie, setSessionCookie } from '../common/utils/cookies';
|
||||||
|
import { CredentialsType } from '../databases/postgres/tables';
|
||||||
|
import { createTwoFactorSchema } from '../dtos/create-totp.dto';
|
||||||
|
import { LoginRequestsService } from '../login/loginrequest.service';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class MfaController extends Controller {
|
||||||
|
constructor(
|
||||||
|
private loginRequestService = inject(LoginRequestsService),
|
||||||
|
private recoveryCodesService = inject(RecoveryCodesService),
|
||||||
|
private totpService = inject(TotpService),
|
||||||
|
private usersService = inject(UsersService),
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
routes() {
|
||||||
|
return this.controller
|
||||||
|
.get('/totp', requireTempAuth, async (c) => {
|
||||||
|
const user = c.var.user;
|
||||||
|
c.var.logger.info(`The user ${user.id} is requesting TOTP credentials`);
|
||||||
|
const totpCredential = await this.totpService.findOneByUserId(user.id);
|
||||||
|
return c.json({ totpCredential });
|
||||||
|
})
|
||||||
|
.post('/totp', requireTempAuth, zValidator('json', createTwoFactorSchema), async (c) => {
|
||||||
|
const user = c.var.user;
|
||||||
|
const { key } = c.req.valid('json');
|
||||||
|
const totpCredential = await this.totpService.create(user.id, decodeBase64(key));
|
||||||
|
if (totpCredential) {
|
||||||
|
await this.usersService.updateUser(user.id, { mfa_enabled: true });
|
||||||
|
return c.json({ totpCredential });
|
||||||
|
}
|
||||||
|
return c.status(StatusCodes.INTERNAL_SERVER_ERROR);
|
||||||
|
})
|
||||||
|
.delete('/totp', requireFullAuth, async (c) => {
|
||||||
|
const user = c.var.user;
|
||||||
|
try {
|
||||||
|
await this.totpService.deleteOneByUserIdAndType(user.id, CredentialsType.TOTP);
|
||||||
|
await this.recoveryCodesService.deleteAllRecoveryCodesByUserId(user.id);
|
||||||
|
await this.usersService.updateUser(user.id, { mfa_enabled: false });
|
||||||
|
console.log('TOTP deleted');
|
||||||
|
return c.body(null, StatusCodes.NO_CONTENT);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return c.status(StatusCodes.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.get('/totp/recoveryCodes', requireFullAuth, async (c) => {
|
||||||
|
const user = c.var.user;
|
||||||
|
// You can only view recovery codes once and that is on creation
|
||||||
|
const existingCodes = await this.recoveryCodesService.findAllRecoveryCodesByUserId(user.id);
|
||||||
|
if (existingCodes && existingCodes.length > 0) {
|
||||||
|
console.log('Recovery Codes found', existingCodes);
|
||||||
|
// Filter out codes that are not used and only return the code
|
||||||
|
const codes = existingCodes.filter((code) => !code.used).map((code) => code.code);
|
||||||
|
return c.json({ recoveryCodes: codes });
|
||||||
|
}
|
||||||
|
const recoveryCodes = await this.recoveryCodesService.createRecoveryCodes(user.id);
|
||||||
|
return c.json({ recoveryCodes });
|
||||||
|
})
|
||||||
|
.post('/totp/recoveryCodes', requireFullAuth, zValidator('json', verifyTotpDto), async (c) => {
|
||||||
|
try {
|
||||||
|
const user = c.var.user;
|
||||||
|
const { code } = c.req.valid('json');
|
||||||
|
c.var.logger.info(`Verifying code ${code} for user ${user.id}`);
|
||||||
|
const verified = await this.recoveryCodesService.verify(user.id, code);
|
||||||
|
if (verified) {
|
||||||
|
return c.json({}, StatusCodes.OK);
|
||||||
|
}
|
||||||
|
return c.json('Invalid code', StatusCodes.BAD_REQUEST);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return c.status(StatusCodes.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.post('/totp/verify', requireTempAuth, zValidator('json', verifyTotpDto), async (c) => {
|
||||||
|
try {
|
||||||
|
const user = c.var.user;
|
||||||
|
const { code } = c.req.valid('json');
|
||||||
|
c.var.logger.info(`Verifying code ${code} for user ${user.id}`);
|
||||||
|
const verified = await this.totpService.verify(user.id, code);
|
||||||
|
if (verified) {
|
||||||
|
await this.usersService.updateUser(user.id, { mfa_enabled: true });
|
||||||
|
const session = await this.loginRequestService.createUserSession(user.id, c.req, true, true);
|
||||||
|
const sessionCookie = createSessionTokenCookie(session.id, cookieExpiresAt);
|
||||||
|
console.log('set cookie', sessionCookie);
|
||||||
|
setSessionCookie(c, sessionCookie);
|
||||||
|
return c.json({}, StatusCodes.OK);
|
||||||
|
}
|
||||||
|
return c.json('Invalid code', StatusCodes.BAD_REQUEST);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return c.status(StatusCodes.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
36
src/lib/server/api/mfa/tables/two-factor.table.ts
Normal file
36
src/lib/server/api/mfa/tables/two-factor.table.ts
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||||
|
import { boolean, pgTable, text, timestamp, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import { id, timestamps } from '../../common/utils/drizzle';
|
||||||
|
import { usersTable } from '../../users/tables/users.table';
|
||||||
|
import { generateId } from '../../common/utils/crypto';
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Table */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
export const twoFactorTable = pgTable('two_factor', {
|
||||||
|
id: id()
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => generateId()),
|
||||||
|
secret: text().notNull(),
|
||||||
|
enabled: boolean().notNull().default(false),
|
||||||
|
user_id: id()
|
||||||
|
.notNull()
|
||||||
|
.references(() => usersTable.id)
|
||||||
|
.unique('two_factor_user_id_unique'),
|
||||||
|
...timestamps,
|
||||||
|
});
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Relations */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
export const emailVerificationsRelations = relations(twoFactorTable, ({ one }) => ({
|
||||||
|
user: one(usersTable, {
|
||||||
|
fields: [twoFactorTable.user_id],
|
||||||
|
references: [usersTable.id],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Types */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
export type TwoFactor = InferSelectModel<typeof twoFactorTable>;
|
||||||
57
src/lib/server/api/mfa/totp.service.ts
Normal file
57
src/lib/server/api/mfa/totp.service.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
import { inject, injectable } from '@needle-di/core';
|
||||||
|
import { decodeBase64, encodeBase64 } from '@oslojs/encoding';
|
||||||
|
import { generateTOTP, verifyTOTP } from '@oslojs/otp';
|
||||||
|
import { EncryptionService } from '../common/services/encryption.service';
|
||||||
|
import type { CredentialsType } from '../databases/postgres/tables';
|
||||||
|
import { CredentialsRepository } from '../users/credentials.repository';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
export class TotpService {
|
||||||
|
constructor(
|
||||||
|
private credentialsRepository = inject(CredentialsRepository),
|
||||||
|
private encryptionService = inject(EncryptionService),
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async findOneByUserId(userId: string) {
|
||||||
|
return this.credentialsRepository.findTOTPCredentialsByUserId(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOneByUserIdOrThrow(userId: string) {
|
||||||
|
const credential = await this.findOneByUserId(userId);
|
||||||
|
if (!credential) {
|
||||||
|
throw new Error('TOTP credential not found');
|
||||||
|
}
|
||||||
|
return credential;
|
||||||
|
}
|
||||||
|
|
||||||
|
async create(userId: string, key: Uint8Array) {
|
||||||
|
try {
|
||||||
|
return await this.credentialsRepository.create({
|
||||||
|
user_id: userId,
|
||||||
|
secret_data: encodeBase64(this.encryptionService.encrypt(key)),
|
||||||
|
type: 'totp',
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteOneByUserId(userId: string) {
|
||||||
|
return this.credentialsRepository.deleteByUserId(userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteOneByUserIdAndType(userId: string, type: CredentialsType) {
|
||||||
|
return this.credentialsRepository.deleteByUserIdAndType(userId, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
async verify(userId: string, code: string) {
|
||||||
|
const credential = await this.credentialsRepository.findTOTPCredentialsByUserId(userId);
|
||||||
|
console.log(`TOTP credential: ${JSON.stringify(credential)}`);
|
||||||
|
if (!credential) {
|
||||||
|
throw new Error('TOTP credential not found');
|
||||||
|
}
|
||||||
|
const secret = this.encryptionService.decrypt(decodeBase64(credential.secret_data));
|
||||||
|
return verifyTOTP(secret, 30, 6, code);
|
||||||
|
}
|
||||||
|
}
|
||||||
38
src/lib/server/api/roles/tables/roles.table.ts
Normal file
38
src/lib/server/api/roles/tables/roles.table.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { type InferSelectModel, relations } from 'drizzle-orm';
|
||||||
|
import { pgTable, text } from 'drizzle-orm/pg-core';
|
||||||
|
import { user_roles } from '../../users/tables/user-roles.table';
|
||||||
|
import { timestamps } from '../../common/utils/drizzle';
|
||||||
|
import { id } from '../../common/utils/drizzle';
|
||||||
|
import { generateId } from '../../common/utils/crypto';
|
||||||
|
|
||||||
|
export enum RoleName {
|
||||||
|
ADMIN = 'admin',
|
||||||
|
EDITOR = 'editor',
|
||||||
|
MODERATOR = 'moderator',
|
||||||
|
USER = 'user',
|
||||||
|
}
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Table */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
export const rolesTable = pgTable('roles', {
|
||||||
|
id: id()
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => generateId()),
|
||||||
|
name: text().unique().notNull(),
|
||||||
|
...timestamps,
|
||||||
|
});
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Relations */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
export const role_relations = relations(rolesTable, ({ many }) => ({
|
||||||
|
user_roles: many(user_roles),
|
||||||
|
}));
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Types */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
export type Roles = InferSelectModel<typeof rolesTable>;
|
||||||
|
|
||||||
|
|
||||||
29
src/lib/server/api/users/tables/credentials.table.ts
Normal file
29
src/lib/server/api/users/tables/credentials.table.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import type { InferSelectModel } from 'drizzle-orm';
|
||||||
|
import { pgTable, text } from 'drizzle-orm/pg-core';
|
||||||
|
import { id, timestamps } from '../../common/utils/drizzle';
|
||||||
|
import { usersTable } from './users.table';
|
||||||
|
import { generateId } from '../../common/utils/crypto';
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Table */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
export enum CredentialsType {
|
||||||
|
SECRET = 'secret',
|
||||||
|
PASSWORD = 'password',
|
||||||
|
TOTP = 'totp',
|
||||||
|
HOTP = 'hotp',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const credentialsTable = pgTable('credentials', {
|
||||||
|
id: id()
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => generateId()),
|
||||||
|
user_id: id()
|
||||||
|
.notNull()
|
||||||
|
.references(() => usersTable.id, { onDelete: 'cascade' }),
|
||||||
|
type: text().notNull().default(CredentialsType.PASSWORD),
|
||||||
|
secret_data: text().notNull(),
|
||||||
|
...timestamps,
|
||||||
|
});
|
||||||
|
|
||||||
|
export type Credentials = InferSelectModel<typeof credentialsTable>;
|
||||||
50
src/lib/server/api/users/tables/user-roles.table.ts
Normal file
50
src/lib/server/api/users/tables/user-roles.table.ts
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { type InferSelectModel, relations, getTableColumns } from 'drizzle-orm';
|
||||||
|
import { boolean, pgTable, text, uuid } from 'drizzle-orm/pg-core';
|
||||||
|
import { rolesTable } from '../../roles/tables/roles.table';
|
||||||
|
import { usersTable } from './users.table';
|
||||||
|
import { generateId } from '../../common/utils/crypto';
|
||||||
|
import { id, timestamps } from '../../common/utils/drizzle';
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Table */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
export const user_roles = pgTable('user_roles', {
|
||||||
|
id: id()
|
||||||
|
.primaryKey()
|
||||||
|
.$defaultFn(() => generateId()),
|
||||||
|
user_id: uuid()
|
||||||
|
.notNull()
|
||||||
|
.references(() => usersTable.id, { onDelete: 'cascade' }),
|
||||||
|
role_id: uuid()
|
||||||
|
.notNull()
|
||||||
|
.references(() => rolesTable.id, { onDelete: 'cascade' }),
|
||||||
|
primary: boolean().default(false),
|
||||||
|
...timestamps,
|
||||||
|
});
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Relations */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
export const user_role_relations = relations(user_roles, ({ one }) => ({
|
||||||
|
role: one(rolesTable, {
|
||||||
|
fields: [user_roles.role_id],
|
||||||
|
references: [rolesTable.id],
|
||||||
|
}),
|
||||||
|
user: one(usersTable, {
|
||||||
|
fields: [user_roles.user_id],
|
||||||
|
references: [usersTable.id],
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Types */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
export type UserRoles = InferSelectModel<typeof user_roles>;
|
||||||
|
export type UserRolesWithRelations = UserRoles & {};
|
||||||
|
|
||||||
|
const userRolesColumns = getTableColumns(user_roles);
|
||||||
|
|
||||||
|
export const publicUserColumns = {
|
||||||
|
id: userRolesColumns.id,
|
||||||
|
...timestamps,
|
||||||
|
};
|
||||||
|
|
@ -1,24 +1,32 @@
|
||||||
import { pgTable, text } from 'drizzle-orm/pg-core';
|
import { boolean, pgTable, text } from 'drizzle-orm/pg-core';
|
||||||
import { getTableColumns, type InferSelectModel, relations } from 'drizzle-orm';
|
import { getTableColumns, type InferSelectModel, relations } from 'drizzle-orm';
|
||||||
import { citext, id, timestamps } from '../../common/utils/drizzle';
|
import { citext, id, timestamps } from '../../common/utils/drizzle';
|
||||||
import { generateId } from '../../common/utils/crypto';
|
import { generateId } from '../../common/utils/crypto';
|
||||||
|
import { user_roles } from './user-roles.table';
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* Table */
|
/* Table */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
export const usersTable = pgTable('users', {
|
export const usersTable = pgTable('users', {
|
||||||
id: id()
|
id: id()
|
||||||
.primaryKey()
|
.primaryKey()
|
||||||
.$defaultFn(() => generateId()),
|
.$defaultFn(() => generateId()),
|
||||||
email: citext().unique().notNull(),
|
username: text().unique(),
|
||||||
avatar: text(),
|
email: citext().unique().notNull(),
|
||||||
...timestamps
|
first_name: text(),
|
||||||
|
last_name: text(),
|
||||||
|
email_verified: boolean().default(false),
|
||||||
|
mfa_enabled: boolean().notNull().default(false),
|
||||||
|
avatar: text(),
|
||||||
|
...timestamps,
|
||||||
});
|
});
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* Relations */
|
/* Relations */
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
export const usersRelations = relations(usersTable, () => ({}));
|
export const userRelations = relations(usersTable, ({ many }) => ({
|
||||||
|
user_roles: many(user_roles),
|
||||||
|
}));
|
||||||
|
|
||||||
/* -------------------------------------------------------------------------- */
|
/* -------------------------------------------------------------------------- */
|
||||||
/* Types */
|
/* Types */
|
||||||
|
|
@ -29,8 +37,11 @@ export type UserWithRelations = User & {};
|
||||||
const userColumns = getTableColumns(usersTable);
|
const userColumns = getTableColumns(usersTable);
|
||||||
|
|
||||||
export const publicUserColumns = {
|
export const publicUserColumns = {
|
||||||
id: userColumns.id,
|
id: userColumns.id,
|
||||||
email: userColumns.email,
|
username: userColumns.username,
|
||||||
avatar: userColumns.avatar,
|
email: userColumns.email,
|
||||||
...timestamps
|
avatar: userColumns.avatar,
|
||||||
|
first_name: userColumns.first_name,
|
||||||
|
last_name: userColumns.last_name,
|
||||||
|
...timestamps,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
34
src/lib/tanstack-query/iam.ts
Normal file
34
src/lib/tanstack-query/iam.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
import type { InferRequestType } from 'hono';
|
||||||
|
import { parseApiResponse } from '$lib/utils/api';
|
||||||
|
import type { Api, ApiMutation } from '$lib/utils/types';
|
||||||
|
import { TanstackQueryModule } from './query-module';
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Types */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
type RequestLogin = Api['iam']['login']['request']['$post'];
|
||||||
|
type VerifyLogin = Api['iam']['login']['verify']['$post'];
|
||||||
|
type Logout = Api['iam']['logout']['$post'];
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Api */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
export class IamModule extends TanstackQueryModule<'iam'> {
|
||||||
|
logout(): ApiMutation<Logout> {
|
||||||
|
return {
|
||||||
|
mutationFn: async () => await this.api.iam.logout.$post().then(parseApiResponse)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
requestLogin(): ApiMutation<RequestLogin> {
|
||||||
|
return {
|
||||||
|
mutationFn: async (data: InferRequestType<RequestLogin>) =>
|
||||||
|
await this.api.iam.login.request.$post(data).then(parseApiResponse)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
verifyLogin(): ApiMutation<VerifyLogin> {
|
||||||
|
return {
|
||||||
|
mutationFn: async (data: InferRequestType<VerifyLogin>) =>
|
||||||
|
await this.api.iam.login.verify.$post(data).then(parseApiResponse)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/lib/tanstack-query/index.ts
Normal file
11
src/lib/tanstack-query/index.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { IamModule } from './iam';
|
||||||
|
import type { ClientRequestOptions } from 'hono';
|
||||||
|
import { UsersModule } from './users';
|
||||||
|
import { TanstackQueryModule } from './query-module';
|
||||||
|
|
||||||
|
class TanstackQueryHandler extends TanstackQueryModule {
|
||||||
|
iam = new IamModule(this.opts);
|
||||||
|
users = new UsersModule(this.opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const queryHandler = (opts?: ClientRequestOptions) => new TanstackQueryHandler(opts);
|
||||||
13
src/lib/tanstack-query/query-module.ts
Normal file
13
src/lib/tanstack-query/query-module.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
import type { ClientRequestOptions } from 'hono';
|
||||||
|
import { api } from '$lib/utils/api';
|
||||||
|
|
||||||
|
export abstract class TanstackQueryModule<T extends string | null = null> {
|
||||||
|
protected readonly opts: ClientRequestOptions | undefined;
|
||||||
|
protected readonly api: ReturnType<typeof api>;
|
||||||
|
public namespace: T | null = null;
|
||||||
|
|
||||||
|
constructor(opts?: ClientRequestOptions) {
|
||||||
|
this.opts = opts;
|
||||||
|
this.api = api(opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/lib/tanstack-query/users.ts
Normal file
37
src/lib/tanstack-query/users.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { parseApiResponse } from '$lib/utils/api';
|
||||||
|
import type { Api, ApiMutation, ApiQuery } from '$lib/utils/types';
|
||||||
|
import type { InferRequestType } from 'hono';
|
||||||
|
import { TanstackQueryModule } from './query-module';
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Types */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
type Me = Api['users']['me']['$get'];
|
||||||
|
type UpdateEmailRequest = Api['users']['me']['email']['request']['$post'];
|
||||||
|
type VerifyEmailRequest = Api['users']['me']['email']['verify']['$post'];
|
||||||
|
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
/* Api */
|
||||||
|
/* -------------------------------------------------------------------------- */
|
||||||
|
export class UsersModule extends TanstackQueryModule<'users'> {
|
||||||
|
me(): ApiQuery<Me> {
|
||||||
|
return {
|
||||||
|
queryKey: [this.namespace, 'me'],
|
||||||
|
queryFn: async () => await this.api.users.me.$get().then(parseApiResponse)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
updateEmailRequest(): ApiMutation<UpdateEmailRequest> {
|
||||||
|
return {
|
||||||
|
mutationFn: async (args: InferRequestType<UpdateEmailRequest>) =>
|
||||||
|
await this.api.users.me.email.request.$post(args).then(parseApiResponse)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyEmailRequest(): ApiMutation<VerifyEmailRequest> {
|
||||||
|
return {
|
||||||
|
mutationFn: async (args: InferRequestType<VerifyEmailRequest>) =>
|
||||||
|
await this.api.users.me.email.verify.$post(args).then(parseApiResponse)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,9 +6,28 @@
|
||||||
import * as DropdownMenu from '$lib/components/ui/dropdown-menu/index.js';
|
import * as DropdownMenu from '$lib/components/ui/dropdown-menu/index.js';
|
||||||
import { Input } from '$lib/components/ui/input/index.js';
|
import { Input } from '$lib/components/ui/input/index.js';
|
||||||
import * as Sheet from '$lib/components/ui/sheet/index.js';
|
import * as Sheet from '$lib/components/ui/sheet/index.js';
|
||||||
import ThemeDropdown from '@/components/theme-dropdown.svelte';
|
import { createMutation } from '@tanstack/svelte-query';
|
||||||
|
import { authContext } from '$lib/hooks/session.svelte.js';
|
||||||
|
import { queryHandler } from '$lib/tanstack-query/index.js';
|
||||||
|
import { goto, invalidateAll } from '$app/navigation';
|
||||||
|
import UserAvatar from '$lib/components/user-avatar.svelte';
|
||||||
|
import ThemeDropdown from '$lib/components/theme-dropdown.svelte';
|
||||||
|
|
||||||
const { children, data } = $props();
|
const { children, data } = $props();
|
||||||
|
|
||||||
|
$effect.pre(() => {
|
||||||
|
authContext.setAuthedUser(data.authedUser);
|
||||||
|
});
|
||||||
|
|
||||||
|
const logoutMutation = createMutation({
|
||||||
|
...queryHandler().iam.logout(),
|
||||||
|
onSuccess: async () => {
|
||||||
|
await data.queryClient.invalidateQueries();
|
||||||
|
invalidateAll();
|
||||||
|
goto('/login');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
queryHandler;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex min-h-screen w-full flex-col">
|
<div class="flex min-h-screen w-full flex-col">
|
||||||
|
|
@ -71,7 +90,7 @@
|
||||||
<DropdownMenu.Root>
|
<DropdownMenu.Root>
|
||||||
<DropdownMenu.Trigger>
|
<DropdownMenu.Trigger>
|
||||||
<Button variant="secondary" size="icon" class="rounded-lg">
|
<Button variant="secondary" size="icon" class="rounded-lg">
|
||||||
<!-- <UserAvatar class="h-8 w-8 rounded-lg" user={data.authedUser} /> -->
|
<UserAvatar class="h-8 w-8 rounded-lg" user={data.authedUser} />
|
||||||
<span class="sr-only">Toggle user menu</span>
|
<span class="sr-only">Toggle user menu</span>
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenu.Trigger>
|
</DropdownMenu.Trigger>
|
||||||
|
|
@ -82,7 +101,7 @@
|
||||||
</DropdownMenu.Item>
|
</DropdownMenu.Item>
|
||||||
<DropdownMenu.Separator />
|
<DropdownMenu.Separator />
|
||||||
<DropdownMenu.Item >Logout</DropdownMenu.Item>
|
<DropdownMenu.Item >Logout</DropdownMenu.Item>
|
||||||
<!-- onclick={$logoutMutation.mutate} -->
|
onclick={$logoutMutation.mutate}
|
||||||
</DropdownMenu.Group>
|
</DropdownMenu.Group>
|
||||||
</DropdownMenu.Content>
|
</DropdownMenu.Content>
|
||||||
</DropdownMenu.Root>
|
</DropdownMenu.Root>
|
||||||
|
|
|
||||||
7
src/routes/(app)/+layout.ts
Normal file
7
src/routes/(app)/+layout.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { queryHandler } from '$lib/tanstack-query';
|
||||||
|
|
||||||
|
export const load = async ({ parent, fetch }) => {
|
||||||
|
const { queryClient } = await parent();
|
||||||
|
const authedUser = await queryClient.fetchQuery(queryHandler({ fetch }).users.me());
|
||||||
|
return { authedUser };
|
||||||
|
};
|
||||||
|
|
@ -1,14 +1,20 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { i18n } from "$lib/i18n";
|
import { i18n } from "$lib/i18n";
|
||||||
|
import { QueryClientProvider } from "@tanstack/svelte-query";
|
||||||
|
import { SvelteQueryDevtools } from "@tanstack/svelte-query-devtools";
|
||||||
import { ParaglideJS } from "@inlang/paraglide-sveltekit";
|
import { ParaglideJS } from "@inlang/paraglide-sveltekit";
|
||||||
import { ModeWatcher } from "mode-watcher";
|
import { ModeWatcher } from "mode-watcher";
|
||||||
import '../app.css';
|
import "../app.css";
|
||||||
let { children } = $props();
|
|
||||||
|
let { data, children } = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ParaglideJS {i18n}>
|
<QueryClientProvider client={data.queryClient}>
|
||||||
<ModeWatcher />
|
<ParaglideJS {i18n}>
|
||||||
<main>
|
<ModeWatcher />
|
||||||
{@render children()}
|
<main>
|
||||||
</main>
|
{@render children()}
|
||||||
</ParaglideJS>
|
</main>
|
||||||
|
<SvelteQueryDevtools />
|
||||||
|
</ParaglideJS>
|
||||||
|
</QueryClientProvider>
|
||||||
|
|
|
||||||
15
src/routes/+layout.ts
Normal file
15
src/routes/+layout.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { browser } from '$app/environment';
|
||||||
|
import { QueryClient } from '@tanstack/svelte-query';
|
||||||
|
|
||||||
|
export const load = async () => {
|
||||||
|
const queryClient = new QueryClient({
|
||||||
|
defaultOptions: {
|
||||||
|
queries: {
|
||||||
|
enabled: browser,
|
||||||
|
staleTime: 60 * 1000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return { queryClient };
|
||||||
|
};
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
<a href="/demo/paraglide">paraglide</a>
|
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
<script lang="ts">
|
|
||||||
import type { AvailableLanguageTag } from '$lib/paraglide/runtime';
|
|
||||||
import { i18n } from '$lib/i18n';
|
|
||||||
import { page } from '$app/stores';
|
|
||||||
import { goto } from '$app/navigation';
|
|
||||||
import * as m from '$lib/paraglide/messages.js';
|
|
||||||
|
|
||||||
function switchToLanguage(newLanguage: AvailableLanguageTag) {
|
|
||||||
const canonicalPath = i18n.route($page.url.pathname);
|
|
||||||
const localisedPath = i18n.resolveRoute(canonicalPath, newLanguage);
|
|
||||||
goto(localisedPath);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h1>{m.hello_world({ name: 'SvelteKit User' })}</h1>
|
|
||||||
<div>
|
|
||||||
<button onclick={() => switchToLanguage('en')}>en</button>
|
|
||||||
<button onclick={() => switchToLanguage('es')}>es</button>
|
|
||||||
</div>
|
|
||||||
Loading…
Reference in a new issue