From 9f53874d1d97481938d606418e839d457e27448e Mon Sep 17 00:00:00 2001 From: static Date: Thu, 17 Jul 2025 01:54:58 +0900 Subject: [PATCH 01/23] =?UTF-8?q?=EB=B9=84=EB=94=94=EC=98=A4=20=EC=9E=AC?= =?UTF-8?q?=EC=83=9D=EC=9D=B4=20=EC=A7=80=EC=9B=90=EB=90=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8A=94=20=ED=8F=AC=EB=A7=B7=EC=9D=BC=20=EB=95=8C=20?= =?UTF-8?q?=EC=8D=B8=EB=84=A4=EC=9D=BC=20=EC=83=9D=EC=84=B1=20=EC=9E=91?= =?UTF-8?q?=EC=97=85=EC=9D=B4=20=EB=AC=B4=ED=95=9C=ED=9E=88=20=EB=81=9D?= =?UTF-8?q?=EB=82=98=EC=A7=80=20=EC=95=8A=EB=8D=98=20=EB=B2=84=EA=B7=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/modules/thumbnail.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/lib/modules/thumbnail.ts b/src/lib/modules/thumbnail.ts index 2d5cd55..d9a995b 100644 --- a/src/lib/modules/thumbnail.ts +++ b/src/lib/modules/thumbnail.ts @@ -67,10 +67,15 @@ const generateVideoThumbnail = (videoUrl: string, time = 0) => { return new Promise((resolve, reject) => { const video = document.createElement("video"); video.onloadedmetadata = () => { - video.currentTime = Math.min(time, video.duration); - video.requestVideoFrameCallback(() => { + if (video.videoWidth === 0 || video.videoHeight === 0) { + return reject(); + } + + const callbackId = video.requestVideoFrameCallback(() => { captureVideoThumbnail(video).then(resolve).catch(reject); + video.cancelVideoFrameCallback(callbackId); }); + video.currentTime = Math.min(time, video.duration); }; video.onerror = reject; From 4e91cdad9522721838429bc738768bf531f42aac Mon Sep 17 00:00:00 2001 From: static Date: Sun, 20 Jul 2025 05:17:38 +0900 Subject: [PATCH 02/23] =?UTF-8?q?=EC=84=9C=EB=B2=84=EB=A1=9C=EB=B6=80?= =?UTF-8?q?=ED=84=B0=20=ED=8C=8C=EC=9D=BC=EC=9D=98=20DEK=EB=A5=BC=20?= =?UTF-8?q?=EB=8B=A4=EC=9A=B4=EB=A1=9C=EB=93=9C=ED=95=9C=20=ED=9B=84?= =?UTF-8?q?=EC=97=90=EC=95=BC=20=EC=8D=B8=EB=84=A4=EC=9D=BC=EC=9D=B4=20?= =?UTF-8?q?=ED=91=9C=EC=8B=9C=EB=90=98=EB=8D=98=20=ED=98=84=EC=83=81=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/components/organisms/Category/File.svelte | 2 +- src/lib/services/file.ts | 4 ++-- .../(main)/directory/[[id]]/DirectoryEntries/File.svelte | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib/components/organisms/Category/File.svelte b/src/lib/components/organisms/Category/File.svelte index 7d49cf3..8e3fc12 100644 --- a/src/lib/components/organisms/Category/File.svelte +++ b/src/lib/components/organisms/Category/File.svelte @@ -32,7 +32,7 @@ }; $effect(() => { - if ($info?.dataKey) { + if ($info) { requestFileThumbnailDownload($info.id, $info.dataKey) .then((thumbnailUrl) => { thumbnail = thumbnailUrl ?? undefined; diff --git a/src/lib/services/file.ts b/src/lib/services/file.ts index 11742c8..bab3dac 100644 --- a/src/lib/services/file.ts +++ b/src/lib/services/file.ts @@ -48,9 +48,9 @@ export const requestFileThumbnailUpload = async ( return await fetch(`/api/file/${fileId}/thumbnail/upload`, { method: "POST", body: form }); }; -export const requestFileThumbnailDownload = async (fileId: number, dataKey: CryptoKey) => { +export const requestFileThumbnailDownload = async (fileId: number, dataKey?: CryptoKey) => { const cache = await getFileThumbnailCache(fileId); - if (cache) return cache; + if (cache || !dataKey) return cache; let res = await callGetApi(`/api/file/${fileId}/thumbnail`); if (!res.ok) return null; diff --git a/src/routes/(main)/directory/[[id]]/DirectoryEntries/File.svelte b/src/routes/(main)/directory/[[id]]/DirectoryEntries/File.svelte index 8251331..cdfd4d0 100644 --- a/src/routes/(main)/directory/[[id]]/DirectoryEntries/File.svelte +++ b/src/routes/(main)/directory/[[id]]/DirectoryEntries/File.svelte @@ -34,7 +34,7 @@ }; $effect(() => { - if ($info?.dataKey) { + if ($info) { requestFileThumbnailDownload($info.id, $info.dataKey) .then((thumbnailUrl) => { thumbnail = thumbnailUrl ?? undefined; From 328baba3951e1621e681a517a4429e78f807501c Mon Sep 17 00:00:00 2001 From: static Date: Sun, 2 Nov 2025 02:57:18 +0900 Subject: [PATCH 03/23] =?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=20=EB=B2=84?= =?UTF-8?q?=EC=A0=84=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 8 +- docker-compose.yaml | 8 +- package.json | 64 +- pnpm-lock.yaml | 1790 +++++++++++++++++++++++-------------------- 4 files changed, 994 insertions(+), 876 deletions(-) diff --git a/Dockerfile b/Dockerfile index f809fbc..676c0ee 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,11 +2,7 @@ FROM node:22-alpine AS base WORKDIR /app -RUN apk add --no-cache bash curl && \ - curl -o /usr/local/bin/wait-for-it https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh && \ - chmod +x /usr/local/bin/wait-for-it - -RUN npm install -g pnpm@9 +RUN npm install -g pnpm@10 COPY pnpm-lock.yaml . # Build Stage @@ -29,4 +25,4 @@ COPY --from=build /app/build ./build EXPOSE 3000 ENV BODY_SIZE_LIMIT=Infinity -CMD ["bash", "-c", "wait-for-it ${DATABASE_HOST:-localhost}:${DATABASE_PORT:-5432} -- node ./build/index.js"] +CMD ["node", "./build/index.js"] diff --git a/docker-compose.yaml b/docker-compose.yaml index 57a423f..2015066 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -3,7 +3,8 @@ services: build: . restart: unless-stopped depends_on: - - database + database: + condition: service_healthy user: ${CONTAINER_UID:-0}:${CONTAINER_GID:-0} volumes: - ./data/library:/app/data/library @@ -35,3 +36,8 @@ services: environment: - POSTGRES_USER=arkvault - POSTGRES_PASSWORD=${DATABASE_PASSWORD:?} + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER}"] + interval: 5s + timeout: 5s + retries: 5 diff --git a/package.json b/package.json index e91eafa..5c7d0f3 100644 --- a/package.json +++ b/package.json @@ -16,53 +16,53 @@ "db:migrate": "kysely migrate" }, "devDependencies": { - "@eslint/compat": "^1.3.1", - "@iconify-json/material-symbols": "^1.2.29", - "@sveltejs/adapter-node": "^5.2.13", - "@sveltejs/kit": "^2.22.5", - "@sveltejs/vite-plugin-svelte": "^4.0.4", + "@eslint/compat": "^1.4.1", + "@iconify-json/material-symbols": "^1.2.44", + "@sveltejs/adapter-node": "^5.4.0", + "@sveltejs/kit": "^2.48.4", + "@sveltejs/vite-plugin-svelte": "^6.2.1", "@types/file-saver": "^2.0.7", - "@types/ms": "^0.7.34", + "@types/ms": "^2.1.0", "@types/node-schedule": "^2.1.8", - "@types/pg": "^8.15.4", + "@types/pg": "^8.15.6", "autoprefixer": "^10.4.21", - "axios": "^1.10.0", - "dexie": "^4.0.11", - "eslint": "^9.30.1", - "eslint-config-prettier": "^10.1.5", - "eslint-plugin-svelte": "^3.10.1", - "eslint-plugin-tailwindcss": "^3.18.0", - "exifreader": "^4.31.1", + "axios": "^1.13.1", + "dexie": "^4.2.1", + "eslint": "^9.39.0", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-svelte": "^3.13.0", + "eslint-plugin-tailwindcss": "^3.18.2", + "exifreader": "^4.32.0", "file-saver": "^2.0.5", - "globals": "^16.3.0", + "globals": "^16.5.0", "heic2any": "^0.0.4", - "kysely-ctl": "^0.13.1", - "lru-cache": "^11.1.0", - "mime": "^4.0.7", - "p-limit": "^6.2.0", + "kysely-ctl": "^0.19.0", + "lru-cache": "^11.2.2", + "mime": "^4.1.0", + "p-limit": "^7.2.0", "prettier": "^3.6.2", "prettier-plugin-svelte": "^3.4.0", - "prettier-plugin-tailwindcss": "^0.6.14", - "svelte": "^5.35.6", - "svelte-check": "^4.2.2", - "tailwindcss": "^3.4.17", - "typescript": "^5.8.3", - "typescript-eslint": "^8.36.0", - "unplugin-icons": "^22.1.0", - "vite": "^5.4.19" + "prettier-plugin-tailwindcss": "^0.7.1", + "svelte": "^5.43.2", + "svelte-check": "^4.3.3", + "tailwindcss": "^3.4.18", + "typescript": "^5.9.3", + "typescript-eslint": "^8.46.2", + "unplugin-icons": "^22.5.0", + "vite": "^7.1.12" }, "dependencies": { - "@fastify/busboy": "^3.1.1", - "argon2": "^0.43.0", - "kysely": "^0.28.2", + "@fastify/busboy": "^3.2.0", + "argon2": "^0.44.0", + "kysely": "^0.28.8", "ms": "^2.1.3", "node-schedule": "^2.1.1", "pg": "^8.16.3", - "uuid": "^11.1.0", + "uuid": "^13.0.0", "zod": "^3.25.76" }, "engines": { "node": "^22.0.0", - "pnpm": "^9.0.0" + "pnpm": "^10.0.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d0d9407..da0ae97 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,14 +9,14 @@ importers: .: dependencies: '@fastify/busboy': - specifier: ^3.1.1 - version: 3.1.1 + specifier: ^3.2.0 + version: 3.2.0 argon2: - specifier: ^0.43.0 - version: 0.43.0 + specifier: ^0.44.0 + version: 0.44.0 kysely: - specifier: ^0.28.2 - version: 0.28.2 + specifier: ^0.28.8 + version: 0.28.8 ms: specifier: ^2.1.3 version: 2.1.3 @@ -27,114 +27,114 @@ importers: specifier: ^8.16.3 version: 8.16.3 uuid: - specifier: ^11.1.0 - version: 11.1.0 + specifier: ^13.0.0 + version: 13.0.0 zod: specifier: ^3.25.76 version: 3.25.76 devDependencies: '@eslint/compat': - specifier: ^1.3.1 - version: 1.3.1(eslint@9.30.1(jiti@2.4.2)) + specifier: ^1.4.1 + version: 1.4.1(eslint@9.39.0(jiti@1.21.7)) '@iconify-json/material-symbols': - specifier: ^1.2.29 - version: 1.2.29 + specifier: ^1.2.44 + version: 1.2.44 '@sveltejs/adapter-node': - specifier: ^5.2.13 - version: 5.2.13(@sveltejs/kit@2.22.5(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.35.6)(vite@5.4.19(@types/node@24.0.13)))(svelte@5.35.6)(vite@5.4.19(@types/node@24.0.13))) + specifier: ^5.4.0 + version: 5.4.0(@sveltejs/kit@2.48.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0))) '@sveltejs/kit': - specifier: ^2.22.5 - version: 2.22.5(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.35.6)(vite@5.4.19(@types/node@24.0.13)))(svelte@5.35.6)(vite@5.4.19(@types/node@24.0.13)) + specifier: ^2.48.4 + version: 2.48.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)) '@sveltejs/vite-plugin-svelte': - specifier: ^4.0.4 - version: 4.0.4(svelte@5.35.6)(vite@5.4.19(@types/node@24.0.13)) + specifier: ^6.2.1 + version: 6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)) '@types/file-saver': specifier: ^2.0.7 version: 2.0.7 '@types/ms': - specifier: ^0.7.34 - version: 0.7.34 + specifier: ^2.1.0 + version: 2.1.0 '@types/node-schedule': specifier: ^2.1.8 version: 2.1.8 '@types/pg': - specifier: ^8.15.4 - version: 8.15.4 + specifier: ^8.15.6 + version: 8.15.6 autoprefixer: specifier: ^10.4.21 version: 10.4.21(postcss@8.5.6) axios: - specifier: ^1.10.0 - version: 1.10.0 + specifier: ^1.13.1 + version: 1.13.1 dexie: - specifier: ^4.0.11 - version: 4.0.11 + specifier: ^4.2.1 + version: 4.2.1 eslint: - specifier: ^9.30.1 - version: 9.30.1(jiti@2.4.2) + specifier: ^9.39.0 + version: 9.39.0(jiti@1.21.7) eslint-config-prettier: - specifier: ^10.1.5 - version: 10.1.5(eslint@9.30.1(jiti@2.4.2)) + specifier: ^10.1.8 + version: 10.1.8(eslint@9.39.0(jiti@1.21.7)) eslint-plugin-svelte: - specifier: ^3.10.1 - version: 3.10.1(eslint@9.30.1(jiti@2.4.2))(svelte@5.35.6) + specifier: ^3.13.0 + version: 3.13.0(eslint@9.39.0(jiti@1.21.7))(svelte@5.43.2) eslint-plugin-tailwindcss: - specifier: ^3.18.0 - version: 3.18.0(tailwindcss@3.4.17) + specifier: ^3.18.2 + version: 3.18.2(tailwindcss@3.4.18(yaml@2.8.0)) exifreader: - specifier: ^4.31.1 - version: 4.31.1 + specifier: ^4.32.0 + version: 4.32.0 file-saver: specifier: ^2.0.5 version: 2.0.5 globals: - specifier: ^16.3.0 - version: 16.3.0 + specifier: ^16.5.0 + version: 16.5.0 heic2any: specifier: ^0.0.4 version: 0.0.4 kysely-ctl: - specifier: ^0.13.1 - version: 0.13.1(kysely@0.28.2) + specifier: ^0.19.0 + version: 0.19.0(kysely@0.28.8)(typescript@5.9.3) lru-cache: - specifier: ^11.1.0 - version: 11.1.0 + specifier: ^11.2.2 + version: 11.2.2 mime: - specifier: ^4.0.7 - version: 4.0.7 + specifier: ^4.1.0 + version: 4.1.0 p-limit: - specifier: ^6.2.0 - version: 6.2.0 + specifier: ^7.2.0 + version: 7.2.0 prettier: specifier: ^3.6.2 version: 3.6.2 prettier-plugin-svelte: specifier: ^3.4.0 - version: 3.4.0(prettier@3.6.2)(svelte@5.35.6) + version: 3.4.0(prettier@3.6.2)(svelte@5.43.2) prettier-plugin-tailwindcss: - specifier: ^0.6.14 - version: 0.6.14(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.35.6))(prettier@3.6.2) + specifier: ^0.7.1 + version: 0.7.1(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.43.2))(prettier@3.6.2) svelte: - specifier: ^5.35.6 - version: 5.35.6 + specifier: ^5.43.2 + version: 5.43.2 svelte-check: - specifier: ^4.2.2 - version: 4.2.2(picomatch@4.0.2)(svelte@5.35.6)(typescript@5.8.3) + specifier: ^4.3.3 + version: 4.3.3(picomatch@4.0.3)(svelte@5.43.2)(typescript@5.9.3) tailwindcss: - specifier: ^3.4.17 - version: 3.4.17 + specifier: ^3.4.18 + version: 3.4.18(yaml@2.8.0) typescript: - specifier: ^5.8.3 - version: 5.8.3 + specifier: ^5.9.3 + version: 5.9.3 typescript-eslint: - specifier: ^8.36.0 - version: 8.36.0(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3) + specifier: ^8.46.2 + version: 8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3) unplugin-icons: - specifier: ^22.1.0 - version: 22.1.0(svelte@5.35.6) + specifier: ^22.5.0 + version: 22.5.0(svelte@5.43.2) vite: - specifier: ^5.4.19 - version: 5.4.19(@types/node@24.0.13) + specifier: ^7.1.12 + version: 7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0) packages: @@ -142,166 +142,183 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - '@antfu/install-pkg@1.1.0': resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} - '@antfu/utils@8.1.1': - resolution: {integrity: sha512-Mex9nXf9vR6AhcXmMrlz/HVgYYZpVGJ6YlPgwl7UnaFpnshXs6EK/oa5Gpf3CzENMjkvEx2tQtntGnb7UtSTOQ==} + '@antfu/utils@9.3.0': + resolution: {integrity: sha512-9hFT4RauhcUzqOE4f1+frMKLZrgNog5b06I7VmZQV1BkvwvqrbC8EBZf3L1eEL2AKb6rNKjER0sEvJiSP1FXEA==} - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} + '@epic-web/invariant@1.0.0': + resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} + + '@esbuild/aix-ppc64@0.25.11': + resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==} + engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} + '@esbuild/android-arm64@0.25.11': + resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==} + engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} + '@esbuild/android-arm@0.25.11': + resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==} + engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} + '@esbuild/android-x64@0.25.11': + resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==} + engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} + '@esbuild/darwin-arm64@0.25.11': + resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==} + engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} + '@esbuild/darwin-x64@0.25.11': + resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==} + engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} + '@esbuild/freebsd-arm64@0.25.11': + resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==} + engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} + '@esbuild/freebsd-x64@0.25.11': + resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==} + engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} + '@esbuild/linux-arm64@0.25.11': + resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==} + engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} + '@esbuild/linux-arm@0.25.11': + resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==} + engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} + '@esbuild/linux-ia32@0.25.11': + resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==} + engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} + '@esbuild/linux-loong64@0.25.11': + resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==} + engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} + '@esbuild/linux-mips64el@0.25.11': + resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==} + engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} + '@esbuild/linux-ppc64@0.25.11': + resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==} + engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} + '@esbuild/linux-riscv64@0.25.11': + resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==} + engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} + '@esbuild/linux-s390x@0.25.11': + resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==} + engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} + '@esbuild/linux-x64@0.25.11': + resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==} + engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} + '@esbuild/netbsd-arm64@0.25.11': + resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.11': + resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==} + engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} + '@esbuild/openbsd-arm64@0.25.11': + resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.11': + resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==} + engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} + '@esbuild/openharmony-arm64@0.25.11': + resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.11': + resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==} + engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} + '@esbuild/win32-arm64@0.25.11': + resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==} + engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} + '@esbuild/win32-ia32@0.25.11': + resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==} + engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} + '@esbuild/win32-x64@0.25.11': + resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==} + engines: {node: '>=18'} cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.7.0': - resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.12.1': - resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/compat@1.3.1': - resolution: {integrity: sha512-k8MHony59I5EPic6EQTCNOuPoVBnoYXkP+20xvwFjN7t0qI3ImyvyBgg+hIVPwC8JaxVjjUZld+cLfBLFDLucg==} + '@eslint/compat@1.4.1': + resolution: {integrity: sha512-cfO82V9zxxGBxcQDr1lfaYB7wykTa0b00mGa36FrJl7iTFd0Z2cHfEYuxcBRP/iNijCsWsEkA+jzT8hGYmv33w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.40 || 9 @@ -309,86 +326,81 @@ packages: eslint: optional: true - '@eslint/config-array@0.21.0': - resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-helpers@0.3.0': - resolution: {integrity: sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==} + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.14.0': - resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - - '@eslint/core@0.15.1': - resolution: {integrity: sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==} + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/eslintrc@3.3.1': resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.30.1': - resolution: {integrity: sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg==} + '@eslint/js@9.39.0': + resolution: {integrity: sha512-BIhe0sW91JGPiaF1mOuPy5v8NflqfjIcDNpC+LbW9f609WVRX1rArrhi6Z2ymvrAry9jw+5POTj4t2t62o8Bmw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/object-schema@2.1.6': - resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.3.3': - resolution: {integrity: sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==} + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@fastify/busboy@3.1.1': - resolution: {integrity: sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==} + '@fastify/busboy@3.2.0': + resolution: {integrity: sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==} '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} - '@humanfs/node@0.16.6': - resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} - '@humanwhocodes/retry@0.3.1': - resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} - engines: {node: '>=18.18'} - '@humanwhocodes/retry@0.4.3': resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@iconify-json/material-symbols@1.2.29': - resolution: {integrity: sha512-UUSrsl0gHF0GjAB9eZOpXrj7/v55ayMzo3QnMwUqP/FSfSkITKLR7CsBmUIFS8eEj8eRTfBNWA1yiIJR6UOdWg==} + '@iconify-json/material-symbols@1.2.44': + resolution: {integrity: sha512-NAJjhswaK9FxBeIzFFsNygws7wHtmAkBWhF4YEwn1NZIMbA+LNITqhUiq6sP5mOdKQqnoritFTlQaZ47a5BgBg==} '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} - '@iconify/utils@2.3.0': - resolution: {integrity: sha512-GmQ78prtwYW6EtzXRU1rY+KwOKfz32PD7iJh6Iyqw68GiKuoZ2A6pRtzWONz5VQJbp50mEjXh/7NkumtrAgRKA==} + '@iconify/utils@3.0.2': + resolution: {integrity: sha512-EfJS0rLfVuRuJRn4psJHtK2A9TqVnkxPpHY6lYHiB9+8eSuudsxbwMiavocG45ujOo6FJ+CIRlRnlOGinzkaGQ==} '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@jridgewell/gen-mapping@0.3.12': - resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.4': - resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/trace-mapping@0.3.29': - resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -413,8 +425,8 @@ packages: '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} - '@rollup/plugin-commonjs@28.0.6': - resolution: {integrity: sha512-XSQB1K7FUU5QP+3lOQmVCE3I0FcbbNvmNT4VJSj93iUjayaARrTQeoRdiYQoftAJBLrR9t2agwAd3ekaTgHNlw==} + '@rollup/plugin-commonjs@28.0.9': + resolution: {integrity: sha512-PIR4/OHZ79romx0BVVll/PkwWpJ7e5lsqFa3gFfcrFPWwLXLV39JVUzQV9RKjWerE7B845Hqjj9VYlQeieZ2dA==} engines: {node: '>=16.0.0 || 14 >= 14.17'} peerDependencies: rollup: ^2.68.0||^3.0.0||^4.0.0 @@ -431,8 +443,8 @@ packages: rollup: optional: true - '@rollup/plugin-node-resolve@16.0.1': - resolution: {integrity: sha512-tk5YCxJWIG81umIvNkSod2qK5KyQW19qcBF/B78n1bjtOON6gzKoVeSzAE8yHCZEDmqkHKkxplExA8KzdJLJpA==} + '@rollup/plugin-node-resolve@16.0.3': + resolution: {integrity: sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^2.78.0||^3.0.0||^4.0.0 @@ -440,8 +452,8 @@ packages: rollup: optional: true - '@rollup/pluginutils@5.2.0': - resolution: {integrity: sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==} + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} engines: {node: '>=14.0.0'} peerDependencies: rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 @@ -449,139 +461,156 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.44.2': - resolution: {integrity: sha512-g0dF8P1e2QYPOj1gu7s/3LVP6kze9A7m6x0BZ9iTdXK8N5c2V7cpBKHV3/9A4Zd8xxavdhK0t4PnqjkqVmUc9Q==} + '@rollup/rollup-android-arm-eabi@4.52.5': + resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.44.2': - resolution: {integrity: sha512-Yt5MKrOosSbSaAK5Y4J+vSiID57sOvpBNBR6K7xAaQvk3MkcNVV0f9fE20T+41WYN8hDn6SGFlFrKudtx4EoxA==} + '@rollup/rollup-android-arm64@4.52.5': + resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.44.2': - resolution: {integrity: sha512-EsnFot9ZieM35YNA26nhbLTJBHD0jTwWpPwmRVDzjylQT6gkar+zenfb8mHxWpRrbn+WytRRjE0WKsfaxBkVUA==} + '@rollup/rollup-darwin-arm64@4.52.5': + resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.44.2': - resolution: {integrity: sha512-dv/t1t1RkCvJdWWxQ2lWOO+b7cMsVw5YFaS04oHpZRWehI1h0fV1gF4wgGCTyQHHjJDfbNpwOi6PXEafRBBezw==} + '@rollup/rollup-darwin-x64@4.52.5': + resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.44.2': - resolution: {integrity: sha512-W4tt4BLorKND4qeHElxDoim0+BsprFTwb+vriVQnFFtT/P6v/xO5I99xvYnVzKWrK6j7Hb0yp3x7V5LUbaeOMg==} + '@rollup/rollup-freebsd-arm64@4.52.5': + resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.44.2': - resolution: {integrity: sha512-tdT1PHopokkuBVyHjvYehnIe20fxibxFCEhQP/96MDSOcyjM/shlTkZZLOufV3qO6/FQOSiJTBebhVc12JyPTA==} + '@rollup/rollup-freebsd-x64@4.52.5': + resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.44.2': - resolution: {integrity: sha512-+xmiDGGaSfIIOXMzkhJ++Oa0Gwvl9oXUeIiwarsdRXSe27HUIvjbSIpPxvnNsRebsNdUo7uAiQVgBD1hVriwSQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': + resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.44.2': - resolution: {integrity: sha512-bDHvhzOfORk3wt8yxIra8N4k/N0MnKInCW5OGZaeDYa/hMrdPaJzo7CSkjKZqX4JFUWjUGm88lI6QJLCM7lDrA==} + '@rollup/rollup-linux-arm-musleabihf@4.52.5': + resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.44.2': - resolution: {integrity: sha512-NMsDEsDiYghTbeZWEGnNi4F0hSbGnsuOG+VnNvxkKg0IGDvFh7UVpM/14mnMwxRxUf9AdAVJgHPvKXf6FpMB7A==} + '@rollup/rollup-linux-arm64-gnu@4.52.5': + resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.44.2': - resolution: {integrity: sha512-lb5bxXnxXglVq+7imxykIp5xMq+idehfl+wOgiiix0191av84OqbjUED+PRC5OA8eFJYj5xAGcpAZ0pF2MnW+A==} + '@rollup/rollup-linux-arm64-musl@4.52.5': + resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.44.2': - resolution: {integrity: sha512-Yl5Rdpf9pIc4GW1PmkUGHdMtbx0fBLE1//SxDmuf3X0dUC57+zMepow2LK0V21661cjXdTn8hO2tXDdAWAqE5g==} + '@rollup/rollup-linux-loong64-gnu@4.52.5': + resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.44.2': - resolution: {integrity: sha512-03vUDH+w55s680YYryyr78jsO1RWU9ocRMaeV2vMniJJW/6HhoTBwyyiiTPVHNWLnhsnwcQ0oH3S9JSBEKuyqw==} + '@rollup/rollup-linux-ppc64-gnu@4.52.5': + resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.44.2': - resolution: {integrity: sha512-iYtAqBg5eEMG4dEfVlkqo05xMOk6y/JXIToRca2bAWuqjrJYJlx/I7+Z+4hSrsWU8GdJDFPL4ktV3dy4yBSrzg==} + '@rollup/rollup-linux-riscv64-gnu@4.52.5': + resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.44.2': - resolution: {integrity: sha512-e6vEbgaaqz2yEHqtkPXa28fFuBGmUJ0N2dOJK8YUfijejInt9gfCSA7YDdJ4nYlv67JfP3+PSWFX4IVw/xRIPg==} + '@rollup/rollup-linux-riscv64-musl@4.52.5': + resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.44.2': - resolution: {integrity: sha512-evFOtkmVdY3udE+0QKrV5wBx7bKI0iHz5yEVx5WqDJkxp9YQefy4Mpx3RajIVcM6o7jxTvVd/qpC1IXUhGc1Mw==} + '@rollup/rollup-linux-s390x-gnu@4.52.5': + resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.44.2': - resolution: {integrity: sha512-/bXb0bEsWMyEkIsUL2Yt5nFB5naLAwyOWMEviQfQY1x3l5WsLKgvZf66TM7UTfED6erckUVUJQ/jJ1FSpm3pRQ==} + '@rollup/rollup-linux-x64-gnu@4.52.5': + resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.44.2': - resolution: {integrity: sha512-3D3OB1vSSBXmkGEZR27uiMRNiwN08/RVAcBKwhUYPaiZ8bcvdeEwWPvbnXvvXHY+A/7xluzcN+kaiOFNiOZwWg==} + '@rollup/rollup-linux-x64-musl@4.52.5': + resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.44.2': - resolution: {integrity: sha512-VfU0fsMK+rwdK8mwODqYeM2hDrF2WiHaSmCBrS7gColkQft95/8tphyzv2EupVxn3iE0FI78wzffoULH1G+dkw==} + '@rollup/rollup-openharmony-arm64@4.52.5': + resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.52.5': + resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.44.2': - resolution: {integrity: sha512-+qMUrkbUurpE6DVRjiJCNGZBGo9xM4Y0FXU5cjgudWqIBWbcLkjE3XprJUsOFgC6xjBClwVa9k6O3A7K3vxb5Q==} + '@rollup/rollup-win32-ia32-msvc@4.52.5': + resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.44.2': - resolution: {integrity: sha512-3+QZROYfJ25PDcxFF66UEk8jGWigHJeecZILvkPkyQN7oc5BvFo4YEXFkOs154j3FTMp9mn9Ky8RCOwastduEA==} + '@rollup/rollup-win32-x64-gnu@4.52.5': + resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==} cpu: [x64] os: [win32] - '@sveltejs/acorn-typescript@1.0.5': - resolution: {integrity: sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ==} + '@rollup/rollup-win32-x64-msvc@4.52.5': + resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==} + cpu: [x64] + os: [win32] + + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + + '@sveltejs/acorn-typescript@1.0.6': + resolution: {integrity: sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ==} peerDependencies: acorn: ^8.9.0 - '@sveltejs/adapter-node@5.2.13': - resolution: {integrity: sha512-yS2TVFmIrxjGhYaV5/iIUrJ3mJl6zjaYn0lBD70vTLnYvJeqf3cjvLXeXCUCuYinhSBoyF4DpfGla49BnIy7sQ==} + '@sveltejs/adapter-node@5.4.0': + resolution: {integrity: sha512-NMsrwGVPEn+J73zH83Uhss/hYYZN6zT3u31R3IHAn3MiKC3h8fjmIAhLfTSOeNHr5wPYfjjMg8E+1gyFgyrEcQ==} peerDependencies: '@sveltejs/kit': ^2.4.0 - '@sveltejs/kit@2.22.5': - resolution: {integrity: sha512-l5i+LcDaoymD2mg5ziptnHmzzF79+c9twJiDoLWAPKq7afMEe4mvGesJ+LVtm33A92mLzd2KUHgtGSqTrvfkvg==} + '@sveltejs/kit@2.48.4': + resolution: {integrity: sha512-TGFX1pZUt9qqY20Cv5NyYvy0iLWHf2jXi8s+eCGsig7jQMdwZWKUFMR6TbvFNhfDSUpc1sH/Y5EHv20g3HHA3g==} 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 svelte: ^4.0.0 || ^5.0.0-next.0 vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true - '@sveltejs/vite-plugin-svelte-inspector@3.0.1': - resolution: {integrity: sha512-2CKypmj1sM4GE7HjllT7UKmo4Q6L5xFRd7VMGEWhYnZ+wc6AUVU01IBd7yUi6WnFndEwWoMNOd6e8UjoN0nbvQ==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22} + '@sveltejs/vite-plugin-svelte-inspector@5.0.1': + resolution: {integrity: sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA==} + engines: {node: ^20.19 || ^22.12 || >=24} peerDependencies: - '@sveltejs/vite-plugin-svelte': ^4.0.0-next.0||^4.0.0 - svelte: ^5.0.0-next.96 || ^5.0.0 - vite: ^5.0.0 + '@sveltejs/vite-plugin-svelte': ^6.0.0-next.0 + svelte: ^5.0.0 + vite: ^6.3.0 || ^7.0.0 - '@sveltejs/vite-plugin-svelte@4.0.4': - resolution: {integrity: sha512-0ba1RQ/PHen5FGpdSrW7Y3fAMQjrXantECALeOiOdBdzR5+5vPP6HVZRLmZaQL+W8m++o+haIAKq5qT+MiZ7VA==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22} + '@sveltejs/vite-plugin-svelte@6.2.1': + resolution: {integrity: sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==} + engines: {node: ^20.19 || ^22.12 || >=24} peerDependencies: - svelte: ^5.0.0-next.96 || ^5.0.0 - vite: ^5.0.0 + svelte: ^5.0.0 + vite: ^6.3.0 || ^7.0.0 '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} @@ -595,78 +624,78 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/ms@0.7.34': - resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} '@types/node-schedule@2.1.8': resolution: {integrity: sha512-k00g6Yj/oUg/CDC+MeLHUzu0+OFxWbIqrFfDiLi6OPKxTujvpv29mHGM8GtKr7B+9Vv92FcK/8mRqi1DK5f3hA==} - '@types/node@24.0.13': - resolution: {integrity: sha512-Qm9OYVOFHFYg3wJoTSrz80hoec5Lia/dPp84do3X7dZvLikQvM1YpmvTBEdIr/e+U8HTkFjLHLnl78K/qjf+jQ==} + '@types/node@24.9.2': + resolution: {integrity: sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==} - '@types/pg@8.15.4': - resolution: {integrity: sha512-I6UNVBAoYbvuWkkU3oosC8yxqH21f4/Jc4DK71JLG3dT2mdlGe1z+ep/LQGXaKaOgcvUrsQoPRqfgtMcvZiJhg==} + '@types/pg@8.15.6': + resolution: {integrity: sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==} '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} - '@typescript-eslint/eslint-plugin@8.36.0': - resolution: {integrity: sha512-lZNihHUVB6ZZiPBNgOQGSxUASI7UJWhT8nHyUGCnaQ28XFCw98IfrMCG3rUl1uwUWoAvodJQby2KTs79UTcrAg==} + '@typescript-eslint/eslint-plugin@8.46.2': + resolution: {integrity: sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.36.0 + '@typescript-eslint/parser': ^8.46.2 eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.36.0': - resolution: {integrity: sha512-FuYgkHwZLuPbZjQHzJXrtXreJdFMKl16BFYyRrLxDhWr6Qr7Kbcu2s1Yhu8tsiMXw1S0W1pjfFfYEt+R604s+Q==} + '@typescript-eslint/parser@8.46.2': + resolution: {integrity: sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.36.0': - resolution: {integrity: sha512-JAhQFIABkWccQYeLMrHadu/fhpzmSQ1F1KXkpzqiVxA/iYI6UnRt2trqXHt1sYEcw1mxLnB9rKMsOxXPxowN/g==} + '@typescript-eslint/project-service@8.46.2': + resolution: {integrity: sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.36.0': - resolution: {integrity: sha512-wCnapIKnDkN62fYtTGv2+RY8FlnBYA3tNm0fm91kc2BjPhV2vIjwwozJ7LToaLAyb1ca8BxrS7vT+Pvvf7RvqA==} + '@typescript-eslint/scope-manager@8.46.2': + resolution: {integrity: sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.36.0': - resolution: {integrity: sha512-Nhh3TIEgN18mNbdXpd5Q8mSCBnrZQeY9V7Ca3dqYvNDStNIGRmJA6dmrIPMJ0kow3C7gcQbpsG2rPzy1Ks/AnA==} + '@typescript-eslint/tsconfig-utils@8.46.2': + resolution: {integrity: sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.36.0': - resolution: {integrity: sha512-5aaGYG8cVDd6cxfk/ynpYzxBRZJk7w/ymto6uiyUFtdCozQIsQWh7M28/6r57Fwkbweng8qAzoMCPwSJfWlmsg==} + '@typescript-eslint/type-utils@8.46.2': + resolution: {integrity: sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.36.0': - resolution: {integrity: sha512-xGms6l5cTJKQPZOKM75Dl9yBfNdGeLRsIyufewnxT4vZTrjC0ImQT4fj8QmtJK84F58uSh5HVBSANwcfiXxABQ==} + '@typescript-eslint/types@8.46.2': + resolution: {integrity: sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.36.0': - resolution: {integrity: sha512-JaS8bDVrfVJX4av0jLpe4ye0BpAaUW7+tnS4Y4ETa3q7NoZgzYbN9zDQTJ8kPb5fQ4n0hliAt9tA4Pfs2zA2Hg==} + '@typescript-eslint/typescript-estree@8.46.2': + resolution: {integrity: sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.36.0': - resolution: {integrity: sha512-VOqmHu42aEMT+P2qYjylw6zP/3E/HvptRwdn/PZxyV27KhZg2IOszXod4NcXisWzPAGSS4trE/g4moNj6XmH2g==} + '@typescript-eslint/utils@8.46.2': + resolution: {integrity: sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.36.0': - resolution: {integrity: sha512-vZrhV2lRPWDuGoxcmrzRZyxAggPL+qp3WzUrlZD+slFueDiYHxeBa34dUXPuC0RmGKzl4lS5kFJYvKCq9cnNDA==} + '@typescript-eslint/visitor-keys@8.46.2': + resolution: {integrity: sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@xmldom/xmldom@0.9.8': @@ -690,16 +719,16 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.1.0: - resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} any-promise@1.3.0: @@ -712,8 +741,8 @@ packages: arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - argon2@0.43.0: - resolution: {integrity: sha512-u/HKLcbWShVDhkfwI4hWyiUf3qyX8QhTfaIv2cWE18uqhXCmR5hb6Ed7oqYi2KCQegeAnRhiFzbjzm7i5yl1GA==} + argon2@0.44.0: + resolution: {integrity: sha512-zHPGN3S55sihSQo0dBbK0A5qpi2R31z7HZDZnry3ifOyj8bZZnpZND2gpmhnRGO1V/d555RwBqIK5W4Mrmv3ig==} engines: {node: '>=16.17.0'} argparse@2.0.1: @@ -733,8 +762,8 @@ packages: peerDependencies: postcss: ^8.1.0 - axios@1.10.0: - resolution: {integrity: sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==} + axios@1.13.1: + resolution: {integrity: sha512-hU4EGxxt+j7TQijx1oYdAjw4xuIp1wRQSsbMFwSthCWeBQur1eF+qJ5iQ5sN3Tw8YRzQNKb8jszgBdMDVqwJcw==} axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} @@ -743,6 +772,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + baseline-browser-mapping@2.8.23: + resolution: {integrity: sha512-616V5YX4bepJFzNyOfce5Fa8fDJMfoxzOIzDCZwaGL8MKVpFrXqfNUoIpRn9YMI5pXf/VKgzjB4htFMsFKKdiQ==} + hasBin: true + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -757,13 +790,13 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.25.1: - resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} + browserslist@4.27.0: + resolution: {integrity: sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - c12@3.0.4: - resolution: {integrity: sha512-t5FaZTYbbCtvxuZq9xxIruYydrAGsJ+8UdP0pZzMiK2xl/gNiSOy0OxhLzHUEEb0m1QXYqfzfvyIFEmz/g9lqg==} + c12@3.3.1: + resolution: {integrity: sha512-LcWQ01LT9tkoUINHgpIOv3mMs+Abv7oVCrtpMRi1PaapVEpWoMga5WuT7/DqFTu7URP9ftbOmimNw1KNIGh9DQ==} peerDependencies: magicast: ^0.3.5 peerDependenciesMeta: @@ -782,8 +815,8 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - caniuse-lite@1.0.30001727: - resolution: {integrity: sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==} + caniuse-lite@1.0.30001752: + resolution: {integrity: sha512-vKUk7beoukxE47P5gcVNKkDRzXdVofotshHwfR9vmpeFKxmI5PBpgOMC18LUJUA/DvJ70Y7RveasIBraqsyO/g==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -843,6 +876,11 @@ packages: resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} engines: {node: '>=12.0.0'} + cross-env@10.1.0: + resolution: {integrity: sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==} + engines: {node: '>=20'} + hasBin: true + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -852,8 +890,8 @@ packages: engines: {node: '>=4'} hasBin: true - debug@4.4.1: - resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -878,11 +916,11 @@ packages: destr@2.0.5: resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} - devalue@5.1.1: - resolution: {integrity: sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==} + devalue@5.4.2: + resolution: {integrity: sha512-MwPZTKEPK2k8Qgfmqrd48ZKVvzSQjgW0lXLxiIBA8dQjtf/6mw6pggHNLcyDKyf+fI6eXxlQwPsfaCMTU5U+Bw==} - dexie@4.0.11: - resolution: {integrity: sha512-SOKO002EqlvBYYKQSew3iymBoN2EQ4BDw/3yprjh7kAfFzjBYkaMNa/pZvcA7HSWlcKSQb9XhPe3wKyQ0x4A8A==} + dexie@4.2.1: + resolution: {integrity: sha512-Ckej0NS6jxQ4Po3OrSQBFddayRhTCic2DoCAG5zacOfOVB9P2Q5Xc5uL/nVa7ZVs+HdMnvUPzLFCB/JwpB6Csg==} didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} @@ -890,8 +928,8 @@ packages: dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} - dotenv@16.6.1: - resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + dotenv@17.2.3: + resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} engines: {node: '>=12'} dunder-proto@1.0.1: @@ -901,8 +939,8 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.5.182: - resolution: {integrity: sha512-Lv65Btwv9W4J9pyODI6EWpdnhfvrve/us5h1WspW8B2Fb0366REPtY3hX7ounk1CkV/TBjWCEvCBBbYbmV0qCA==} + electron-to-chromium@1.5.244: + resolution: {integrity: sha512-OszpBN7xZX4vWMPJwB9illkN/znA8M36GQqQxi6MNy9axWxhOfJyZZJtSLQCpEFLHP2xK33BiWx9aIuIEXVCcw==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -926,9 +964,9 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} + esbuild@0.25.11: + resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==} + engines: {node: '>=18'} hasBin: true escalade@3.2.0: @@ -939,14 +977,14 @@ packages: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} - eslint-config-prettier@10.1.5: - resolution: {integrity: sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==} + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} hasBin: true peerDependencies: eslint: '>=7.0.0' - eslint-plugin-svelte@3.10.1: - resolution: {integrity: sha512-csCh2x0ge/DugXC7dCANh46Igi7bjMZEy6rHZCdS13AoGVJSu7a90Kru3I8oMYLGEemPRE1hQXadxvRPVMAAXQ==} + eslint-plugin-svelte@3.13.0: + resolution: {integrity: sha512-2ohCCQJJTNbIpQCSDSTWj+FN0OVfPmSO03lmSNT7ytqMaWF6kpT86LdzDqtm4sh7TVPl/OEWJ/d7R87bXP2Vjg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.1 || ^9.0.0 @@ -955,8 +993,8 @@ packages: svelte: optional: true - eslint-plugin-tailwindcss@3.18.0: - resolution: {integrity: sha512-PQDU4ZMzFH0eb2DrfHPpbgo87Zgg2EXSMOj1NSfzdZm+aJzpuwGerfowMIaVehSREEa0idbf/eoNYAOHSJoDAQ==} + eslint-plugin-tailwindcss@3.18.2: + resolution: {integrity: sha512-QbkMLDC/OkkjFQ1iz/5jkMdHfiMu/uwujUHLAJK5iwNHD8RTxVTlsUezE0toTZ6VhybNBsk+gYGPDq2agfeRNA==} engines: {node: '>=18.12.0'} peerDependencies: tailwindcss: ^3.4.0 @@ -973,8 +1011,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.30.1: - resolution: {integrity: sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==} + eslint@9.39.0: + resolution: {integrity: sha512-iy2GE3MHrYTL5lrCtMZ0X1KLEKKUjmK0kzwcnefhR66txcEmXZD2YWgR5GNdcEwkNx3a0siYkSvl0vIC+Svjmg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -994,8 +1032,8 @@ packages: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} - esrap@2.1.0: - resolution: {integrity: sha512-yzmPNpl7TBbMRC5Lj2JlJZNPml0tzqoqP5B1JXycNUwtqma9AKCO0M2wHrdgsHcy1WRW7S9rJknAMtByg3usgA==} + esrap@2.1.2: + resolution: {integrity: sha512-DgvlIQeowRNyvLPWW4PT7Gu13WznY288Du086E751mwwbsgr29ytBiYeLzAGIo0qk3Ujob0SDk8TiSaM5WQzNg==} esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} @@ -1012,8 +1050,8 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - exifreader@4.31.1: - resolution: {integrity: sha512-rkSg/NejDN9D+8GuRWZ2Y4G8KSrj0hdaeMoew8d0J5cF5oS0p6DVar2PSQ0fP3assu6s1PYh6M1lhtS7Kigk6Q==} + exifreader@4.32.0: + resolution: {integrity: sha512-sj1PzjpaPwSE/2MeUqoAYcfc2u7AZOGSby0FzmAkB4jjeCXgDryxzVgMwV+tJKGIkGdWkkWiUWoLSJoPHJ6V5Q==} exsolve@1.0.7: resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} @@ -1034,8 +1072,9 @@ packages: fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - fdir@6.4.6: - resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -1064,8 +1103,8 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - follow-redirects@1.15.9: - resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -1077,8 +1116,8 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} - form-data@4.0.3: - resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==} + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} fraction.js@4.3.7: @@ -1124,8 +1163,8 @@ packages: resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} engines: {node: '>=18'} - globals@16.3.0: - resolution: {integrity: sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==} + globals@16.5.0: + resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} engines: {node: '>=18'} gopd@1.2.0: @@ -1213,8 +1252,8 @@ packages: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true - jiti@2.4.2: - resolution: {integrity: sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==} + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true js-yaml@4.1.0: @@ -1243,20 +1282,26 @@ packages: kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} - kysely-ctl@0.13.1: - resolution: {integrity: sha512-DhTgpt1ru3Y74rI8O3IRLJ5HV09vMSz4q2KEeqaUX0GYMB09zO1KsdwSsF+ZMa8bHeS4F4J3Vjfbc4ulJQ1r0A==} - engines: {node: '>=18'} + kysely-ctl@0.19.0: + resolution: {integrity: sha512-89hzOd1cy/H063jB2E9wYHq+uKYpaHv6Mb5RiNFpRZL6BYCah9ncsdl3x5b52eirxry4UyWSmGNN3sFv+gK+ig==} + engines: {node: '>=20'} hasBin: true peerDependencies: kysely: '>=0.18.1 <0.29.0' - kysely-postgres-js: ^2 + kysely-neon: ^2 + kysely-postgres-js: ^2 || ^3 + kysely-prisma-postgres: ^0.1 peerDependenciesMeta: + kysely-neon: + optional: true kysely-postgres-js: optional: true + kysely-prisma-postgres: + optional: true - kysely@0.28.2: - resolution: {integrity: sha512-4YAVLoF0Sf0UTqlhgQMFU9iQECdah7n+13ANkiuVfRvlK+uI0Etbgd7bVP36dKlG+NXWbhGua8vnGt+sdhvT7A==} - engines: {node: '>=18.0.0'} + kysely@0.28.8: + resolution: {integrity: sha512-QUOgl5ZrS9IRuhq5FvOKFSsD/3+IA6MLE81/bOOTRA/YQpKDza2sFdN5g6JCB9BOpqMJDGefLCQ9F12hRS13TA==} + engines: {node: '>=20.0.0'} levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} @@ -1273,8 +1318,8 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - local-pkg@1.1.1: - resolution: {integrity: sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==} + local-pkg@1.1.2: + resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==} engines: {node: '>=14'} locate-character@3.0.0: @@ -1293,16 +1338,16 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lru-cache@11.1.0: - resolution: {integrity: sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==} + lru-cache@11.2.2: + resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==} engines: {node: 20 || >=22} - luxon@3.7.1: - resolution: {integrity: sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==} + luxon@3.7.2: + resolution: {integrity: sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==} engines: {node: '>=12'} - magic-string@0.30.17: - resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} @@ -1324,8 +1369,8 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - mime@4.0.7: - resolution: {integrity: sha512-2OfDPL+e03E0LrXaGYOtTFIYhiuzep94NSsuhrNULq+stylcJedcHdzHtz0atMUuGwJfFYs0YL5xeC/Ca2x0eQ==} + mime@4.1.0: + resolution: {integrity: sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==} engines: {node: '>=16'} hasBin: true @@ -1340,8 +1385,8 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - mlly@1.7.4: - resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} + mlly@1.8.0: + resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} @@ -1365,19 +1410,19 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - node-addon-api@8.4.0: - resolution: {integrity: sha512-D9DI/gXHvVmjHS08SVch0Em8G5S1P+QWtU31appcKT/8wFSPRcdHadIFSAntdMMVM5zz+/DL+bL/gz3UDppqtg==} + node-addon-api@8.5.0: + resolution: {integrity: sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==} engines: {node: ^18 || ^20 || >= 21} - node-fetch-native@1.6.6: - resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==} + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} node-gyp-build@4.8.4: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} node-schedule@2.1.1: resolution: {integrity: sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==} @@ -1391,8 +1436,8 @@ packages: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} - nypm@0.6.0: - resolution: {integrity: sha512-mn8wBFV9G9+UFHIrq+pZ2r2zL4aPau/by3kJb3cM7+5tQHMt6HGQB8FDIeKFYp8o0D2pnH6nVsO88N4AmUxIWg==} + nypm@0.6.2: + resolution: {integrity: sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==} engines: {node: ^14.16.0 || >=16.10.0} hasBin: true @@ -1404,8 +1449,8 @@ packages: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} - ofetch@1.4.1: - resolution: {integrity: sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==} + ofetch@1.5.1: + resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} ohash@2.0.11: resolution: {integrity: sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==} @@ -1418,9 +1463,9 @@ packages: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-limit@6.2.0: - resolution: {integrity: sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==} - engines: {node: '>=18'} + p-limit@7.2.0: + resolution: {integrity: sha512-ATHLtwoTNDloHRFFxFJdHnG6n2WUeFjaR8XQMFdKIv0xkXjrER8/iG9iu265jOM95zXHAfv9oTkqhrfbIzosrQ==} + engines: {node: '>=20'} p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} @@ -1429,8 +1474,8 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - package-manager-detector@1.3.0: - resolution: {integrity: sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==} + package-manager-detector@1.5.0: + resolution: {integrity: sha512-uBj69dVlYe/+wxj8JOpr97XfsxH/eumMt6HqjNTmJDf/6NO9s+0uxeOneIz3AsPt2m6y9PqzDzd3ATcU17MNfw==} parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} @@ -1454,8 +1499,8 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - perfect-debounce@1.0.0: - resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + perfect-debounce@2.0.0: + resolution: {integrity: sha512-fkEH/OBiKrqqI/yIgjR92lMfs2K8105zt/VT6+7eTjNwisrsh47CeIED9z58zI7DfKdH3uHAn25ziRZn3kgAow==} pg-cloudflare@1.2.7: resolution: {integrity: sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==} @@ -1498,8 +1543,8 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - picomatch@4.0.2: - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} pify@2.3.0: @@ -1513,8 +1558,8 @@ packages: pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} - pkg-types@2.2.0: - resolution: {integrity: sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ==} + pkg-types@2.3.0: + resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} postcss-import@15.1.0: resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} @@ -1522,8 +1567,8 @@ packages: peerDependencies: postcss: ^8.0.0 - postcss-js@4.0.1: - resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + postcss-js@4.1.0: + resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} engines: {node: ^12 || ^14 || >= 16} peerDependencies: postcss: ^8.4.21 @@ -1540,16 +1585,22 @@ packages: ts-node: optional: true - postcss-load-config@4.0.2: - resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} - engines: {node: '>= 14'} + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} peerDependencies: + jiti: '>=1.21.0' postcss: '>=8.0.9' - ts-node: '>=9.0.0' + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: + jiti: + optional: true postcss: optional: true - ts-node: + tsx: + optional: true + yaml: optional: true postcss-nested@6.2.0: @@ -1611,9 +1662,9 @@ packages: prettier: ^3.0.0 svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 - prettier-plugin-tailwindcss@0.6.14: - resolution: {integrity: sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==} - engines: {node: '>=14.21.3'} + prettier-plugin-tailwindcss@0.7.1: + resolution: {integrity: sha512-Bzv1LZcuiR1Sk02iJTS1QzlFNp/o5l2p3xkopwOrbPmtMeh3fK9rVW5M3neBQzHq+kGKj/4LGQMTNcTH4NGPtQ==} + engines: {node: '>=20.19'} peerDependencies: '@ianvs/prettier-plugin-sort-imports': '*' '@prettier/plugin-hermes': '*' @@ -1625,14 +1676,12 @@ packages: prettier: ^3.0 prettier-plugin-astro: '*' prettier-plugin-css-order: '*' - prettier-plugin-import-sort: '*' prettier-plugin-jsdoc: '*' prettier-plugin-marko: '*' prettier-plugin-multiline-arrays: '*' prettier-plugin-organize-attributes: '*' prettier-plugin-organize-imports: '*' prettier-plugin-sort-imports: '*' - prettier-plugin-style-order: '*' prettier-plugin-svelte: '*' peerDependenciesMeta: '@ianvs/prettier-plugin-sort-imports': @@ -1653,8 +1702,6 @@ packages: optional: true prettier-plugin-css-order: optional: true - prettier-plugin-import-sort: - optional: true prettier-plugin-jsdoc: optional: true prettier-plugin-marko: @@ -1667,8 +1714,6 @@ packages: optional: true prettier-plugin-sort-imports: optional: true - prettier-plugin-style-order: - optional: true prettier-plugin-svelte: optional: true @@ -1684,8 +1729,8 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} - quansync@0.2.10: - resolution: {integrity: sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==} + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} @@ -1708,8 +1753,8 @@ packages: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} - resolve@1.22.10: - resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} engines: {node: '>= 0.4'} hasBin: true @@ -1717,8 +1762,8 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.44.2: - resolution: {integrity: sha512-PVoapzTwSEcelaWGth3uR66u7ZRo6qhPHc0f2uRO9fX6XDVNrIiGYS0Pj9+R8yIIYSD/mCx2b16Ws9itljKSPg==} + rollup@4.52.5: + resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -1729,13 +1774,13 @@ packages: resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} engines: {node: '>=6'} - semver@7.7.2: - resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} engines: {node: '>=10'} hasBin: true - set-cookie-parser@2.7.1: - resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} @@ -1749,8 +1794,8 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - sirv@3.0.1: - resolution: {integrity: sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A==} + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} engines: {node: '>=18'} sorted-array-functions@1.3.0: @@ -1764,8 +1809,8 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} - std-env@3.9.0: - resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} @@ -1779,8 +1824,8 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} engines: {node: '>=12'} strip-json-comments@3.1.1: @@ -1800,29 +1845,29 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svelte-check@4.2.2: - resolution: {integrity: sha512-1+31EOYZ7NKN0YDMKusav2hhEoA51GD9Ws6o//0SphMT0ve9mBTsTUEX7OmDMadUP3KjNHsSKtJrqdSaD8CrGQ==} + svelte-check@4.3.3: + resolution: {integrity: sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg==} engines: {node: '>= 18.0.0'} hasBin: true peerDependencies: svelte: ^4.0.0 || ^5.0.0-next.0 typescript: '>=5.0.0' - svelte-eslint-parser@1.2.0: - resolution: {integrity: sha512-mbPtajIeuiyU80BEyGvwAktBeTX7KCr5/0l+uRGLq1dafwRNrjfM5kHGJScEBlPG3ipu6dJqfW/k0/fujvIEVw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + svelte-eslint-parser@1.4.0: + resolution: {integrity: sha512-fjPzOfipR5S7gQ/JvI9r2H8y9gMGXO3JtmrylHLLyahEMquXI0lrebcjT+9/hNgDej0H7abTyox5HpHmW1PSWA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0, pnpm: 10.18.3} peerDependencies: svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 peerDependenciesMeta: svelte: optional: true - svelte@5.35.6: - resolution: {integrity: sha512-p7PVLQYrvCxJuxzGfOv/l71hVuHC6EZk5UDjbt/bndMYaBcUV5sFjDsj+PSIYvz1vcfbG6inX83/xIUeik1xGA==} + svelte@5.43.2: + resolution: {integrity: sha512-ro1umEzX8rT5JpCmlf0PPv7ncD8MdVob9e18bhwqTKNoLjS8kDvhVpaoYVPc+qMwDAOfcwJtyY7ZFSDbOaNPgA==} engines: {node: '>=18'} - tailwindcss@3.4.17: - resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} + tailwindcss@3.4.18: + resolution: {integrity: sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==} engines: {node: '>=14.0.0'} hasBin: true @@ -1833,12 +1878,13 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - tinyexec@0.3.2: - resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyexec@1.0.1: resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -1856,30 +1902,40 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + tsconfck@3.1.6: + resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - typescript-eslint@8.36.0: - resolution: {integrity: sha512-fTCqxthY+h9QbEgSIBfL9iV6CvKDFuoxg6bHPNpJ9HIUzS+jy2lCEyCmGyZRWEBSaykqcDPf1SJ+BfCI8DRopA==} + typescript-eslint@8.46.2: + resolution: {integrity: sha512-vbw8bOmiuYNdzzV3lsiWv6sRwjyuKJMQqWulBOU7M0RrxedXledX8G8kBbQeiOYDnTfiXz0Y4081E1QMNB6iQg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <5.9.0' + typescript: '>=4.8.4 <6.0.0' - typescript@5.8.3: - resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} hasBin: true ufo@1.6.1: resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} - undici-types@7.8.0: - resolution: {integrity: sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==} + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} - unplugin-icons@22.1.0: - resolution: {integrity: sha512-ect2ZNtk1Zgwb0NVHd0C1IDW/MV+Jk/xaq4t8o6rYdVS3+L660ZdD5kTSQZvsgdwCvquRw+/wYn75hsweRjoIA==} + unplugin-icons@22.5.0: + resolution: {integrity: sha512-MBlMtT5RuMYZy4TZgqUL2OTtOdTUVsS1Mhj6G1pEzMlFJlEnq6mhUfoIt45gBWxHcsOdXJDWLg3pRZ+YmvAVWQ==} peerDependencies: '@svgr/core': '>=7.0.0' '@svgx/core': ^1.0.1 @@ -1901,12 +1957,12 @@ packages: vue-template-es2015-compiler: optional: true - unplugin@2.3.5: - resolution: {integrity: sha512-RyWSb5AHmGtjjNQ6gIlA67sHOsWpsbWpwDokLwTcejVdOjEkJZh7QKu14J00gDDVSh8kGH4KYC/TNBceXFZhtw==} + unplugin@2.3.10: + resolution: {integrity: sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==} engines: {node: '>=18.12.0'} - update-browserslist-db@1.1.3: - resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + update-browserslist-db@1.1.4: + resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -1917,26 +1973,31 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@11.1.0: - resolution: {integrity: sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==} + uuid@13.0.0: + resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} hasBin: true - vite@5.4.19: - resolution: {integrity: sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==} - engines: {node: ^18.0.0 || >=20.0.0} + vite@7.1.12: + resolution: {integrity: sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==} + engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.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 + jiti: + optional: true less: optional: true lightningcss: @@ -1951,6 +2012,10 @@ packages: optional: true terser: optional: true + tsx: + optional: true + yaml: + optional: true vitefu@1.1.1: resolution: {integrity: sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==} @@ -2001,8 +2066,8 @@ packages: resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} engines: {node: '>=12.20'} - zimmerframe@1.1.2: - resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} + zimmerframe@1.1.4: + resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -2011,120 +2076,126 @@ snapshots: '@alloc/quick-lru@5.2.0': {} - '@ampproject/remapping@2.3.0': - dependencies: - '@jridgewell/gen-mapping': 0.3.12 - '@jridgewell/trace-mapping': 0.3.29 - '@antfu/install-pkg@1.1.0': dependencies: - package-manager-detector: 1.3.0 + package-manager-detector: 1.5.0 tinyexec: 1.0.1 - '@antfu/utils@8.1.1': {} + '@antfu/utils@9.3.0': {} - '@esbuild/aix-ppc64@0.21.5': + '@epic-web/invariant@1.0.0': {} + + '@esbuild/aix-ppc64@0.25.11': optional: true - '@esbuild/android-arm64@0.21.5': + '@esbuild/android-arm64@0.25.11': optional: true - '@esbuild/android-arm@0.21.5': + '@esbuild/android-arm@0.25.11': optional: true - '@esbuild/android-x64@0.21.5': + '@esbuild/android-x64@0.25.11': optional: true - '@esbuild/darwin-arm64@0.21.5': + '@esbuild/darwin-arm64@0.25.11': optional: true - '@esbuild/darwin-x64@0.21.5': + '@esbuild/darwin-x64@0.25.11': optional: true - '@esbuild/freebsd-arm64@0.21.5': + '@esbuild/freebsd-arm64@0.25.11': optional: true - '@esbuild/freebsd-x64@0.21.5': + '@esbuild/freebsd-x64@0.25.11': optional: true - '@esbuild/linux-arm64@0.21.5': + '@esbuild/linux-arm64@0.25.11': optional: true - '@esbuild/linux-arm@0.21.5': + '@esbuild/linux-arm@0.25.11': optional: true - '@esbuild/linux-ia32@0.21.5': + '@esbuild/linux-ia32@0.25.11': optional: true - '@esbuild/linux-loong64@0.21.5': + '@esbuild/linux-loong64@0.25.11': optional: true - '@esbuild/linux-mips64el@0.21.5': + '@esbuild/linux-mips64el@0.25.11': optional: true - '@esbuild/linux-ppc64@0.21.5': + '@esbuild/linux-ppc64@0.25.11': optional: true - '@esbuild/linux-riscv64@0.21.5': + '@esbuild/linux-riscv64@0.25.11': optional: true - '@esbuild/linux-s390x@0.21.5': + '@esbuild/linux-s390x@0.25.11': optional: true - '@esbuild/linux-x64@0.21.5': + '@esbuild/linux-x64@0.25.11': optional: true - '@esbuild/netbsd-x64@0.21.5': + '@esbuild/netbsd-arm64@0.25.11': optional: true - '@esbuild/openbsd-x64@0.21.5': + '@esbuild/netbsd-x64@0.25.11': optional: true - '@esbuild/sunos-x64@0.21.5': + '@esbuild/openbsd-arm64@0.25.11': optional: true - '@esbuild/win32-arm64@0.21.5': + '@esbuild/openbsd-x64@0.25.11': optional: true - '@esbuild/win32-ia32@0.21.5': + '@esbuild/openharmony-arm64@0.25.11': optional: true - '@esbuild/win32-x64@0.21.5': + '@esbuild/sunos-x64@0.25.11': optional: true - '@eslint-community/eslint-utils@4.7.0(eslint@9.30.1(jiti@2.4.2))': + '@esbuild/win32-arm64@0.25.11': + optional: true + + '@esbuild/win32-ia32@0.25.11': + optional: true + + '@esbuild/win32-x64@0.25.11': + optional: true + + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.0(jiti@1.21.7))': dependencies: - eslint: 9.30.1(jiti@2.4.2) + eslint: 9.39.0(jiti@1.21.7) eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.12.1': {} + '@eslint-community/regexpp@4.12.2': {} - '@eslint/compat@1.3.1(eslint@9.30.1(jiti@2.4.2))': - optionalDependencies: - eslint: 9.30.1(jiti@2.4.2) - - '@eslint/config-array@0.21.0': + '@eslint/compat@1.4.1(eslint@9.39.0(jiti@1.21.7))': dependencies: - '@eslint/object-schema': 2.1.6 - debug: 4.4.1 + '@eslint/core': 0.17.0 + optionalDependencies: + eslint: 9.39.0(jiti@1.21.7) + + '@eslint/config-array@0.21.1': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 minimatch: 3.1.2 transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.3.0': {} - - '@eslint/core@0.14.0': + '@eslint/config-helpers@0.4.2': dependencies: - '@types/json-schema': 7.0.15 + '@eslint/core': 0.17.0 - '@eslint/core@0.15.1': + '@eslint/core@0.17.0': dependencies: '@types/json-schema': 7.0.15 '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 - debug: 4.4.1 + debug: 4.4.3 espree: 10.4.0 globals: 14.0.0 ignore: 5.3.2 @@ -2135,46 +2206,44 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.30.1': {} + '@eslint/js@9.39.0': {} - '@eslint/object-schema@2.1.6': {} + '@eslint/object-schema@2.1.7': {} - '@eslint/plugin-kit@0.3.3': + '@eslint/plugin-kit@0.4.1': dependencies: - '@eslint/core': 0.15.1 + '@eslint/core': 0.17.0 levn: 0.4.1 - '@fastify/busboy@3.1.1': {} + '@fastify/busboy@3.2.0': {} '@humanfs/core@0.19.1': {} - '@humanfs/node@0.16.6': + '@humanfs/node@0.16.7': dependencies: '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.3.1 + '@humanwhocodes/retry': 0.4.3 '@humanwhocodes/module-importer@1.0.1': {} - '@humanwhocodes/retry@0.3.1': {} - '@humanwhocodes/retry@0.4.3': {} - '@iconify-json/material-symbols@1.2.29': + '@iconify-json/material-symbols@1.2.44': dependencies: '@iconify/types': 2.0.0 '@iconify/types@2.0.0': {} - '@iconify/utils@2.3.0': + '@iconify/utils@3.0.2': dependencies: '@antfu/install-pkg': 1.1.0 - '@antfu/utils': 8.1.1 + '@antfu/utils': 9.3.0 '@iconify/types': 2.0.0 - debug: 4.4.1 + debug: 4.4.3 globals: 15.15.0 kolorist: 1.8.0 - local-pkg: 1.1.1 - mlly: 1.7.4 + local-pkg: 1.1.2 + mlly: 1.8.0 transitivePeerDependencies: - supports-color @@ -2182,24 +2251,29 @@ snapshots: dependencies: string-width: 5.1.2 string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@jridgewell/gen-mapping@0.3.12': + '@jridgewell/gen-mapping@0.3.13': dependencies: - '@jridgewell/sourcemap-codec': 1.5.4 - '@jridgewell/trace-mapping': 0.3.29 + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/sourcemap-codec@1.5.4': {} + '@jridgewell/sourcemap-codec@1.5.5': {} - '@jridgewell/trace-mapping@0.3.29': + '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/sourcemap-codec': 1.5.5 '@nodelib/fs.scandir@2.1.5': dependencies: @@ -2220,151 +2294,159 @@ snapshots: '@polka/url@1.0.0-next.29': {} - '@rollup/plugin-commonjs@28.0.6(rollup@4.44.2)': + '@rollup/plugin-commonjs@28.0.9(rollup@4.52.5)': dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.44.2) + '@rollup/pluginutils': 5.3.0(rollup@4.52.5) commondir: 1.0.1 estree-walker: 2.0.2 - fdir: 6.4.6(picomatch@4.0.2) + fdir: 6.5.0(picomatch@4.0.3) is-reference: 1.2.1 - magic-string: 0.30.17 - picomatch: 4.0.2 + magic-string: 0.30.21 + picomatch: 4.0.3 optionalDependencies: - rollup: 4.44.2 + rollup: 4.52.5 - '@rollup/plugin-json@6.1.0(rollup@4.44.2)': + '@rollup/plugin-json@6.1.0(rollup@4.52.5)': dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.44.2) + '@rollup/pluginutils': 5.3.0(rollup@4.52.5) optionalDependencies: - rollup: 4.44.2 + rollup: 4.52.5 - '@rollup/plugin-node-resolve@16.0.1(rollup@4.44.2)': + '@rollup/plugin-node-resolve@16.0.3(rollup@4.52.5)': dependencies: - '@rollup/pluginutils': 5.2.0(rollup@4.44.2) + '@rollup/pluginutils': 5.3.0(rollup@4.52.5) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 - resolve: 1.22.10 + resolve: 1.22.11 optionalDependencies: - rollup: 4.44.2 + rollup: 4.52.5 - '@rollup/pluginutils@5.2.0(rollup@4.44.2)': + '@rollup/pluginutils@5.3.0(rollup@4.52.5)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 - picomatch: 4.0.2 + picomatch: 4.0.3 optionalDependencies: - rollup: 4.44.2 + rollup: 4.52.5 - '@rollup/rollup-android-arm-eabi@4.44.2': + '@rollup/rollup-android-arm-eabi@4.52.5': optional: true - '@rollup/rollup-android-arm64@4.44.2': + '@rollup/rollup-android-arm64@4.52.5': optional: true - '@rollup/rollup-darwin-arm64@4.44.2': + '@rollup/rollup-darwin-arm64@4.52.5': optional: true - '@rollup/rollup-darwin-x64@4.44.2': + '@rollup/rollup-darwin-x64@4.52.5': optional: true - '@rollup/rollup-freebsd-arm64@4.44.2': + '@rollup/rollup-freebsd-arm64@4.52.5': optional: true - '@rollup/rollup-freebsd-x64@4.44.2': + '@rollup/rollup-freebsd-x64@4.52.5': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.44.2': + '@rollup/rollup-linux-arm-gnueabihf@4.52.5': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.44.2': + '@rollup/rollup-linux-arm-musleabihf@4.52.5': optional: true - '@rollup/rollup-linux-arm64-gnu@4.44.2': + '@rollup/rollup-linux-arm64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-arm64-musl@4.44.2': + '@rollup/rollup-linux-arm64-musl@4.52.5': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.44.2': + '@rollup/rollup-linux-loong64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.44.2': + '@rollup/rollup-linux-ppc64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.44.2': + '@rollup/rollup-linux-riscv64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-riscv64-musl@4.44.2': + '@rollup/rollup-linux-riscv64-musl@4.52.5': optional: true - '@rollup/rollup-linux-s390x-gnu@4.44.2': + '@rollup/rollup-linux-s390x-gnu@4.52.5': optional: true - '@rollup/rollup-linux-x64-gnu@4.44.2': + '@rollup/rollup-linux-x64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-x64-musl@4.44.2': + '@rollup/rollup-linux-x64-musl@4.52.5': optional: true - '@rollup/rollup-win32-arm64-msvc@4.44.2': + '@rollup/rollup-openharmony-arm64@4.52.5': optional: true - '@rollup/rollup-win32-ia32-msvc@4.44.2': + '@rollup/rollup-win32-arm64-msvc@4.52.5': optional: true - '@rollup/rollup-win32-x64-msvc@4.44.2': + '@rollup/rollup-win32-ia32-msvc@4.52.5': optional: true - '@sveltejs/acorn-typescript@1.0.5(acorn@8.15.0)': + '@rollup/rollup-win32-x64-gnu@4.52.5': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.52.5': + optional: true + + '@standard-schema/spec@1.0.0': {} + + '@sveltejs/acorn-typescript@1.0.6(acorn@8.15.0)': dependencies: acorn: 8.15.0 - '@sveltejs/adapter-node@5.2.13(@sveltejs/kit@2.22.5(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.35.6)(vite@5.4.19(@types/node@24.0.13)))(svelte@5.35.6)(vite@5.4.19(@types/node@24.0.13)))': + '@sveltejs/adapter-node@5.4.0(@sveltejs/kit@2.48.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)))': dependencies: - '@rollup/plugin-commonjs': 28.0.6(rollup@4.44.2) - '@rollup/plugin-json': 6.1.0(rollup@4.44.2) - '@rollup/plugin-node-resolve': 16.0.1(rollup@4.44.2) - '@sveltejs/kit': 2.22.5(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.35.6)(vite@5.4.19(@types/node@24.0.13)))(svelte@5.35.6)(vite@5.4.19(@types/node@24.0.13)) - rollup: 4.44.2 + '@rollup/plugin-commonjs': 28.0.9(rollup@4.52.5) + '@rollup/plugin-json': 6.1.0(rollup@4.52.5) + '@rollup/plugin-node-resolve': 16.0.3(rollup@4.52.5) + '@sveltejs/kit': 2.48.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)) + rollup: 4.52.5 - '@sveltejs/kit@2.22.5(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.35.6)(vite@5.4.19(@types/node@24.0.13)))(svelte@5.35.6)(vite@5.4.19(@types/node@24.0.13))': + '@sveltejs/kit@2.48.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0))': dependencies: - '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) - '@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.35.6)(vite@5.4.19(@types/node@24.0.13)) + '@standard-schema/spec': 1.0.0 + '@sveltejs/acorn-typescript': 1.0.6(acorn@8.15.0) + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)) '@types/cookie': 0.6.0 acorn: 8.15.0 cookie: 0.6.0 - devalue: 5.1.1 + devalue: 5.4.2 esm-env: 1.2.2 kleur: 4.1.5 - magic-string: 0.30.17 + magic-string: 0.30.21 mrmime: 2.0.1 sade: 1.8.1 - set-cookie-parser: 2.7.1 - sirv: 3.0.1 - svelte: 5.35.6 - vite: 5.4.19(@types/node@24.0.13) + set-cookie-parser: 2.7.2 + sirv: 3.0.2 + svelte: 5.43.2 + vite: 7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0) - '@sveltejs/vite-plugin-svelte-inspector@3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.35.6)(vite@5.4.19(@types/node@24.0.13)))(svelte@5.35.6)(vite@5.4.19(@types/node@24.0.13))': + '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.35.6)(vite@5.4.19(@types/node@24.0.13)) - debug: 4.4.1 - svelte: 5.35.6 - vite: 5.4.19(@types/node@24.0.13) + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)) + debug: 4.4.3 + svelte: 5.43.2 + vite: 7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.35.6)(vite@5.4.19(@types/node@24.0.13))': + '@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.35.6)(vite@5.4.19(@types/node@24.0.13)))(svelte@5.35.6)(vite@5.4.19(@types/node@24.0.13)) - debug: 4.4.1 + '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)) + debug: 4.4.3 deepmerge: 4.3.1 - kleur: 4.1.5 - magic-string: 0.30.17 - svelte: 5.35.6 - vite: 5.4.19(@types/node@24.0.13) - vitefu: 1.1.1(vite@5.4.19(@types/node@24.0.13)) + magic-string: 0.30.21 + svelte: 5.43.2 + vite: 7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0) + vitefu: 1.1.1(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)) transitivePeerDependencies: - supports-color @@ -2376,114 +2458,115 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/ms@0.7.34': {} + '@types/ms@2.1.0': {} '@types/node-schedule@2.1.8': dependencies: - '@types/node': 24.0.13 + '@types/node': 24.9.2 - '@types/node@24.0.13': + '@types/node@24.9.2': dependencies: - undici-types: 7.8.0 + undici-types: 7.16.0 - '@types/pg@8.15.4': + '@types/pg@8.15.6': dependencies: - '@types/node': 24.0.13 + '@types/node': 24.9.2 pg-protocol: 1.10.3 pg-types: 2.2.0 '@types/resolve@1.20.2': {} - '@typescript-eslint/eslint-plugin@8.36.0(@typescript-eslint/parser@8.36.0(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3))(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3)': dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.36.0(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/scope-manager': 8.36.0 - '@typescript-eslint/type-utils': 8.36.0(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/utils': 8.36.0(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.36.0 - eslint: 9.30.1(jiti@2.4.2) + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.46.2 + '@typescript-eslint/type-utils': 8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.46.2 + eslint: 9.39.0(jiti@1.21.7) graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.36.0(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.36.0 - '@typescript-eslint/types': 8.36.0 - '@typescript-eslint/typescript-estree': 8.36.0(typescript@5.8.3) - '@typescript-eslint/visitor-keys': 8.36.0 - debug: 4.4.1 - eslint: 9.30.1(jiti@2.4.2) - typescript: 5.8.3 + '@typescript-eslint/scope-manager': 8.46.2 + '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.46.2 + debug: 4.4.3 + eslint: 9.39.0(jiti@1.21.7) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.36.0(typescript@5.8.3)': + '@typescript-eslint/project-service@8.46.2(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.36.0(typescript@5.8.3) - '@typescript-eslint/types': 8.36.0 - debug: 4.4.1 - typescript: 5.8.3 + '@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3) + '@typescript-eslint/types': 8.46.2 + debug: 4.4.3 + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.36.0': + '@typescript-eslint/scope-manager@8.46.2': dependencies: - '@typescript-eslint/types': 8.36.0 - '@typescript-eslint/visitor-keys': 8.36.0 + '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/visitor-keys': 8.46.2 - '@typescript-eslint/tsconfig-utils@8.36.0(typescript@5.8.3)': + '@typescript-eslint/tsconfig-utils@8.46.2(typescript@5.9.3)': dependencies: - typescript: 5.8.3 + typescript: 5.9.3 - '@typescript-eslint/type-utils@8.36.0(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.36.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.36.0(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3) - debug: 4.4.1 - eslint: 9.30.1(jiti@2.4.2) - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.0(jiti@1.21.7) + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.36.0': {} + '@typescript-eslint/types@8.46.2': {} - '@typescript-eslint/typescript-estree@8.36.0(typescript@5.8.3)': + '@typescript-eslint/typescript-estree@8.46.2(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.36.0(typescript@5.8.3) - '@typescript-eslint/tsconfig-utils': 8.36.0(typescript@5.8.3) - '@typescript-eslint/types': 8.36.0 - '@typescript-eslint/visitor-keys': 8.36.0 - debug: 4.4.1 + '@typescript-eslint/project-service': 8.46.2(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3) + '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/visitor-keys': 8.46.2 + debug: 4.4.3 fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 - semver: 7.7.2 - ts-api-utils: 2.1.0(typescript@5.8.3) - typescript: 5.8.3 + semver: 7.7.3 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.36.0(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3)': + '@typescript-eslint/utils@8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.30.1(jiti@2.4.2)) - '@typescript-eslint/scope-manager': 8.36.0 - '@typescript-eslint/types': 8.36.0 - '@typescript-eslint/typescript-estree': 8.36.0(typescript@5.8.3) - eslint: 9.30.1(jiti@2.4.2) - typescript: 5.8.3 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.0(jiti@1.21.7)) + '@typescript-eslint/scope-manager': 8.46.2 + '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) + eslint: 9.39.0(jiti@1.21.7) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.36.0': + '@typescript-eslint/visitor-keys@8.46.2': dependencies: - '@typescript-eslint/types': 8.36.0 + '@typescript-eslint/types': 8.46.2 eslint-visitor-keys: 4.2.1 '@xmldom/xmldom@0.9.8': @@ -2504,13 +2587,13 @@ snapshots: ansi-regex@5.0.1: {} - ansi-regex@6.1.0: {} + ansi-regex@6.2.2: {} ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - ansi-styles@6.2.1: {} + ansi-styles@6.2.3: {} any-promise@1.3.0: {} @@ -2521,10 +2604,11 @@ snapshots: arg@5.0.2: {} - argon2@0.43.0: + argon2@0.44.0: dependencies: '@phc/format': 1.0.0 - node-addon-api: 8.4.0 + cross-env: 10.1.0 + node-addon-api: 8.5.0 node-gyp-build: 4.8.4 argparse@2.0.1: {} @@ -2535,18 +2619,18 @@ snapshots: autoprefixer@10.4.21(postcss@8.5.6): dependencies: - browserslist: 4.25.1 - caniuse-lite: 1.0.30001727 + browserslist: 4.27.0 + caniuse-lite: 1.0.30001752 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 postcss: 8.5.6 postcss-value-parser: 4.2.0 - axios@1.10.0: + axios@1.13.1: dependencies: - follow-redirects: 1.15.9 - form-data: 4.0.3 + follow-redirects: 1.15.11 + form-data: 4.0.4 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug @@ -2555,6 +2639,8 @@ snapshots: balanced-match@1.0.2: {} + baseline-browser-mapping@2.8.23: {} + binary-extensions@2.3.0: {} brace-expansion@1.1.12: @@ -2570,26 +2656,27 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.25.1: + browserslist@4.27.0: dependencies: - caniuse-lite: 1.0.30001727 - electron-to-chromium: 1.5.182 - node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.25.1) + baseline-browser-mapping: 2.8.23 + caniuse-lite: 1.0.30001752 + electron-to-chromium: 1.5.244 + node-releases: 2.0.27 + update-browserslist-db: 1.1.4(browserslist@4.27.0) - c12@3.0.4: + c12@3.3.1: dependencies: chokidar: 4.0.3 confbox: 0.2.2 defu: 6.1.4 - dotenv: 16.6.1 + dotenv: 17.2.3 exsolve: 1.0.7 giget: 2.0.0 - jiti: 2.4.2 + jiti: 2.6.1 ohash: 2.0.11 pathe: 2.0.3 - perfect-debounce: 1.0.0 - pkg-types: 2.2.0 + perfect-debounce: 2.0.0 + pkg-types: 2.3.0 rc9: 2.1.2 call-bind-apply-helpers@1.0.2: @@ -2601,7 +2688,7 @@ snapshots: camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001727: {} + caniuse-lite@1.0.30001752: {} chalk@4.1.2: dependencies: @@ -2656,7 +2743,12 @@ snapshots: cron-parser@4.9.0: dependencies: - luxon: 3.7.1 + luxon: 3.7.2 + + cross-env@10.1.0: + dependencies: + '@epic-web/invariant': 1.0.0 + cross-spawn: 7.0.6 cross-spawn@7.0.6: dependencies: @@ -2666,7 +2758,7 @@ snapshots: cssesc@3.0.0: {} - debug@4.4.1: + debug@4.4.3: dependencies: ms: 2.1.3 @@ -2680,15 +2772,15 @@ snapshots: destr@2.0.5: {} - devalue@5.1.1: {} + devalue@5.4.2: {} - dexie@4.0.11: {} + dexie@4.2.1: {} didyoumean@1.2.2: {} dlv@1.1.3: {} - dotenv@16.6.1: {} + dotenv@17.2.3: {} dunder-proto@1.0.1: dependencies: @@ -2698,7 +2790,7 @@ snapshots: eastasianwidth@0.2.0: {} - electron-to-chromium@1.5.182: {} + electron-to-chromium@1.5.244: {} emoji-regex@8.0.0: {} @@ -2719,63 +2811,66 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 - esbuild@0.21.5: + esbuild@0.25.11: optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 + '@esbuild/aix-ppc64': 0.25.11 + '@esbuild/android-arm': 0.25.11 + '@esbuild/android-arm64': 0.25.11 + '@esbuild/android-x64': 0.25.11 + '@esbuild/darwin-arm64': 0.25.11 + '@esbuild/darwin-x64': 0.25.11 + '@esbuild/freebsd-arm64': 0.25.11 + '@esbuild/freebsd-x64': 0.25.11 + '@esbuild/linux-arm': 0.25.11 + '@esbuild/linux-arm64': 0.25.11 + '@esbuild/linux-ia32': 0.25.11 + '@esbuild/linux-loong64': 0.25.11 + '@esbuild/linux-mips64el': 0.25.11 + '@esbuild/linux-ppc64': 0.25.11 + '@esbuild/linux-riscv64': 0.25.11 + '@esbuild/linux-s390x': 0.25.11 + '@esbuild/linux-x64': 0.25.11 + '@esbuild/netbsd-arm64': 0.25.11 + '@esbuild/netbsd-x64': 0.25.11 + '@esbuild/openbsd-arm64': 0.25.11 + '@esbuild/openbsd-x64': 0.25.11 + '@esbuild/openharmony-arm64': 0.25.11 + '@esbuild/sunos-x64': 0.25.11 + '@esbuild/win32-arm64': 0.25.11 + '@esbuild/win32-ia32': 0.25.11 + '@esbuild/win32-x64': 0.25.11 escalade@3.2.0: {} escape-string-regexp@4.0.0: {} - eslint-config-prettier@10.1.5(eslint@9.30.1(jiti@2.4.2)): + eslint-config-prettier@10.1.8(eslint@9.39.0(jiti@1.21.7)): dependencies: - eslint: 9.30.1(jiti@2.4.2) + eslint: 9.39.0(jiti@1.21.7) - eslint-plugin-svelte@3.10.1(eslint@9.30.1(jiti@2.4.2))(svelte@5.35.6): + eslint-plugin-svelte@3.13.0(eslint@9.39.0(jiti@1.21.7))(svelte@5.43.2): dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.30.1(jiti@2.4.2)) - '@jridgewell/sourcemap-codec': 1.5.4 - eslint: 9.30.1(jiti@2.4.2) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.0(jiti@1.21.7)) + '@jridgewell/sourcemap-codec': 1.5.5 + eslint: 9.39.0(jiti@1.21.7) esutils: 2.0.3 - globals: 16.3.0 + globals: 16.5.0 known-css-properties: 0.37.0 postcss: 8.5.6 postcss-load-config: 3.1.4(postcss@8.5.6) postcss-safe-parser: 7.0.1(postcss@8.5.6) - semver: 7.7.2 - svelte-eslint-parser: 1.2.0(svelte@5.35.6) + semver: 7.7.3 + svelte-eslint-parser: 1.4.0(svelte@5.43.2) optionalDependencies: - svelte: 5.35.6 + svelte: 5.43.2 transitivePeerDependencies: - ts-node - eslint-plugin-tailwindcss@3.18.0(tailwindcss@3.4.17): + eslint-plugin-tailwindcss@3.18.2(tailwindcss@3.4.18(yaml@2.8.0)): dependencies: fast-glob: 3.3.3 postcss: 8.5.6 - tailwindcss: 3.4.17 + tailwindcss: 3.4.18(yaml@2.8.0) eslint-scope@8.4.0: dependencies: @@ -2786,25 +2881,24 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.30.1(jiti@2.4.2): + eslint@9.39.0(jiti@1.21.7): dependencies: - '@eslint-community/eslint-utils': 4.7.0(eslint@9.30.1(jiti@2.4.2)) - '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.21.0 - '@eslint/config-helpers': 0.3.0 - '@eslint/core': 0.14.0 + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.0(jiti@1.21.7)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.30.1 - '@eslint/plugin-kit': 0.3.3 - '@humanfs/node': 0.16.6 + '@eslint/js': 9.39.0 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 - '@types/json-schema': 7.0.15 ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.6 - debug: 4.4.1 + debug: 4.4.3 escape-string-regexp: 4.0.0 eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -2824,7 +2918,7 @@ snapshots: natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: - jiti: 2.4.2 + jiti: 1.21.7 transitivePeerDependencies: - supports-color @@ -2840,9 +2934,9 @@ snapshots: dependencies: estraverse: 5.3.0 - esrap@2.1.0: + esrap@2.1.2: dependencies: - '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/sourcemap-codec': 1.5.5 esrecurse@4.3.0: dependencies: @@ -2854,7 +2948,7 @@ snapshots: esutils@2.0.3: {} - exifreader@4.31.1: + exifreader@4.32.0: optionalDependencies: '@xmldom/xmldom': 0.9.8 @@ -2878,9 +2972,9 @@ snapshots: dependencies: reusify: 1.1.0 - fdir@6.4.6(picomatch@4.0.2): + fdir@6.5.0(picomatch@4.0.3): optionalDependencies: - picomatch: 4.0.2 + picomatch: 4.0.3 file-entry-cache@8.0.0: dependencies: @@ -2904,14 +2998,14 @@ snapshots: flatted@3.3.3: {} - follow-redirects@1.15.9: {} + follow-redirects@1.15.11: {} foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 signal-exit: 4.1.0 - form-data@4.0.3: + form-data@4.0.4: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 @@ -2949,8 +3043,8 @@ snapshots: citty: 0.1.6 consola: 3.4.2 defu: 6.1.4 - node-fetch-native: 1.6.6 - nypm: 0.6.0 + node-fetch-native: 1.6.7 + nypm: 0.6.2 pathe: 2.0.3 glob-parent@5.1.2: @@ -2974,7 +3068,7 @@ snapshots: globals@15.15.0: {} - globals@16.3.0: {} + globals@16.5.0: {} gopd@1.2.0: {} @@ -3043,7 +3137,7 @@ snapshots: jiti@1.21.7: {} - jiti@2.4.2: {} + jiti@2.6.1: {} js-yaml@4.1.0: dependencies: @@ -3065,22 +3159,25 @@ snapshots: kolorist@1.8.0: {} - kysely-ctl@0.13.1(kysely@0.28.2): + kysely-ctl@0.19.0(kysely@0.28.8)(typescript@5.9.3): dependencies: - c12: 3.0.4 + c12: 3.3.1 citty: 0.1.6 + confbox: 0.2.2 consola: 3.4.2 - jiti: 2.4.2 - kysely: 0.28.2 - nypm: 0.6.0 - ofetch: 1.4.1 + jiti: 2.6.1 + kysely: 0.28.8 + nypm: 0.6.2 + ofetch: 1.5.1 pathe: 2.0.3 - pkg-types: 2.2.0 - std-env: 3.9.0 + pkg-types: 2.3.0 + std-env: 3.10.0 + tsconfck: 3.1.6(typescript@5.9.3) transitivePeerDependencies: - magicast + - typescript - kysely@0.28.2: {} + kysely@0.28.8: {} levn@0.4.1: dependencies: @@ -3093,11 +3190,11 @@ snapshots: lines-and-columns@1.2.4: {} - local-pkg@1.1.1: + local-pkg@1.1.2: dependencies: - mlly: 1.7.4 - pkg-types: 2.2.0 - quansync: 0.2.10 + mlly: 1.8.0 + pkg-types: 2.3.0 + quansync: 0.2.11 locate-character@3.0.0: {} @@ -3111,13 +3208,13 @@ snapshots: lru-cache@10.4.3: {} - lru-cache@11.1.0: {} + lru-cache@11.2.2: {} - luxon@3.7.1: {} + luxon@3.7.2: {} - magic-string@0.30.17: + magic-string@0.30.21: dependencies: - '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/sourcemap-codec': 1.5.5 math-intrinsics@1.1.0: {} @@ -3134,7 +3231,7 @@ snapshots: dependencies: mime-db: 1.52.0 - mime@4.0.7: {} + mime@4.1.0: {} minimatch@3.1.2: dependencies: @@ -3146,7 +3243,7 @@ snapshots: minipass@7.1.2: {} - mlly@1.7.4: + mlly@1.8.0: dependencies: acorn: 8.15.0 pathe: 2.0.3 @@ -3169,13 +3266,13 @@ snapshots: natural-compare@1.4.0: {} - node-addon-api@8.4.0: {} + node-addon-api@8.5.0: {} - node-fetch-native@1.6.6: {} + node-fetch-native@1.6.7: {} node-gyp-build@4.8.4: {} - node-releases@2.0.19: {} + node-releases@2.0.27: {} node-schedule@2.1.1: dependencies: @@ -3187,22 +3284,22 @@ snapshots: normalize-range@0.1.2: {} - nypm@0.6.0: + nypm@0.6.2: dependencies: citty: 0.1.6 consola: 3.4.2 pathe: 2.0.3 - pkg-types: 2.2.0 - tinyexec: 0.3.2 + pkg-types: 2.3.0 + tinyexec: 1.0.1 object-assign@4.1.1: {} object-hash@3.0.0: {} - ofetch@1.4.1: + ofetch@1.5.1: dependencies: destr: 2.0.5 - node-fetch-native: 1.6.6 + node-fetch-native: 1.6.7 ufo: 1.6.1 ohash@2.0.11: {} @@ -3220,7 +3317,7 @@ snapshots: dependencies: yocto-queue: 0.1.0 - p-limit@6.2.0: + p-limit@7.2.0: dependencies: yocto-queue: 1.2.1 @@ -3230,7 +3327,7 @@ snapshots: package-json-from-dist@1.0.1: {} - package-manager-detector@1.3.0: {} + package-manager-detector@1.5.0: {} parent-module@1.0.1: dependencies: @@ -3249,7 +3346,7 @@ snapshots: pathe@2.0.3: {} - perfect-debounce@1.0.0: {} + perfect-debounce@2.0.0: {} pg-cloudflare@1.2.7: optional: true @@ -3290,7 +3387,7 @@ snapshots: picomatch@2.3.1: {} - picomatch@4.0.2: {} + picomatch@4.0.3: {} pify@2.3.0: {} @@ -3299,10 +3396,10 @@ snapshots: pkg-types@1.3.1: dependencies: confbox: 0.1.8 - mlly: 1.7.4 + mlly: 1.8.0 pathe: 2.0.3 - pkg-types@2.2.0: + pkg-types@2.3.0: dependencies: confbox: 0.2.2 exsolve: 1.0.7 @@ -3313,9 +3410,9 @@ snapshots: postcss: 8.5.6 postcss-value-parser: 4.2.0 read-cache: 1.0.0 - resolve: 1.22.10 + resolve: 1.22.11 - postcss-js@4.0.1(postcss@8.5.6): + postcss-js@4.1.0(postcss@8.5.6): dependencies: camelcase-css: 2.0.1 postcss: 8.5.6 @@ -3327,12 +3424,13 @@ snapshots: optionalDependencies: postcss: 8.5.6 - postcss-load-config@4.0.2(postcss@8.5.6): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6)(yaml@2.8.0): dependencies: lilconfig: 3.1.3 - yaml: 2.8.0 optionalDependencies: + jiti: 1.21.7 postcss: 8.5.6 + yaml: 2.8.0 postcss-nested@6.2.0(postcss@8.5.6): dependencies: @@ -3377,16 +3475,16 @@ snapshots: prelude-ls@1.2.1: {} - prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.35.6): + prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.43.2): dependencies: prettier: 3.6.2 - svelte: 5.35.6 + svelte: 5.43.2 - prettier-plugin-tailwindcss@0.6.14(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.35.6))(prettier@3.6.2): + prettier-plugin-tailwindcss@0.7.1(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.43.2))(prettier@3.6.2): dependencies: prettier: 3.6.2 optionalDependencies: - prettier-plugin-svelte: 3.4.0(prettier@3.6.2)(svelte@5.35.6) + prettier-plugin-svelte: 3.4.0(prettier@3.6.2)(svelte@5.43.2) prettier@3.6.2: {} @@ -3394,7 +3492,7 @@ snapshots: punycode@2.3.1: {} - quansync@0.2.10: {} + quansync@0.2.11: {} queue-microtask@1.2.3: {} @@ -3415,7 +3513,7 @@ snapshots: resolve-from@4.0.0: {} - resolve@1.22.10: + resolve@1.22.11: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 @@ -3423,30 +3521,32 @@ snapshots: reusify@1.1.0: {} - rollup@4.44.2: + rollup@4.52.5: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.44.2 - '@rollup/rollup-android-arm64': 4.44.2 - '@rollup/rollup-darwin-arm64': 4.44.2 - '@rollup/rollup-darwin-x64': 4.44.2 - '@rollup/rollup-freebsd-arm64': 4.44.2 - '@rollup/rollup-freebsd-x64': 4.44.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.44.2 - '@rollup/rollup-linux-arm-musleabihf': 4.44.2 - '@rollup/rollup-linux-arm64-gnu': 4.44.2 - '@rollup/rollup-linux-arm64-musl': 4.44.2 - '@rollup/rollup-linux-loongarch64-gnu': 4.44.2 - '@rollup/rollup-linux-powerpc64le-gnu': 4.44.2 - '@rollup/rollup-linux-riscv64-gnu': 4.44.2 - '@rollup/rollup-linux-riscv64-musl': 4.44.2 - '@rollup/rollup-linux-s390x-gnu': 4.44.2 - '@rollup/rollup-linux-x64-gnu': 4.44.2 - '@rollup/rollup-linux-x64-musl': 4.44.2 - '@rollup/rollup-win32-arm64-msvc': 4.44.2 - '@rollup/rollup-win32-ia32-msvc': 4.44.2 - '@rollup/rollup-win32-x64-msvc': 4.44.2 + '@rollup/rollup-android-arm-eabi': 4.52.5 + '@rollup/rollup-android-arm64': 4.52.5 + '@rollup/rollup-darwin-arm64': 4.52.5 + '@rollup/rollup-darwin-x64': 4.52.5 + '@rollup/rollup-freebsd-arm64': 4.52.5 + '@rollup/rollup-freebsd-x64': 4.52.5 + '@rollup/rollup-linux-arm-gnueabihf': 4.52.5 + '@rollup/rollup-linux-arm-musleabihf': 4.52.5 + '@rollup/rollup-linux-arm64-gnu': 4.52.5 + '@rollup/rollup-linux-arm64-musl': 4.52.5 + '@rollup/rollup-linux-loong64-gnu': 4.52.5 + '@rollup/rollup-linux-ppc64-gnu': 4.52.5 + '@rollup/rollup-linux-riscv64-gnu': 4.52.5 + '@rollup/rollup-linux-riscv64-musl': 4.52.5 + '@rollup/rollup-linux-s390x-gnu': 4.52.5 + '@rollup/rollup-linux-x64-gnu': 4.52.5 + '@rollup/rollup-linux-x64-musl': 4.52.5 + '@rollup/rollup-openharmony-arm64': 4.52.5 + '@rollup/rollup-win32-arm64-msvc': 4.52.5 + '@rollup/rollup-win32-ia32-msvc': 4.52.5 + '@rollup/rollup-win32-x64-gnu': 4.52.5 + '@rollup/rollup-win32-x64-msvc': 4.52.5 fsevents: 2.3.3 run-parallel@1.2.0: @@ -3457,9 +3557,9 @@ snapshots: dependencies: mri: 1.2.0 - semver@7.7.2: {} + semver@7.7.3: {} - set-cookie-parser@2.7.1: {} + set-cookie-parser@2.7.2: {} shebang-command@2.0.0: dependencies: @@ -3469,7 +3569,7 @@ snapshots: signal-exit@4.1.0: {} - sirv@3.0.1: + sirv@3.0.2: dependencies: '@polka/url': 1.0.0-next.29 mrmime: 2.0.1 @@ -3481,7 +3581,7 @@ snapshots: split2@4.2.0: {} - std-env@3.9.0: {} + std-env@3.10.0: {} string-width@4.2.3: dependencies: @@ -3493,21 +3593,21 @@ snapshots: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.0: + strip-ansi@7.1.2: dependencies: - ansi-regex: 6.1.0 + ansi-regex: 6.2.2 strip-json-comments@3.1.1: {} sucrase@3.35.0: dependencies: - '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/gen-mapping': 0.3.13 commander: 4.1.1 glob: 10.4.5 lines-and-columns: 1.2.4 @@ -3521,19 +3621,19 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-check@4.2.2(picomatch@4.0.2)(svelte@5.35.6)(typescript@5.8.3): + svelte-check@4.3.3(picomatch@4.0.3)(svelte@5.43.2)(typescript@5.9.3): dependencies: - '@jridgewell/trace-mapping': 0.3.29 + '@jridgewell/trace-mapping': 0.3.31 chokidar: 4.0.3 - fdir: 6.4.6(picomatch@4.0.2) + fdir: 6.5.0(picomatch@4.0.3) picocolors: 1.1.1 sade: 1.8.1 - svelte: 5.35.6 - typescript: 5.8.3 + svelte: 5.43.2 + typescript: 5.9.3 transitivePeerDependencies: - picomatch - svelte-eslint-parser@1.2.0(svelte@5.35.6): + svelte-eslint-parser@1.4.0(svelte@5.43.2): dependencies: eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -3542,26 +3642,26 @@ snapshots: postcss-scss: 4.0.9(postcss@8.5.6) postcss-selector-parser: 7.1.0 optionalDependencies: - svelte: 5.35.6 + svelte: 5.43.2 - svelte@5.35.6: + svelte@5.43.2: dependencies: - '@ampproject/remapping': 2.3.0 - '@jridgewell/sourcemap-codec': 1.5.4 - '@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0) + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.5.5 + '@sveltejs/acorn-typescript': 1.0.6(acorn@8.15.0) '@types/estree': 1.0.8 acorn: 8.15.0 aria-query: 5.3.2 axobject-query: 4.1.0 clsx: 2.1.1 esm-env: 1.2.2 - esrap: 2.1.0 + esrap: 2.1.2 is-reference: 3.0.3 locate-character: 3.0.0 - magic-string: 0.30.17 - zimmerframe: 1.1.2 + magic-string: 0.30.21 + zimmerframe: 1.1.4 - tailwindcss@3.4.17: + tailwindcss@3.4.18(yaml@2.8.0): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -3579,14 +3679,15 @@ snapshots: picocolors: 1.1.1 postcss: 8.5.6 postcss-import: 15.1.0(postcss@8.5.6) - postcss-js: 4.0.1(postcss@8.5.6) - postcss-load-config: 4.0.2(postcss@8.5.6) + postcss-js: 4.1.0(postcss@8.5.6) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)(yaml@2.8.0) postcss-nested: 6.2.0(postcss@8.5.6) postcss-selector-parser: 6.1.2 - resolve: 1.22.10 + resolve: 1.22.11 sucrase: 3.35.0 transitivePeerDependencies: - - ts-node + - tsx + - yaml thenify-all@1.6.0: dependencies: @@ -3596,63 +3697,72 @@ snapshots: dependencies: any-promise: 1.3.0 - tinyexec@0.3.2: {} - tinyexec@1.0.1: {} + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 totalist@3.0.1: {} - ts-api-utils@2.1.0(typescript@5.8.3): + ts-api-utils@2.1.0(typescript@5.9.3): dependencies: - typescript: 5.8.3 + typescript: 5.9.3 ts-interface-checker@0.1.13: {} + tsconfck@3.1.6(typescript@5.9.3): + optionalDependencies: + typescript: 5.9.3 + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - typescript-eslint@8.36.0(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3): + typescript-eslint@8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.36.0(@typescript-eslint/parser@8.36.0(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3))(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/parser': 8.36.0(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3) - '@typescript-eslint/utils': 8.36.0(eslint@9.30.1(jiti@2.4.2))(typescript@5.8.3) - eslint: 9.30.1(jiti@2.4.2) - typescript: 5.8.3 + '@typescript-eslint/eslint-plugin': 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/parser': 8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3) + eslint: 9.39.0(jiti@1.21.7) + typescript: 5.9.3 transitivePeerDependencies: - supports-color - typescript@5.8.3: {} + typescript@5.9.3: {} ufo@1.6.1: {} - undici-types@7.8.0: {} + undici-types@7.16.0: {} - unplugin-icons@22.1.0(svelte@5.35.6): + unplugin-icons@22.5.0(svelte@5.43.2): dependencies: '@antfu/install-pkg': 1.1.0 - '@iconify/utils': 2.3.0 - debug: 4.4.1 - local-pkg: 1.1.1 - unplugin: 2.3.5 + '@iconify/utils': 3.0.2 + debug: 4.4.3 + local-pkg: 1.1.2 + unplugin: 2.3.10 optionalDependencies: - svelte: 5.35.6 + svelte: 5.43.2 transitivePeerDependencies: - supports-color - unplugin@2.3.5: + unplugin@2.3.10: dependencies: + '@jridgewell/remapping': 2.3.5 acorn: 8.15.0 - picomatch: 4.0.2 + picomatch: 4.0.3 webpack-virtual-modules: 0.6.2 - update-browserslist-db@1.1.3(browserslist@4.25.1): + update-browserslist-db@1.1.4(browserslist@4.27.0): dependencies: - browserslist: 4.25.1 + browserslist: 4.27.0 escalade: 3.2.0 picocolors: 1.1.1 @@ -3662,20 +3772,25 @@ snapshots: util-deprecate@1.0.2: {} - uuid@11.1.0: {} + uuid@13.0.0: {} - vite@5.4.19(@types/node@24.0.13): + vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0): dependencies: - esbuild: 0.21.5 + esbuild: 0.25.11 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.44.2 + rollup: 4.52.5 + tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 24.0.13 + '@types/node': 24.9.2 fsevents: 2.3.3 + jiti: 1.21.7 + yaml: 2.8.0 - vitefu@1.1.1(vite@5.4.19(@types/node@24.0.13)): + vitefu@1.1.1(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)): optionalDependencies: - vite: 5.4.19(@types/node@24.0.13) + vite: 7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0) webpack-virtual-modules@0.6.2: {} @@ -3693,20 +3808,21 @@ snapshots: wrap-ansi@8.1.0: dependencies: - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 string-width: 5.1.2 - strip-ansi: 7.1.0 + strip-ansi: 7.1.2 xtend@4.0.2: {} yaml@1.10.2: {} - yaml@2.8.0: {} + yaml@2.8.0: + optional: true yocto-queue@0.1.0: {} yocto-queue@1.2.1: {} - zimmerframe@1.1.2: {} + zimmerframe@1.1.4: {} zod@3.25.76: {} From 7779910949ebc6faf870e4dd6cfbb1c455338841 Mon Sep 17 00:00:00 2001 From: static Date: Sun, 2 Nov 2025 23:09:01 +0900 Subject: [PATCH 04/23] =?UTF-8?q?tRPC=20=EC=B4=88=EA=B8=B0=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 4 +++- pnpm-lock.yaml | 36 ++++++++++++++++++++++++---- src/routes/trpc/[...trpc]/+server.ts | 15 ++++++++++++ src/trpc/client.ts | 23 ++++++++++++++++++ src/trpc/init.server.ts | 9 +++++++ src/trpc/router.server.ts | 13 ++++++++++ svelte.config.js | 9 +++---- 7 files changed, 97 insertions(+), 12 deletions(-) create mode 100644 src/routes/trpc/[...trpc]/+server.ts create mode 100644 src/trpc/client.ts create mode 100644 src/trpc/init.server.ts create mode 100644 src/trpc/router.server.ts diff --git a/package.json b/package.json index 5c7d0f3..4a8493b 100644 --- a/package.json +++ b/package.json @@ -21,8 +21,9 @@ "@sveltejs/adapter-node": "^5.4.0", "@sveltejs/kit": "^2.48.4", "@sveltejs/vite-plugin-svelte": "^6.2.1", + "@trpc/client": "^11.7.1", "@types/file-saver": "^2.0.7", - "@types/ms": "^2.1.0", + "@types/ms": "^0.7.34", "@types/node-schedule": "^2.1.8", "@types/pg": "^8.15.6", "autoprefixer": "^10.4.21", @@ -53,6 +54,7 @@ }, "dependencies": { "@fastify/busboy": "^3.2.0", + "@trpc/server": "^11.7.1", "argon2": "^0.44.0", "kysely": "^0.28.8", "ms": "^2.1.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index da0ae97..50affd7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@fastify/busboy': specifier: ^3.2.0 version: 3.2.0 + '@trpc/server': + specifier: ^11.7.1 + version: 11.7.1(typescript@5.9.3) argon2: specifier: ^0.44.0 version: 0.44.0 @@ -48,12 +51,15 @@ importers: '@sveltejs/vite-plugin-svelte': specifier: ^6.2.1 version: 6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)) + '@trpc/client': + specifier: ^11.7.1 + version: 11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3) '@types/file-saver': specifier: ^2.0.7 version: 2.0.7 '@types/ms': - specifier: ^2.1.0 - version: 2.1.0 + specifier: ^0.7.34 + version: 0.7.34 '@types/node-schedule': specifier: ^2.1.8 version: 2.1.8 @@ -612,6 +618,17 @@ packages: svelte: ^5.0.0 vite: ^6.3.0 || ^7.0.0 + '@trpc/client@11.7.1': + resolution: {integrity: sha512-uOnAjElKI892/U6aQMcBHYs3x7mme3Cvv1F87ytBL56rBvs7+DyK7r43zgaXKf13+GtPEI6ex5xjVUfyDW8XcQ==} + peerDependencies: + '@trpc/server': 11.7.1 + typescript: '>=5.7.2' + + '@trpc/server@11.7.1': + resolution: {integrity: sha512-N3U8LNLIP4g9C7LJ/sLkjuPHwqlvE3bnspzC4DEFVdvx2+usbn70P80E3wj5cjOTLhmhRiwJCSXhlB+MHfGeCw==} + peerDependencies: + typescript: '>=5.7.2' + '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} @@ -624,8 +641,8 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/ms@2.1.0': - resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/ms@0.7.34': + resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} '@types/node-schedule@2.1.8': resolution: {integrity: sha512-k00g6Yj/oUg/CDC+MeLHUzu0+OFxWbIqrFfDiLi6OPKxTujvpv29mHGM8GtKr7B+9Vv92FcK/8mRqi1DK5f3hA==} @@ -2450,6 +2467,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@trpc/client@11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3)': + dependencies: + '@trpc/server': 11.7.1(typescript@5.9.3) + typescript: 5.9.3 + + '@trpc/server@11.7.1(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + '@types/cookie@0.6.0': {} '@types/estree@1.0.8': {} @@ -2458,7 +2484,7 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/ms@2.1.0': {} + '@types/ms@0.7.34': {} '@types/node-schedule@2.1.8': dependencies: diff --git a/src/routes/trpc/[...trpc]/+server.ts b/src/routes/trpc/[...trpc]/+server.ts new file mode 100644 index 0000000..052ee09 --- /dev/null +++ b/src/routes/trpc/[...trpc]/+server.ts @@ -0,0 +1,15 @@ +import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; +import { createContext } from "$trpc/init.server"; +import { appRouter } from "$trpc/router.server"; +import type { RequestHandler } from "./$types"; + +const trpcHandler: RequestHandler = (event) => + fetchRequestHandler({ + endpoint: "/trpc", + req: event.request, + router: appRouter, + createContext: () => createContext(event), + }); + +export const GET = trpcHandler; +export const POST = trpcHandler; diff --git a/src/trpc/client.ts b/src/trpc/client.ts new file mode 100644 index 0000000..0596c63 --- /dev/null +++ b/src/trpc/client.ts @@ -0,0 +1,23 @@ +import { createTRPCClient, httpBatchLink } from "@trpc/client"; +import { browser } from "$app/environment"; +import type { AppRouter } from "./router.server"; + +const createClient = (fetch: typeof globalThis.fetch) => + createTRPCClient({ + links: [ + httpBatchLink({ + url: "/trpc", + fetch, + }), + ], + }); + +let browserClient: ReturnType; + +export const trpc = (fetch = globalThis.fetch) => { + const client = browserClient ?? createClient(fetch); + if (browser) { + browserClient ??= client; + } + return client; +}; diff --git a/src/trpc/init.server.ts b/src/trpc/init.server.ts new file mode 100644 index 0000000..a6af870 --- /dev/null +++ b/src/trpc/init.server.ts @@ -0,0 +1,9 @@ +import type { RequestEvent } from "@sveltejs/kit"; +import { initTRPC } from "@trpc/server"; + +export const createContext = (event: RequestEvent) => event; + +const t = initTRPC.context>>().create(); + +export const router = t.router; +export const publicProcedure = t.procedure; diff --git a/src/trpc/router.server.ts b/src/trpc/router.server.ts new file mode 100644 index 0000000..35aff92 --- /dev/null +++ b/src/trpc/router.server.ts @@ -0,0 +1,13 @@ +import type { RequestEvent } from "@sveltejs/kit"; +import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server"; +import { createContext, router } from "./init.server"; + +export const appRouter = router({ + // TODO +}); + +export const createCaller = (event: RequestEvent) => appRouter.createCaller(createContext(event)); + +export type AppRouter = typeof appRouter; +export type RouterInputs = inferRouterInputs; +export type RouterOutputs = inferRouterOutputs; diff --git a/svelte.config.js b/svelte.config.js index bbef2bb..4ffc844 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -3,15 +3,12 @@ import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; /** @type {import('@sveltejs/kit').Config} */ const config = { - // Consult https://svelte.dev/docs/kit/integrations - // for more information about preprocessors preprocess: vitePreprocess(), - kit: { - // 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(), + alias: { + $trpc: "./src/trpc", + }, }, }; From 640e12d2c398711e1118fb466fcd355028fcb15b Mon Sep 17 00:00:00 2001 From: static Date: Thu, 25 Dec 2025 16:50:41 +0900 Subject: [PATCH 05/23] =?UTF-8?q?tRPC=20Authorization=20=EB=AF=B8=EB=93=A4?= =?UTF-8?q?=EC=9B=A8=EC=96=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/server/middlewares/authenticate.ts | 2 +- src/lib/server/modules/auth.ts | 89 +++++++++++++--------- src/trpc/init.server.ts | 20 ++++- src/trpc/middlewares/authorize.ts | 36 +++++++++ 4 files changed, 109 insertions(+), 38 deletions(-) create mode 100644 src/trpc/middlewares/authorize.ts diff --git a/src/lib/server/middlewares/authenticate.ts b/src/lib/server/middlewares/authenticate.ts index 8585bce..ad8c585 100644 --- a/src/lib/server/middlewares/authenticate.ts +++ b/src/lib/server/middlewares/authenticate.ts @@ -4,7 +4,7 @@ import { authenticate, AuthenticationError } from "$lib/server/modules/auth"; export const authenticateMiddleware: Handle = async ({ event, resolve }) => { const { pathname, search } = event.url; - if (pathname === "/api/auth/login") { + if (pathname === "/api/auth/login" || pathname.startsWith("/trpc")) { return await resolve(event); } diff --git a/src/lib/server/modules/auth.ts b/src/lib/server/modules/auth.ts index d25033d..6ae3865 100644 --- a/src/lib/server/modules/auth.ts +++ b/src/lib/server/modules/auth.ts @@ -11,10 +11,17 @@ interface Session { clientId?: number; } -interface ClientSession extends Session { +export interface ClientSession extends Session { clientId: number; } +export type SessionPermission = + | "any" + | "notClient" + | "anyClient" + | "pendingClient" + | "activeClient"; + export class AuthenticationError extends Error { constructor( public status: 400 | 401, @@ -25,6 +32,16 @@ export class AuthenticationError extends Error { } } +export class AuthorizationError extends Error { + constructor( + public status: 403 | 500, + message: string, + ) { + super(message); + this.name = "AuthorizationError"; + } +} + export const startSession = async (userId: number, ip: string, userAgent: string) => { const { sessionId, sessionIdSigned } = await issueSessionId(32, env.session.secret); await createSession(userId, sessionId, ip, userAgent); @@ -52,34 +69,12 @@ export const authenticate = async (sessionIdSigned: string, ip: string, userAgen } }; -export async function authorize(locals: App.Locals, requiredPermission: "any"): Promise; - -export async function authorize( +export const authorizeInternal = async ( locals: App.Locals, - requiredPermission: "notClient", -): Promise; - -export async function authorize( - locals: App.Locals, - requiredPermission: "anyClient", -): Promise; - -export async function authorize( - locals: App.Locals, - requiredPermission: "pendingClient", -): Promise; - -export async function authorize( - locals: App.Locals, - requiredPermission: "activeClient", -): Promise; - -export async function authorize( - locals: App.Locals, - requiredPermission: "any" | "notClient" | "anyClient" | "pendingClient" | "activeClient", -): Promise { + requiredPermission: SessionPermission, +): Promise => { if (!locals.session) { - error(500, "Unauthenticated"); + throw new AuthorizationError(500, "Unauthenticated"); } const { id: sessionId, userId, clientId } = locals.session; @@ -89,39 +84,63 @@ export async function authorize( break; case "notClient": if (clientId) { - error(403, "Forbidden"); + throw new AuthorizationError(403, "Forbidden"); } break; case "anyClient": if (!clientId) { - error(403, "Forbidden"); + throw new AuthorizationError(403, "Forbidden"); } break; case "pendingClient": { if (!clientId) { - error(403, "Forbidden"); + throw new AuthorizationError(403, "Forbidden"); } const userClient = await getUserClient(userId, clientId); if (!userClient) { - error(500, "Invalid session id"); + throw new AuthorizationError(500, "Invalid session id"); } else if (userClient.state !== "pending") { - error(403, "Forbidden"); + throw new AuthorizationError(403, "Forbidden"); } break; } case "activeClient": { if (!clientId) { - error(403, "Forbidden"); + throw new AuthorizationError(403, "Forbidden"); } const userClient = await getUserClient(userId, clientId); if (!userClient) { - error(500, "Invalid session id"); + throw new AuthorizationError(500, "Invalid session id"); } else if (userClient.state !== "active") { - error(403, "Forbidden"); + throw new AuthorizationError(403, "Forbidden"); } break; } } return { sessionId, userId, clientId }; +}; + +export async function authorize( + locals: App.Locals, + requiredPermission: "any" | "notClient", +): Promise; + +export async function authorize( + locals: App.Locals, + requiredPermission: "anyClient" | "pendingClient" | "activeClient", +): Promise; + +export async function authorize( + locals: App.Locals, + requiredPermission: SessionPermission, +): Promise { + try { + return await authorizeInternal(locals, requiredPermission); + } catch (e) { + if (e instanceof AuthorizationError) { + error(e.status, e.message); + } + throw e; + } } diff --git a/src/trpc/init.server.ts b/src/trpc/init.server.ts index a6af870..15a35fa 100644 --- a/src/trpc/init.server.ts +++ b/src/trpc/init.server.ts @@ -1,9 +1,25 @@ import type { RequestEvent } from "@sveltejs/kit"; -import { initTRPC } from "@trpc/server"; +import { initTRPC, TRPCError } from "@trpc/server"; +import { authorizeMiddleware, authorizeClientMiddleware } from "./middlewares/authorize"; export const createContext = (event: RequestEvent) => event; -const t = initTRPC.context>>().create(); +export const t = initTRPC.context>>().create(); export const router = t.router; export const publicProcedure = t.procedure; + +const authedProcedure = publicProcedure.use(async ({ ctx, next }) => { + if (!ctx.locals.session) { + throw new TRPCError({ code: "UNAUTHORIZED" }); + } + return next(); +}); + +export const roleProcedure = { + any: authedProcedure.use(authorizeMiddleware("any")), + notClient: authedProcedure.use(authorizeMiddleware("notClient")), + anyClient: authedProcedure.use(authorizeClientMiddleware("anyClient")), + pendingClient: authedProcedure.use(authorizeClientMiddleware("pendingClient")), + activeClient: authedProcedure.use(authorizeClientMiddleware("activeClient")), +}; diff --git a/src/trpc/middlewares/authorize.ts b/src/trpc/middlewares/authorize.ts new file mode 100644 index 0000000..53413b3 --- /dev/null +++ b/src/trpc/middlewares/authorize.ts @@ -0,0 +1,36 @@ +import { TRPCError } from "@trpc/server"; +import { + AuthorizationError, + authorizeInternal, + type ClientSession, + type SessionPermission, +} from "$lib/server/modules/auth"; +import { t } from "../init.server"; + +const authorize = async (locals: App.Locals, requiredPermission: SessionPermission) => { + try { + return await authorizeInternal(locals, requiredPermission); + } catch (e) { + if (e instanceof AuthorizationError) { + throw new TRPCError({ + code: e.status === 403 ? "FORBIDDEN" : "INTERNAL_SERVER_ERROR", + message: e.message, + }); + } + throw e; + } +}; + +export const authorizeMiddleware = (requiredPermission: "any" | "notClient") => + t.middleware(async ({ ctx, next }) => { + const session = await authorize(ctx.locals, requiredPermission); + return next({ ctx: { session } }); + }); + +export const authorizeClientMiddleware = ( + requiredPermission: "anyClient" | "pendingClient" | "activeClient", +) => + t.middleware(async ({ ctx, next }) => { + const session = (await authorize(ctx.locals, requiredPermission)) as ClientSession; + return next({ ctx: { session } }); + }); From aa4a1a74eab20fc962369ed8bf82b38ea515ab39 Mon Sep 17 00:00:00 2001 From: static Date: Thu, 25 Dec 2025 18:59:41 +0900 Subject: [PATCH 06/23] =?UTF-8?q?/api/client=20=EC=95=84=EB=9E=98=EC=9D=98?= =?UTF-8?q?=20Endpoint=EB=93=A4=EC=9D=84=20tRPC=EB=A1=9C=20=EB=A7=88?= =?UTF-8?q?=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/server/db/client.ts | 16 --- src/lib/server/db/index.ts | 10 ++ src/lib/server/db/mek.ts | 13 -- src/lib/server/modules/mek.ts | 6 - src/lib/server/schemas/client.ts | 36 ------ src/lib/server/schemas/index.ts | 1 - src/lib/server/services/client.ts | 116 ------------------ src/lib/services/key.ts | 35 +++--- src/routes/api/client/list/+server.ts | 11 -- src/routes/api/client/register/+server.ts | 20 --- .../api/client/register/verify/+server.ts | 16 --- src/routes/api/client/status/+server.ts | 17 --- src/trpc/client.ts | 2 +- src/trpc/router.server.ts | 3 +- src/trpc/routers/client.ts | 96 +++++++++++++++ src/trpc/routers/index.ts | 1 + 16 files changed, 128 insertions(+), 271 deletions(-) create mode 100644 src/lib/server/db/index.ts delete mode 100644 src/lib/server/schemas/client.ts delete mode 100644 src/lib/server/services/client.ts delete mode 100644 src/routes/api/client/list/+server.ts delete mode 100644 src/routes/api/client/register/+server.ts delete mode 100644 src/routes/api/client/register/verify/+server.ts delete mode 100644 src/routes/api/client/status/+server.ts create mode 100644 src/trpc/routers/client.ts create mode 100644 src/trpc/routers/index.ts diff --git a/src/lib/server/db/client.ts b/src/lib/server/db/client.ts index 373357a..272df61 100644 --- a/src/lib/server/db/client.ts +++ b/src/lib/server/db/client.ts @@ -98,22 +98,6 @@ export const createUserClient = async (userId: number, clientId: number) => { } }; -export const getAllUserClients = async (userId: number) => { - const userClients = await db - .selectFrom("user_client") - .selectAll() - .where("user_id", "=", userId) - .execute(); - return userClients.map( - ({ user_id, client_id, state }) => - ({ - userId: user_id, - clientId: client_id, - state, - }) satisfies UserClient, - ); -}; - export const getUserClient = async (userId: number, clientId: number) => { const userClient = await db .selectFrom("user_client") diff --git a/src/lib/server/db/index.ts b/src/lib/server/db/index.ts new file mode 100644 index 0000000..5c21deb --- /dev/null +++ b/src/lib/server/db/index.ts @@ -0,0 +1,10 @@ +export * as CategoryRepo from "./category"; +export * as ClientRepo from "./client"; +export * as FileRepo from "./file"; +export * as HskRepo from "./hsk"; +export * as MediaRepo from "./media"; +export * as MekRepo from "./mek"; +export * as SessionRepo from "./session"; +export * as UserRepo from "./user"; + +export * from "./error"; diff --git a/src/lib/server/db/mek.ts b/src/lib/server/db/mek.ts index d6eecb0..65d00f8 100644 --- a/src/lib/server/db/mek.ts +++ b/src/lib/server/db/mek.ts @@ -60,19 +60,6 @@ export const registerInitialMek = async ( }); }; -export const getInitialMek = async (userId: number) => { - const mek = await db - .selectFrom("master_encryption_key") - .selectAll() - .where("user_id", "=", userId) - .where("version", "=", 1) - .limit(1) - .executeTakeFirst(); - return mek - ? ({ userId: mek.user_id, version: mek.version, state: mek.state } satisfies Mek) - : null; -}; - export const getAllValidClientMeks = async (userId: number, clientId: number) => { const clientMeks = await db .selectFrom("client_master_encryption_key") diff --git a/src/lib/server/modules/mek.ts b/src/lib/server/modules/mek.ts index 1605d75..23623f8 100644 --- a/src/lib/server/modules/mek.ts +++ b/src/lib/server/modules/mek.ts @@ -1,13 +1,7 @@ import { error } from "@sveltejs/kit"; import { getUserClientWithDetails } from "$lib/server/db/client"; -import { getInitialMek } from "$lib/server/db/mek"; import { verifySignature } from "$lib/server/modules/crypto"; -export const isInitialMekNeeded = async (userId: number) => { - const initialMek = await getInitialMek(userId); - return !initialMek; -}; - export const verifyClientEncMekSig = async ( userId: number, clientId: number, diff --git a/src/lib/server/schemas/client.ts b/src/lib/server/schemas/client.ts deleted file mode 100644 index 08a76b7..0000000 --- a/src/lib/server/schemas/client.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { z } from "zod"; - -export const clientListResponse = z.object({ - clients: z.array( - z.object({ - id: z.number().int().positive(), - state: z.enum(["pending", "active"]), - }), - ), -}); -export type ClientListResponse = z.output; - -export const clientRegisterRequest = z.object({ - encPubKey: z.string().base64().nonempty(), - sigPubKey: z.string().base64().nonempty(), -}); -export type ClientRegisterRequest = z.input; - -export const clientRegisterResponse = z.object({ - id: z.number().int().positive(), - challenge: z.string().base64().nonempty(), -}); -export type ClientRegisterResponse = z.output; - -export const clientRegisterVerifyRequest = z.object({ - id: z.number().int().positive(), - answerSig: z.string().base64().nonempty(), -}); -export type ClientRegisterVerifyRequest = z.input; - -export const clientStatusResponse = z.object({ - id: z.number().int().positive(), - state: z.enum(["pending", "active"]), - isInitialMekNeeded: z.boolean(), -}); -export type ClientStatusResponse = z.output; diff --git a/src/lib/server/schemas/index.ts b/src/lib/server/schemas/index.ts index 1fed0d0..b2d4fa5 100644 --- a/src/lib/server/schemas/index.ts +++ b/src/lib/server/schemas/index.ts @@ -1,6 +1,5 @@ export * from "./auth"; export * from "./category"; -export * from "./client"; export * from "./directory"; export * from "./file"; export * from "./hsk"; diff --git a/src/lib/server/services/client.ts b/src/lib/server/services/client.ts deleted file mode 100644 index 811e58c..0000000 --- a/src/lib/server/services/client.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { error } from "@sveltejs/kit"; -import { - createClient, - getClient, - getClientByPubKeys, - createUserClient, - getAllUserClients, - getUserClient, - setUserClientStateToPending, - registerUserClientChallenge, - consumeUserClientChallenge, -} from "$lib/server/db/client"; -import { IntegrityError } from "$lib/server/db/error"; -import { verifyPubKey, verifySignature, generateChallenge } from "$lib/server/modules/crypto"; -import { isInitialMekNeeded } from "$lib/server/modules/mek"; -import env from "$lib/server/loadenv"; - -export const getUserClientList = async (userId: number) => { - const userClients = await getAllUserClients(userId); - return { - userClients: userClients.map(({ clientId, state }) => ({ - id: clientId, - state: state as "pending" | "active", - })), - }; -}; - -const expiresAt = () => new Date(Date.now() + env.challenge.userClientExp); - -const createUserClientChallenge = async ( - ip: string, - userId: number, - clientId: number, - encPubKey: string, -) => { - const { answer, challenge } = await generateChallenge(32, encPubKey); - const { id } = await registerUserClientChallenge( - userId, - clientId, - answer.toString("base64"), - ip, - expiresAt(), - ); - return { id, challenge: challenge.toString("base64") }; -}; - -export const registerUserClient = async ( - userId: number, - ip: string, - encPubKey: string, - sigPubKey: string, -) => { - const client = await getClientByPubKeys(encPubKey, sigPubKey); - if (client) { - try { - await createUserClient(userId, client.id); - return await createUserClientChallenge(ip, userId, client.id, encPubKey); - } catch (e) { - if (e instanceof IntegrityError && e.message === "User client already exists") { - error(409, "Client already registered"); - } - throw e; - } - } else { - if (encPubKey === sigPubKey) { - error(400, "Same public keys"); - } else if (!verifyPubKey(encPubKey) || !verifyPubKey(sigPubKey)) { - error(400, "Invalid public key(s)"); - } - - try { - const { id: clientId } = await createClient(encPubKey, sigPubKey, userId); - return await createUserClientChallenge(ip, userId, clientId, encPubKey); - } catch (e) { - if (e instanceof IntegrityError && e.message === "Public key(s) already registered") { - error(409, "Public key(s) already used"); - } - throw e; - } - } -}; - -export const verifyUserClient = async ( - userId: number, - ip: string, - challengeId: number, - answerSig: string, -) => { - const challenge = await consumeUserClientChallenge(challengeId, userId, ip); - if (!challenge) { - error(403, "Invalid challenge answer"); - } - - const client = await getClient(challenge.clientId); - if (!client) { - error(500, "Invalid challenge answer"); - } else if ( - !verifySignature(Buffer.from(challenge.answer, "base64"), answerSig, client.sigPubKey) - ) { - error(403, "Invalid challenge answer signature"); - } - - await setUserClientStateToPending(userId, client.id); -}; - -export const getUserClientStatus = async (userId: number, clientId: number) => { - const userClient = await getUserClient(userId, clientId); - if (!userClient) { - error(500, "Invalid session id"); - } - - return { - state: userClient.state as "pending" | "active", - isInitialMekNeeded: await isInitialMekNeeded(userId), - }; -}; diff --git a/src/lib/services/key.ts b/src/lib/services/key.ts index cecd241..349197b 100644 --- a/src/lib/services/key.ts +++ b/src/lib/services/key.ts @@ -10,15 +10,13 @@ import { verifyMasterKeyWrapped, } from "$lib/modules/crypto"; import type { - ClientRegisterRequest, - ClientRegisterResponse, - ClientRegisterVerifyRequest, InitialHmacSecretRegisterRequest, MasterKeyListResponse, InitialMasterKeyRegisterRequest, } from "$lib/server/schemas"; import { requestSessionUpgrade } from "$lib/services/auth"; import { masterKeyStore, type ClientKeys } from "$lib/stores"; +import { useTRPC } from "$trpc/client"; export const requestClientRegistration = async ( encryptKeyBase64: string, @@ -26,21 +24,24 @@ export const requestClientRegistration = async ( verifyKeyBase64: string, signKey: CryptoKey, ) => { - let res = await callPostApi("/api/client/register", { - encPubKey: encryptKeyBase64, - sigPubKey: verifyKeyBase64, - }); - if (!res.ok) return false; + const trpc = useTRPC(); - const { id, challenge }: ClientRegisterResponse = await res.json(); - const answer = await decryptChallenge(challenge, decryptKey); - const answerSig = await signMessageRSA(answer, signKey); - - res = await callPostApi("/api/client/register/verify", { - id, - answerSig: encodeToBase64(answerSig), - }); - return res.ok; + try { + const { id, challenge } = await trpc.client.register.mutate({ + encPubKey: encryptKeyBase64, + sigPubKey: verifyKeyBase64, + }); + const answer = await decryptChallenge(challenge, decryptKey); + const answerSig = await signMessageRSA(answer, signKey); + await trpc.client.verify.mutate({ + id, + answerSig: encodeToBase64(answerSig), + }); + return true; + } catch { + // TODO: Error Handling + return false; + } }; export const requestClientRegistrationAndSessionUpgrade = async ( diff --git a/src/routes/api/client/list/+server.ts b/src/routes/api/client/list/+server.ts deleted file mode 100644 index 78193ee..0000000 --- a/src/routes/api/client/list/+server.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { json } from "@sveltejs/kit"; -import { authorize } from "$lib/server/modules/auth"; -import { clientListResponse, type ClientListResponse } from "$lib/server/schemas"; -import { getUserClientList } from "$lib/server/services/client"; -import type { RequestHandler } from "./$types"; - -export const GET: RequestHandler = async ({ locals }) => { - const { userId } = await authorize(locals, "anyClient"); - const { userClients } = await getUserClientList(userId); - return json(clientListResponse.parse({ clients: userClients } satisfies ClientListResponse)); -}; diff --git a/src/routes/api/client/register/+server.ts b/src/routes/api/client/register/+server.ts deleted file mode 100644 index 5ac2a53..0000000 --- a/src/routes/api/client/register/+server.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { error, json } from "@sveltejs/kit"; -import { authorize } from "$lib/server/modules/auth"; -import { - clientRegisterRequest, - clientRegisterResponse, - type ClientRegisterResponse, -} from "$lib/server/schemas"; -import { registerUserClient } from "$lib/server/services/client"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, request }) => { - const { userId } = await authorize(locals, "notClient"); - - const zodRes = clientRegisterRequest.safeParse(await request.json()); - if (!zodRes.success) error(400, "Invalid request body"); - const { encPubKey, sigPubKey } = zodRes.data; - - const { id, challenge } = await registerUserClient(userId, locals.ip, encPubKey, sigPubKey); - return json(clientRegisterResponse.parse({ id, challenge } satisfies ClientRegisterResponse)); -}; diff --git a/src/routes/api/client/register/verify/+server.ts b/src/routes/api/client/register/verify/+server.ts deleted file mode 100644 index 5ac9396..0000000 --- a/src/routes/api/client/register/verify/+server.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { error, text } from "@sveltejs/kit"; -import { authorize } from "$lib/server/modules/auth"; -import { clientRegisterVerifyRequest } from "$lib/server/schemas"; -import { verifyUserClient } from "$lib/server/services/client"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, request }) => { - const { userId } = await authorize(locals, "notClient"); - - const zodRes = clientRegisterVerifyRequest.safeParse(await request.json()); - if (!zodRes.success) error(400, "Invalid request body"); - const { id, answerSig } = zodRes.data; - - await verifyUserClient(userId, locals.ip, id, answerSig); - return text("Client verified", { headers: { "Content-Type": "text/plain" } }); -}; diff --git a/src/routes/api/client/status/+server.ts b/src/routes/api/client/status/+server.ts deleted file mode 100644 index a7ecc82..0000000 --- a/src/routes/api/client/status/+server.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { json } from "@sveltejs/kit"; -import { authorize } from "$lib/server/modules/auth"; -import { clientStatusResponse, type ClientStatusResponse } from "$lib/server/schemas"; -import { getUserClientStatus } from "$lib/server/services/client"; -import type { RequestHandler } from "./$types"; - -export const GET: RequestHandler = async ({ locals }) => { - const { userId, clientId } = await authorize(locals, "anyClient"); - const { state, isInitialMekNeeded } = await getUserClientStatus(userId, clientId); - return json( - clientStatusResponse.parse({ - id: clientId, - state, - isInitialMekNeeded, - } satisfies ClientStatusResponse), - ); -}; diff --git a/src/trpc/client.ts b/src/trpc/client.ts index 0596c63..433a743 100644 --- a/src/trpc/client.ts +++ b/src/trpc/client.ts @@ -14,7 +14,7 @@ const createClient = (fetch: typeof globalThis.fetch) => let browserClient: ReturnType; -export const trpc = (fetch = globalThis.fetch) => { +export const useTRPC = (fetch = globalThis.fetch) => { const client = browserClient ?? createClient(fetch); if (browser) { browserClient ??= client; diff --git a/src/trpc/router.server.ts b/src/trpc/router.server.ts index 35aff92..fdaee15 100644 --- a/src/trpc/router.server.ts +++ b/src/trpc/router.server.ts @@ -1,9 +1,10 @@ import type { RequestEvent } from "@sveltejs/kit"; import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server"; import { createContext, router } from "./init.server"; +import { clientRouter } from "./routers"; export const appRouter = router({ - // TODO + client: clientRouter, }); export const createCaller = (event: RequestEvent) => appRouter.createCaller(createContext(event)); diff --git a/src/trpc/routers/client.ts b/src/trpc/routers/client.ts new file mode 100644 index 0000000..8add385 --- /dev/null +++ b/src/trpc/routers/client.ts @@ -0,0 +1,96 @@ +import { TRPCError } from "@trpc/server"; +import { z } from "zod"; +import { ClientRepo, IntegrityError } from "$lib/server/db"; +import { verifyPubKey, verifySignature, generateChallenge } from "$lib/server/modules/crypto"; +import env from "$lib/server/loadenv"; +import { router, roleProcedure } from "../init.server"; + +const createUserClientChallenge = async ( + ip: string, + userId: number, + clientId: number, + encPubKey: string, +) => { + const { answer, challenge } = await generateChallenge(32, encPubKey); + const { id } = await ClientRepo.registerUserClientChallenge( + userId, + clientId, + answer.toString("base64"), + ip, + new Date(Date.now() + env.challenge.userClientExp), + ); + return { id, challenge: challenge.toString("base64") }; +}; + +const clientRouter = router({ + register: roleProcedure["notClient"] + .input( + z.object({ + encPubKey: z.string().base64().nonempty(), + sigPubKey: z.string().base64().nonempty(), + }), + ) + .mutation(async ({ ctx, input }) => { + const { userId } = ctx.session; + const { encPubKey, sigPubKey } = input; + const client = await ClientRepo.getClientByPubKeys(encPubKey, sigPubKey); + if (client) { + try { + await ClientRepo.createUserClient(userId, client.id); + return await createUserClientChallenge(ctx.locals.ip, userId, client.id, encPubKey); + } catch (e) { + if (e instanceof IntegrityError && e.message === "User client already exists") { + throw new TRPCError({ code: "CONFLICT", message: "Client already registered" }); + } + throw e; + } + } else { + if (encPubKey === sigPubKey) { + throw new TRPCError({ code: "BAD_REQUEST", message: "Same public keys" }); + } else if (!verifyPubKey(encPubKey) || !verifyPubKey(sigPubKey)) { + throw new TRPCError({ code: "BAD_REQUEST", message: "Invalid public key(s)" }); + } + + try { + const { id: clientId } = await ClientRepo.createClient(encPubKey, sigPubKey, userId); + return await createUserClientChallenge(ctx.locals.ip, userId, clientId, encPubKey); + } catch (e) { + if (e instanceof IntegrityError && e.message === "Public key(s) already registered") { + throw new TRPCError({ code: "CONFLICT", message: "Public key(s) already used" }); + } + throw e; + } + } + }), + + verify: roleProcedure["notClient"] + .input( + z.object({ + id: z.number().int().positive(), + answerSig: z.string().base64().nonempty(), + }), + ) + .mutation(async ({ ctx, input }) => { + const challenge = await ClientRepo.consumeUserClientChallenge( + input.id, + ctx.session.userId, + ctx.locals.ip, + ); + if (!challenge) { + throw new TRPCError({ code: "FORBIDDEN", message: "Invalid challenge answer" }); + } + + const client = await ClientRepo.getClient(challenge.clientId); + if (!client) { + throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Invalid challenge answer" }); + } else if ( + !verifySignature(Buffer.from(challenge.answer, "base64"), input.answerSig, client.sigPubKey) + ) { + throw new TRPCError({ code: "FORBIDDEN", message: "Invalid challenge answer signature" }); + } + + await ClientRepo.setUserClientStateToPending(ctx.session.userId, client.id); + }), +}); + +export default clientRouter; diff --git a/src/trpc/routers/index.ts b/src/trpc/routers/index.ts new file mode 100644 index 0000000..6f13a73 --- /dev/null +++ b/src/trpc/routers/index.ts @@ -0,0 +1 @@ +export { default as clientRouter } from "./client"; From 208252f6b2ad1655f0c65a7f1eeeda3a711ae7d5 Mon Sep 17 00:00:00 2001 From: static Date: Thu, 25 Dec 2025 20:00:15 +0900 Subject: [PATCH 07/23] =?UTF-8?q?/api/hsk,=20/api/mek,=20/api/user=20?= =?UTF-8?q?=EC=95=84=EB=9E=98=EC=9D=98=20Endpoint=EB=93=A4=EC=9D=84=20tRPC?= =?UTF-8?q?=EB=A1=9C=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/server/modules/mek.ts | 19 ------ src/lib/server/schemas/hsk.ts | 19 ------ src/lib/server/schemas/index.ts | 3 - src/lib/server/schemas/mek.ts | 19 ------ src/lib/server/schemas/user.ts | 12 ---- src/lib/server/services/hsk.ts | 31 --------- src/lib/server/services/mek.ts | 38 ----------- src/lib/server/services/user.ts | 15 ----- src/lib/services/key.ts | 56 +++++++++++------ .../(main)/directory/[[id]]/service.svelte.ts | 16 +++-- src/routes/(main)/menu/+page.ts | 14 ++--- src/routes/api/hsk/list/+server.ts | 20 ------ .../api/hsk/register/initial/+server.ts | 16 ----- src/routes/api/mek/list/+server.ts | 20 ------ .../api/mek/register/initial/+server.ts | 16 ----- src/routes/api/user/+server.ts | 11 ---- src/routes/api/user/changeNickname/+server.ts | 16 ----- src/trpc/router.server.ts | 5 +- src/trpc/routers/hsk.ts | 41 ++++++++++++ src/trpc/routers/index.ts | 3 + src/trpc/routers/mek.ts | 63 +++++++++++++++++++ src/trpc/routers/user.ts | 27 ++++++++ 22 files changed, 192 insertions(+), 288 deletions(-) delete mode 100644 src/lib/server/modules/mek.ts delete mode 100644 src/lib/server/schemas/hsk.ts delete mode 100644 src/lib/server/schemas/mek.ts delete mode 100644 src/lib/server/schemas/user.ts delete mode 100644 src/lib/server/services/hsk.ts delete mode 100644 src/lib/server/services/mek.ts delete mode 100644 src/lib/server/services/user.ts delete mode 100644 src/routes/api/hsk/list/+server.ts delete mode 100644 src/routes/api/hsk/register/initial/+server.ts delete mode 100644 src/routes/api/mek/list/+server.ts delete mode 100644 src/routes/api/mek/register/initial/+server.ts delete mode 100644 src/routes/api/user/+server.ts delete mode 100644 src/routes/api/user/changeNickname/+server.ts create mode 100644 src/trpc/routers/hsk.ts create mode 100644 src/trpc/routers/mek.ts create mode 100644 src/trpc/routers/user.ts diff --git a/src/lib/server/modules/mek.ts b/src/lib/server/modules/mek.ts deleted file mode 100644 index 23623f8..0000000 --- a/src/lib/server/modules/mek.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { error } from "@sveltejs/kit"; -import { getUserClientWithDetails } from "$lib/server/db/client"; -import { verifySignature } from "$lib/server/modules/crypto"; - -export const verifyClientEncMekSig = async ( - userId: number, - clientId: number, - version: number, - encMek: string, - encMekSig: string, -) => { - const userClient = await getUserClientWithDetails(userId, clientId); - if (!userClient) { - error(500, "Invalid session id"); - } - - const data = JSON.stringify({ version, key: encMek }); - return verifySignature(Buffer.from(data), encMekSig, userClient.sigPubKey); -}; diff --git a/src/lib/server/schemas/hsk.ts b/src/lib/server/schemas/hsk.ts deleted file mode 100644 index 6f6b428..0000000 --- a/src/lib/server/schemas/hsk.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { z } from "zod"; - -export const hmacSecretListResponse = z.object({ - hsks: z.array( - z.object({ - version: z.number().int().positive(), - state: z.enum(["active"]), - mekVersion: z.number().int().positive(), - hsk: z.string().base64().nonempty(), - }), - ), -}); -export type HmacSecretListResponse = z.output; - -export const initialHmacSecretRegisterRequest = z.object({ - mekVersion: z.number().int().positive(), - hsk: z.string().base64().nonempty(), -}); -export type InitialHmacSecretRegisterRequest = z.input; diff --git a/src/lib/server/schemas/index.ts b/src/lib/server/schemas/index.ts index b2d4fa5..d9ddce7 100644 --- a/src/lib/server/schemas/index.ts +++ b/src/lib/server/schemas/index.ts @@ -2,6 +2,3 @@ export * from "./auth"; export * from "./category"; export * from "./directory"; export * from "./file"; -export * from "./hsk"; -export * from "./mek"; -export * from "./user"; diff --git a/src/lib/server/schemas/mek.ts b/src/lib/server/schemas/mek.ts deleted file mode 100644 index 3d6f468..0000000 --- a/src/lib/server/schemas/mek.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { z } from "zod"; - -export const masterKeyListResponse = z.object({ - meks: z.array( - z.object({ - version: z.number().int().positive(), - state: z.enum(["active", "retired"]), - mek: z.string().base64().nonempty(), - mekSig: z.string().base64().nonempty(), - }), - ), -}); -export type MasterKeyListResponse = z.output; - -export const initialMasterKeyRegisterRequest = z.object({ - mek: z.string().base64().nonempty(), - mekSig: z.string().base64().nonempty(), -}); -export type InitialMasterKeyRegisterRequest = z.input; diff --git a/src/lib/server/schemas/user.ts b/src/lib/server/schemas/user.ts deleted file mode 100644 index 9aec819..0000000 --- a/src/lib/server/schemas/user.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { z } from "zod"; - -export const userInfoResponse = z.object({ - email: z.string().email(), - nickname: z.string().nonempty(), -}); -export type UserInfoResponse = z.output; - -export const nicknameChangeRequest = z.object({ - newNickname: z.string().trim().min(2).max(8), -}); -export type NicknameChangeRequest = z.input; diff --git a/src/lib/server/services/hsk.ts b/src/lib/server/services/hsk.ts deleted file mode 100644 index c381c51..0000000 --- a/src/lib/server/services/hsk.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { error } from "@sveltejs/kit"; -import { IntegrityError } from "$lib/server/db/error"; -import { registerInitialHsk, getAllValidHsks } from "$lib/server/db/hsk"; - -export const getHskList = async (userId: number) => { - const hsks = await getAllValidHsks(userId); - return { - encHsks: hsks.map(({ version, state, mekVersion, encHsk }) => ({ - version, - state, - mekVersion, - encHsk, - })), - }; -}; - -export const registerInitialActiveHsk = async ( - userId: number, - createdBy: number, - mekVersion: number, - encHsk: string, -) => { - try { - await registerInitialHsk(userId, createdBy, mekVersion, encHsk); - } catch (e) { - if (e instanceof IntegrityError && e.message === "HSK already registered") { - error(409, "Initial HSK already registered"); - } - throw e; - } -}; diff --git a/src/lib/server/services/mek.ts b/src/lib/server/services/mek.ts deleted file mode 100644 index 097906a..0000000 --- a/src/lib/server/services/mek.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { error } from "@sveltejs/kit"; -import { setUserClientStateToActive } from "$lib/server/db/client"; -import { IntegrityError } from "$lib/server/db/error"; -import { registerInitialMek, getAllValidClientMeks } from "$lib/server/db/mek"; -import { verifyClientEncMekSig } from "$lib/server/modules/mek"; - -export const getClientMekList = async (userId: number, clientId: number) => { - const clientMeks = await getAllValidClientMeks(userId, clientId); - return { - encMeks: clientMeks.map(({ version, state, encMek, encMekSig }) => ({ - version, - state, - encMek, - encMekSig, - })), - }; -}; - -export const registerInitialActiveMek = async ( - userId: number, - createdBy: number, - encMek: string, - encMekSig: string, -) => { - if (!(await verifyClientEncMekSig(userId, createdBy, 1, encMek, encMekSig))) { - error(400, "Invalid signature"); - } - - try { - await registerInitialMek(userId, createdBy, encMek, encMekSig); - await setUserClientStateToActive(userId, createdBy); - } catch (e) { - if (e instanceof IntegrityError && e.message === "MEK already registered") { - error(409, "Initial MEK already registered"); - } - throw e; - } -}; diff --git a/src/lib/server/services/user.ts b/src/lib/server/services/user.ts deleted file mode 100644 index 5c06458..0000000 --- a/src/lib/server/services/user.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { error } from "@sveltejs/kit"; -import { getUser, setUserNickname } from "$lib/server/db/user"; - -export const getUserInformation = async (userId: number) => { - const user = await getUser(userId); - if (!user) { - error(500, "Invalid session id"); - } - - return { email: user.email, nickname: user.nickname }; -}; - -export const changeNickname = async (userId: number, nickname: string) => { - await setUserNickname(userId, nickname); -}; diff --git a/src/lib/services/key.ts b/src/lib/services/key.ts index 349197b..6149829 100644 --- a/src/lib/services/key.ts +++ b/src/lib/services/key.ts @@ -1,4 +1,4 @@ -import { callGetApi, callPostApi } from "$lib/hooks"; +import { TRPCClientError } from "@trpc/client"; import { storeMasterKeys } from "$lib/indexedDB"; import { encodeToBase64, @@ -9,11 +9,6 @@ import { signMasterKeyWrapped, verifyMasterKeyWrapped, } from "$lib/modules/crypto"; -import type { - InitialHmacSecretRegisterRequest, - MasterKeyListResponse, - InitialMasterKeyRegisterRequest, -} from "$lib/server/schemas"; import { requestSessionUpgrade } from "$lib/services/auth"; import { masterKeyStore, type ClientKeys } from "$lib/stores"; import { useTRPC } from "$trpc/client"; @@ -74,10 +69,16 @@ export const requestClientRegistrationAndSessionUpgrade = async ( }; export const requestMasterKeyDownload = async (decryptKey: CryptoKey, verifyKey: CryptoKey) => { - const res = await callGetApi("/api/mek/list"); - if (!res.ok) return false; + const trpc = useTRPC(); + + let masterKeysWrapped; + try { + masterKeysWrapped = await trpc.mek.list.query(); + } catch { + // TODO: Error Handling + return false; + } - const { meks: masterKeysWrapped }: MasterKeyListResponse = await res.json(); const masterKeys = await Promise.all( masterKeysWrapped.map( async ({ version, state, mek: masterKeyWrapped, mekSig: masterKeyWrappedSig }) => { @@ -109,17 +110,32 @@ export const requestInitialMasterKeyAndHmacSecretRegistration = async ( hmacSecretWrapped: string, signKey: CryptoKey, ) => { - let res = await callPostApi("/api/mek/register/initial", { - mek: masterKeyWrapped, - mekSig: await signMasterKeyWrapped(masterKeyWrapped, 1, signKey), - }); - if (!res.ok) { - return res.status === 403 || res.status === 409; + const trpc = useTRPC(); + + try { + await trpc.mek.registerInitial.mutate({ + mek: masterKeyWrapped, + mekSig: await signMasterKeyWrapped(masterKeyWrapped, 1, signKey), + }); + } catch (e) { + if ( + e instanceof TRPCClientError && + (e.data?.code === "FORBIDDEN" || e.data?.code === "CONFLICT") + ) { + return true; + } + // TODO: Error Handling + return false; } - res = await callPostApi("/api/hsk/register/initial", { - mekVersion: 1, - hsk: hmacSecretWrapped, - }); - return res.ok; + try { + await trpc.hsk.registerInitial.mutate({ + mekVersion: 1, + hsk: hmacSecretWrapped, + }); + return true; + } catch { + // TODO: Error Handling + return false; + } }; diff --git a/src/routes/(main)/directory/[[id]]/service.svelte.ts b/src/routes/(main)/directory/[[id]]/service.svelte.ts index ba5fc4a..40394f7 100644 --- a/src/routes/(main)/directory/[[id]]/service.svelte.ts +++ b/src/routes/(main)/directory/[[id]]/service.svelte.ts @@ -1,5 +1,5 @@ import { getContext, setContext } from "svelte"; -import { callGetApi, callPostApi } from "$lib/hooks"; +import { callPostApi } from "$lib/hooks"; import { storeHmacSecrets } from "$lib/indexedDB"; import { generateDataKey, wrapDataKey, unwrapHmacSecret, encryptString } from "$lib/modules/crypto"; import { @@ -13,10 +13,10 @@ import type { DirectoryRenameRequest, DirectoryCreateRequest, FileRenameRequest, - HmacSecretListResponse, DirectoryDeleteResponse, } from "$lib/server/schemas"; import { hmacSecretStore, type MasterKey, type HmacSecret } from "$lib/stores"; +import { useTRPC } from "$trpc/client"; export interface SelectedEntry { type: "directory" | "file"; @@ -40,10 +40,16 @@ export const useContext = () => { export const requestHmacSecretDownload = async (masterKey: CryptoKey) => { // TODO: MEK rotation - const res = await callGetApi("/api/hsk/list"); - if (!res.ok) return false; + const trpc = useTRPC(); + + let hmacSecretsWrapped; + try { + hmacSecretsWrapped = await trpc.hsk.list.query(); + } catch { + // TODO: Error Handling + return false; + } - const { hsks: hmacSecretsWrapped }: HmacSecretListResponse = await res.json(); const hmacSecrets = await Promise.all( hmacSecretsWrapped.map(async ({ version, state, hsk: hmacSecretWrapped }) => { const { hmacSecret } = await unwrapHmacSecret(hmacSecretWrapped, masterKey); diff --git a/src/routes/(main)/menu/+page.ts b/src/routes/(main)/menu/+page.ts index 30a265a..b1582e7 100644 --- a/src/routes/(main)/menu/+page.ts +++ b/src/routes/(main)/menu/+page.ts @@ -1,14 +1,14 @@ import { error } from "@sveltejs/kit"; -import { callGetApi } from "$lib/hooks"; -import type { UserInfoResponse } from "$lib/server/schemas"; +import { useTRPC } from "$trpc/client"; import type { PageLoad } from "./$types"; export const load: PageLoad = async ({ fetch }) => { - const res = await callGetApi("/api/user", fetch); - if (!res.ok) { + const trpc = useTRPC(fetch); + + try { + const { nickname } = await trpc.user.info.query(); + return { nickname }; + } catch { error(500, "Internal server error"); } - - const { nickname }: UserInfoResponse = await res.json(); - return { nickname }; }; diff --git a/src/routes/api/hsk/list/+server.ts b/src/routes/api/hsk/list/+server.ts deleted file mode 100644 index 50957a3..0000000 --- a/src/routes/api/hsk/list/+server.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { json } from "@sveltejs/kit"; -import { authorize } from "$lib/server/modules/auth"; -import { hmacSecretListResponse, type HmacSecretListResponse } from "$lib/server/schemas"; -import { getHskList } from "$lib/server/services/hsk"; -import type { RequestHandler } from "./$types"; - -export const GET: RequestHandler = async ({ locals }) => { - const { userId } = await authorize(locals, "activeClient"); - const { encHsks } = await getHskList(userId); - return json( - hmacSecretListResponse.parse({ - hsks: encHsks.map(({ version, state, mekVersion, encHsk }) => ({ - version, - state, - mekVersion, - hsk: encHsk, - })), - } satisfies HmacSecretListResponse), - ); -}; diff --git a/src/routes/api/hsk/register/initial/+server.ts b/src/routes/api/hsk/register/initial/+server.ts deleted file mode 100644 index 8b32952..0000000 --- a/src/routes/api/hsk/register/initial/+server.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { error, text } from "@sveltejs/kit"; -import { authorize } from "$lib/server/modules/auth"; -import { initialHmacSecretRegisterRequest } from "$lib/server/schemas"; -import { registerInitialActiveHsk } from "$lib/server/services/hsk"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, request }) => { - const { userId, clientId } = await authorize(locals, "activeClient"); - - const zodRes = initialHmacSecretRegisterRequest.safeParse(await request.json()); - if (!zodRes.success) error(400, "Invalid request body"); - const { mekVersion, hsk } = zodRes.data; - - await registerInitialActiveHsk(userId, clientId, mekVersion, hsk); - return text("HSK registered", { headers: { "Content-Type": "text/plain" } }); -}; diff --git a/src/routes/api/mek/list/+server.ts b/src/routes/api/mek/list/+server.ts deleted file mode 100644 index b3df9fe..0000000 --- a/src/routes/api/mek/list/+server.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { json } from "@sveltejs/kit"; -import { authorize } from "$lib/server/modules/auth"; -import { masterKeyListResponse, type MasterKeyListResponse } from "$lib/server/schemas"; -import { getClientMekList } from "$lib/server/services/mek"; -import type { RequestHandler } from "./$types"; - -export const GET: RequestHandler = async ({ locals }) => { - const { userId, clientId } = await authorize(locals, "activeClient"); - const { encMeks } = await getClientMekList(userId, clientId); - return json( - masterKeyListResponse.parse({ - meks: encMeks.map(({ version, state, encMek, encMekSig }) => ({ - version, - state, - mek: encMek, - mekSig: encMekSig, - })), - } satisfies MasterKeyListResponse), - ); -}; diff --git a/src/routes/api/mek/register/initial/+server.ts b/src/routes/api/mek/register/initial/+server.ts deleted file mode 100644 index bb761e2..0000000 --- a/src/routes/api/mek/register/initial/+server.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { error, text } from "@sveltejs/kit"; -import { authorize } from "$lib/server/modules/auth"; -import { initialMasterKeyRegisterRequest } from "$lib/server/schemas"; -import { registerInitialActiveMek } from "$lib/server/services/mek"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, request }) => { - const { userId, clientId } = await authorize(locals, "pendingClient"); - - const zodRes = initialMasterKeyRegisterRequest.safeParse(await request.json()); - if (!zodRes.success) error(400, "Invalid request body"); - const { mek, mekSig } = zodRes.data; - - await registerInitialActiveMek(userId, clientId, mek, mekSig); - return text("MEK registered", { headers: { "Content-Type": "text/plain" } }); -}; diff --git a/src/routes/api/user/+server.ts b/src/routes/api/user/+server.ts deleted file mode 100644 index b10b13e..0000000 --- a/src/routes/api/user/+server.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { json } from "@sveltejs/kit"; -import { authorize } from "$lib/server/modules/auth"; -import { userInfoResponse, type UserInfoResponse } from "$lib/server/schemas"; -import { getUserInformation } from "$lib/server/services/user"; -import type { RequestHandler } from "./$types"; - -export const GET: RequestHandler = async ({ locals }) => { - const { userId } = await authorize(locals, "any"); - const { email, nickname } = await getUserInformation(userId); - return json(userInfoResponse.parse({ email, nickname } satisfies UserInfoResponse)); -}; diff --git a/src/routes/api/user/changeNickname/+server.ts b/src/routes/api/user/changeNickname/+server.ts deleted file mode 100644 index ad651ac..0000000 --- a/src/routes/api/user/changeNickname/+server.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { error, text } from "@sveltejs/kit"; -import { authorize } from "$lib/server/modules/auth"; -import { nicknameChangeRequest } from "$lib/server/schemas"; -import { changeNickname } from "$lib/server/services/user"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, request }) => { - const { userId } = await authorize(locals, "any"); - - const zodRes = nicknameChangeRequest.safeParse(await request.json()); - if (!zodRes.success) error(400, "Invalid request body"); - const { newNickname } = zodRes.data; - - await changeNickname(userId, newNickname); - return text("Nickname changed", { headers: { "Content-Type": "text/plain" } }); -}; diff --git a/src/trpc/router.server.ts b/src/trpc/router.server.ts index fdaee15..3c44bea 100644 --- a/src/trpc/router.server.ts +++ b/src/trpc/router.server.ts @@ -1,10 +1,13 @@ import type { RequestEvent } from "@sveltejs/kit"; import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server"; import { createContext, router } from "./init.server"; -import { clientRouter } from "./routers"; +import { clientRouter, hskRouter, mekRouter, userRouter } from "./routers"; export const appRouter = router({ client: clientRouter, + hsk: hskRouter, + mek: mekRouter, + user: userRouter, }); export const createCaller = (event: RequestEvent) => appRouter.createCaller(createContext(event)); diff --git a/src/trpc/routers/hsk.ts b/src/trpc/routers/hsk.ts new file mode 100644 index 0000000..eed9d25 --- /dev/null +++ b/src/trpc/routers/hsk.ts @@ -0,0 +1,41 @@ +import { TRPCError } from "@trpc/server"; +import { z } from "zod"; +import { HskRepo, IntegrityError } from "$lib/server/db"; +import { router, roleProcedure } from "../init.server"; + +const hskRouter = router({ + list: roleProcedure["activeClient"].query(async ({ ctx }) => { + const hsks = await HskRepo.getAllValidHsks(ctx.session.userId); + return hsks.map(({ version, state, mekVersion, encHsk }) => ({ + version, + state, + mekVersion, + hsk: encHsk, + })); + }), + + registerInitial: roleProcedure["activeClient"] + .input( + z.object({ + mekVersion: z.number().int().positive(), + hsk: z.string().base64().nonempty(), + }), + ) + .mutation(async ({ ctx, input }) => { + try { + await HskRepo.registerInitialHsk( + ctx.session.userId, + ctx.session.clientId, + input.mekVersion, + input.hsk, + ); + } catch (e) { + if (e instanceof IntegrityError && e.message === "HSK already registered") { + throw new TRPCError({ code: "CONFLICT", message: "Initial HSK already registered" }); + } + throw e; + } + }), +}); + +export default hskRouter; diff --git a/src/trpc/routers/index.ts b/src/trpc/routers/index.ts index 6f13a73..26ac7b2 100644 --- a/src/trpc/routers/index.ts +++ b/src/trpc/routers/index.ts @@ -1 +1,4 @@ export { default as clientRouter } from "./client"; +export { default as hskRouter } from "./hsk"; +export { default as mekRouter } from "./mek"; +export { default as userRouter } from "./user"; diff --git a/src/trpc/routers/mek.ts b/src/trpc/routers/mek.ts new file mode 100644 index 0000000..fa264a5 --- /dev/null +++ b/src/trpc/routers/mek.ts @@ -0,0 +1,63 @@ +import { TRPCError } from "@trpc/server"; +import { z } from "zod"; +import { ClientRepo, MekRepo, IntegrityError } from "$lib/server/db"; +import { verifySignature } from "$lib/server/modules/crypto"; +import { router, roleProcedure } from "../init.server"; + +const verifyClientEncMekSig = async ( + userId: number, + clientId: number, + version: number, + encMek: string, + encMekSig: string, +) => { + const userClient = await ClientRepo.getUserClientWithDetails(userId, clientId); + if (!userClient) { + throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Invalid session id" }); + } + + const data = JSON.stringify({ version, key: encMek }); + return verifySignature(Buffer.from(data), encMekSig, userClient.sigPubKey); +}; + +const mekRouter = router({ + list: roleProcedure["activeClient"].query(async ({ ctx }) => { + const clientMeks = await MekRepo.getAllValidClientMeks( + ctx.session.userId, + ctx.session.clientId, + ); + return clientMeks.map(({ version, state, encMek, encMekSig }) => ({ + version, + state, + mek: encMek, + mekSig: encMekSig, + })); + }), + + registerInitial: roleProcedure["pendingClient"] + .input( + z.object({ + mek: z.string().base64().nonempty(), + mekSig: z.string().base64().nonempty(), + }), + ) + .mutation(async ({ ctx, input }) => { + const { userId, clientId } = ctx.session; + const { mek, mekSig } = input; + if (!(await verifyClientEncMekSig(userId, clientId, 1, mek, mekSig))) { + throw new TRPCError({ code: "BAD_REQUEST", message: "Invalid signature" }); + } + + try { + await MekRepo.registerInitialMek(userId, clientId, mek, mekSig); + await ClientRepo.setUserClientStateToActive(userId, clientId); + } catch (e) { + if (e instanceof IntegrityError && e.message === "MEK already registered") { + throw new TRPCError({ code: "CONFLICT", message: "Initial MEK already registered" }); + } + throw e; + } + }), +}); + +export default mekRouter; diff --git a/src/trpc/routers/user.ts b/src/trpc/routers/user.ts new file mode 100644 index 0000000..37b2460 --- /dev/null +++ b/src/trpc/routers/user.ts @@ -0,0 +1,27 @@ +import { TRPCError } from "@trpc/server"; +import { z } from "zod"; +import { UserRepo } from "$lib/server/db"; +import { router, roleProcedure } from "../init.server"; + +const userRouter = router({ + info: roleProcedure.any.query(async ({ ctx }) => { + const user = await UserRepo.getUser(ctx.session.userId); + if (!user) { + throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Invalid session id" }); + } + + return { email: user.email, nickname: user.nickname }; + }), + + changeNickname: roleProcedure.any + .input( + z.object({ + newNickname: z.string().trim().min(2).max(8), + }), + ) + .mutation(async ({ ctx, input }) => { + await UserRepo.setUserNickname(ctx.session.userId, input.newNickname); + }), +}); + +export default userRouter; From a08ddf2c09d24d65fb07d6509cda6473a22d9371 Mon Sep 17 00:00:00 2001 From: static Date: Thu, 25 Dec 2025 20:22:58 +0900 Subject: [PATCH 08/23] =?UTF-8?q?tRPC=20Endpoint=EB=A5=BC=20/api/trpc?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/server/middlewares/authenticate.ts | 4 ++-- src/routes/{trpc/[...trpc] => api/trpc/[trpc]}/+server.ts | 2 +- src/trpc/client.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename src/routes/{trpc/[...trpc] => api/trpc/[trpc]}/+server.ts (94%) diff --git a/src/lib/server/middlewares/authenticate.ts b/src/lib/server/middlewares/authenticate.ts index ad8c585..cc635b4 100644 --- a/src/lib/server/middlewares/authenticate.ts +++ b/src/lib/server/middlewares/authenticate.ts @@ -4,7 +4,7 @@ import { authenticate, AuthenticationError } from "$lib/server/modules/auth"; export const authenticateMiddleware: Handle = async ({ event, resolve }) => { const { pathname, search } = event.url; - if (pathname === "/api/auth/login" || pathname.startsWith("/trpc")) { + if (pathname === "/api/auth/login") { return await resolve(event); } @@ -24,7 +24,7 @@ export const authenticateMiddleware: Handle = async ({ event, resolve }) => { }); } catch (e) { if (e instanceof AuthenticationError) { - if (pathname === "/auth/login") { + if (pathname === "/auth/login" || pathname.startsWith("/api/trpc")) { return await resolve(event); } else if (pathname.startsWith("/api")) { error(e.status, e.message); diff --git a/src/routes/trpc/[...trpc]/+server.ts b/src/routes/api/trpc/[trpc]/+server.ts similarity index 94% rename from src/routes/trpc/[...trpc]/+server.ts rename to src/routes/api/trpc/[trpc]/+server.ts index 052ee09..ec1320a 100644 --- a/src/routes/trpc/[...trpc]/+server.ts +++ b/src/routes/api/trpc/[trpc]/+server.ts @@ -5,7 +5,7 @@ import type { RequestHandler } from "./$types"; const trpcHandler: RequestHandler = (event) => fetchRequestHandler({ - endpoint: "/trpc", + endpoint: "/api/trpc", req: event.request, router: appRouter, createContext: () => createContext(event), diff --git a/src/trpc/client.ts b/src/trpc/client.ts index 433a743..dbf4e80 100644 --- a/src/trpc/client.ts +++ b/src/trpc/client.ts @@ -6,7 +6,7 @@ const createClient = (fetch: typeof globalThis.fetch) => createTRPCClient({ links: [ httpBatchLink({ - url: "/trpc", + url: "/api/trpc", fetch, }), ], From 6d95059450ac554da54c55235e18a371c017a1bd Mon Sep 17 00:00:00 2001 From: static Date: Thu, 25 Dec 2025 22:45:55 +0900 Subject: [PATCH 09/23] =?UTF-8?q?/api/category,=20/api/directory,=20/api/f?= =?UTF-8?q?ile=20=EC=95=84=EB=9E=98=EC=9D=98=20=EB=8C=80=EB=B6=80=EB=B6=84?= =?UTF-8?q?=EC=9D=98=20Endpoint=EB=93=A4=EC=9D=84=20tRPC=EB=A1=9C=20?= =?UTF-8?q?=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + pnpm-lock.yaml | 25 +++ src/lib/modules/file/upload.ts | 10 +- src/lib/modules/filesystem.ts | 74 +++---- src/lib/server/db/user.ts | 4 - src/lib/server/modules/filesystem.ts | 7 + src/lib/server/schemas/category.ts | 52 ----- src/lib/server/schemas/directory.ts | 38 ---- src/lib/server/schemas/file.ts | 55 ----- src/lib/server/services/category.ts | 133 ------------ src/lib/server/services/directory.ts | 96 --------- src/lib/server/services/file.ts | 125 ++--------- src/lib/services/category.ts | 41 ++-- src/lib/services/file.ts | 33 +-- src/routes/(fullscreen)/file/[id]/service.ts | 16 +- .../(fullscreen)/settings/thumbnail/+page.ts | 14 +- .../(main)/category/[[id]]/service.svelte.ts | 33 ++- .../(main)/directory/[[id]]/service.svelte.ts | 93 +++++---- src/routes/(main)/menu/+page.ts | 2 +- src/routes/api/category/[id]/+server.ts | 33 --- .../api/category/[id]/delete/+server.ts | 20 -- .../api/category/[id]/file/add/+server.ts | 25 --- .../api/category/[id]/file/list/+server.ts | 36 ---- .../api/category/[id]/file/remove/+server.ts | 25 --- .../api/category/[id]/rename/+server.ts | 25 --- src/routes/api/category/create/+server.ts | 23 --- src/routes/api/directory/[id]/+server.ts | 34 --- .../api/directory/[id]/delete/+server.ts | 23 --- .../api/directory/[id]/rename/+server.ts | 25 --- src/routes/api/directory/create/+server.ts | 23 --- src/routes/api/file/[id]/+server.ts | 48 ----- src/routes/api/file/[id]/delete/+server.ts | 20 -- src/routes/api/file/[id]/rename/+server.ts | 25 --- src/routes/api/file/[id]/thumbnail/+server.ts | 26 --- src/routes/api/file/list/+server.ts | 11 - src/routes/api/file/scanDuplicates/+server.ts | 20 -- .../api/file/scanMissingThumbnails/+server.ts | 16 -- src/trpc/client.ts | 2 + src/trpc/init.server.ts | 6 +- src/trpc/router.server.ts | 13 +- src/trpc/routers/category.ts | 194 ++++++++++++++++++ src/trpc/routers/directory.ts | 125 +++++++++++ src/trpc/routers/file.ts | 122 +++++++++++ src/trpc/routers/index.ts | 3 + src/trpc/routers/user.ts | 13 +- 45 files changed, 691 insertions(+), 1097 deletions(-) create mode 100644 src/lib/server/modules/filesystem.ts delete mode 100644 src/lib/server/services/category.ts delete mode 100644 src/lib/server/services/directory.ts delete mode 100644 src/routes/api/category/[id]/+server.ts delete mode 100644 src/routes/api/category/[id]/delete/+server.ts delete mode 100644 src/routes/api/category/[id]/file/add/+server.ts delete mode 100644 src/routes/api/category/[id]/file/list/+server.ts delete mode 100644 src/routes/api/category/[id]/file/remove/+server.ts delete mode 100644 src/routes/api/category/[id]/rename/+server.ts delete mode 100644 src/routes/api/category/create/+server.ts delete mode 100644 src/routes/api/directory/[id]/+server.ts delete mode 100644 src/routes/api/directory/[id]/delete/+server.ts delete mode 100644 src/routes/api/directory/[id]/rename/+server.ts delete mode 100644 src/routes/api/directory/create/+server.ts delete mode 100644 src/routes/api/file/[id]/+server.ts delete mode 100644 src/routes/api/file/[id]/delete/+server.ts delete mode 100644 src/routes/api/file/[id]/rename/+server.ts delete mode 100644 src/routes/api/file/[id]/thumbnail/+server.ts delete mode 100644 src/routes/api/file/list/+server.ts delete mode 100644 src/routes/api/file/scanDuplicates/+server.ts delete mode 100644 src/routes/api/file/scanMissingThumbnails/+server.ts create mode 100644 src/trpc/routers/category.ts create mode 100644 src/trpc/routers/directory.ts create mode 100644 src/trpc/routers/file.ts diff --git a/package.json b/package.json index 4a8493b..b1425b0 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "ms": "^2.1.3", "node-schedule": "^2.1.1", "pg": "^8.16.3", + "superjson": "^2.2.6", "uuid": "^13.0.0", "zod": "^3.25.76" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 50affd7..b428b0f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: pg: specifier: ^8.16.3 version: 8.16.3 + superjson: + specifier: ^2.2.6 + version: 2.2.6 uuid: specifier: ^13.0.0 version: 13.0.0 @@ -889,6 +892,10 @@ packages: resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} engines: {node: '>= 0.6'} + copy-anything@4.0.5: + resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==} + engines: {node: '>=18'} + cron-parser@4.9.0: resolution: {integrity: sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==} engines: {node: '>=12.0.0'} @@ -1259,6 +1266,10 @@ packages: is-reference@3.0.3: resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==} + is-what@5.5.0: + resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==} + engines: {node: '>=18'} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -1854,6 +1865,10 @@ packages: engines: {node: '>=16 || 14 >=14.17'} hasBin: true + superjson@2.2.6: + resolution: {integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==} + engines: {node: '>=16'} + supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} @@ -2767,6 +2782,10 @@ snapshots: cookie@0.6.0: {} + copy-anything@4.0.5: + dependencies: + is-what: 5.5.0 + cron-parser@4.9.0: dependencies: luxon: 3.7.2 @@ -3153,6 +3172,8 @@ snapshots: dependencies: '@types/estree': 1.0.8 + is-what@5.5.0: {} + isexe@2.0.0: {} jackspeak@3.4.3: @@ -3641,6 +3662,10 @@ snapshots: pirates: 4.0.7 ts-interface-checker: 0.1.13 + superjson@2.2.6: + dependencies: + copy-anything: 4.0.5 + supports-color@7.2.0: dependencies: has-flag: 4.0.0 diff --git a/src/lib/modules/file/upload.ts b/src/lib/modules/file/upload.ts index b5b00a1..7c48503 100644 --- a/src/lib/modules/file/upload.ts +++ b/src/lib/modules/file/upload.ts @@ -13,8 +13,6 @@ import { } from "$lib/modules/crypto"; import { generateThumbnail } from "$lib/modules/thumbnail"; import type { - DuplicateFileScanRequest, - DuplicateFileScanResponse, FileThumbnailUploadRequest, FileUploadRequest, FileUploadResponse, @@ -25,18 +23,18 @@ import { type HmacSecret, type FileUploadStatus, } from "$lib/stores"; +import { useTRPC } from "$trpc/client"; const requestDuplicateFileScan = limitFunction( async (file: File, hmacSecret: HmacSecret, onDuplicate: () => Promise) => { + const trpc = useTRPC(); const fileBuffer = await file.arrayBuffer(); const fileSigned = encodeToBase64(await signMessageHmac(fileBuffer, hmacSecret.secret)); - const res = await axios.post("/api/file/scanDuplicates", { + const files = await trpc.file.listByHash.query({ hskVersion: hmacSecret.version, contentHmac: fileSigned, - } satisfies DuplicateFileScanRequest); - const { files }: DuplicateFileScanResponse = res.data; - + }); if (files.length === 0 || (await onDuplicate())) { return { fileBuffer, fileSigned }; } else { diff --git a/src/lib/modules/filesystem.ts b/src/lib/modules/filesystem.ts index c160534..8b9b203 100644 --- a/src/lib/modules/filesystem.ts +++ b/src/lib/modules/filesystem.ts @@ -1,5 +1,5 @@ +import { TRPCClientError } from "@trpc/client"; import { get, writable, type Writable } from "svelte/store"; -import { callGetApi } from "$lib/hooks"; import { getDirectoryInfos as getDirectoryInfosFromIndexedDB, getDirectoryInfo as getDirectoryInfoFromIndexedDB, @@ -18,12 +18,7 @@ import { type CategoryId, } from "$lib/indexedDB"; import { unwrapDataKey, decryptString } from "$lib/modules/crypto"; -import type { - CategoryInfoResponse, - CategoryFileListResponse, - DirectoryInfoResponse, - FileInfoResponse, -} from "$lib/server/schemas"; +import { useTRPC } from "$trpc/client"; export type DirectoryInfo = | { @@ -106,20 +101,20 @@ const fetchDirectoryInfoFromServer = async ( info: Writable, masterKey: CryptoKey, ) => { - const res = await callGetApi(`/api/directory/${id}`); - if (res.status === 404) { - info.set(null); - await deleteDirectoryInfo(id as number); - return; - } else if (!res.ok) { + const trpc = useTRPC(); + let data; + try { + data = await trpc.directory.get.query({ id }); + } catch (e) { + if (e instanceof TRPCClientError && e.data?.code === "NOT_FOUND") { + info.set(null); + await deleteDirectoryInfo(id as number); + return; + } throw new Error("Failed to fetch directory information"); } - const { - metadata, - subDirectories: subDirectoryIds, - files: fileIds, - }: DirectoryInfoResponse = await res.json(); + const { metadata, subDirectories: subDirectoryIds, files: fileIds } = data; if (id === "root") { info.set({ id, subDirectoryIds, fileIds }); @@ -179,16 +174,18 @@ const fetchFileInfoFromServer = async ( info: Writable, masterKey: CryptoKey, ) => { - const res = await callGetApi(`/api/file/${id}`); - if (res.status === 404) { - info.set(null); - await deleteFileInfo(id); - return; - } else if (!res.ok) { + const trpc = useTRPC(); + let metadata; + try { + metadata = await trpc.file.get.query({ id }); + } catch (e) { + if (e instanceof TRPCClientError && e.data?.code === "NOT_FOUND") { + info.set(null); + await deleteFileInfo(id); + return; + } throw new Error("Failed to fetch file information"); } - - const metadata: FileInfoResponse = await res.json(); const { dataKey } = await unwrapDataKey(metadata.dek, masterKey); const name = await decryptString(metadata.name, metadata.nameIv, dataKey); @@ -273,16 +270,20 @@ const fetchCategoryInfoFromServer = async ( info: Writable, masterKey: CryptoKey, ) => { - let res = await callGetApi(`/api/category/${id}`); - if (res.status === 404) { - info.set(null); - await deleteCategoryInfo(id as number); - return; - } else if (!res.ok) { + const trpc = useTRPC(); + let data; + try { + data = await trpc.category.get.query({ id }); + } catch (e) { + if (e instanceof TRPCClientError && e.data?.code === "NOT_FOUND") { + info.set(null); + await deleteCategoryInfo(id as number); + return; + } throw new Error("Failed to fetch category information"); } - const { metadata, subCategories }: CategoryInfoResponse = await res.json(); + const { metadata, subCategories } = data; if (id === "root") { info.set({ id, subCategoryIds: subCategories }); @@ -290,12 +291,13 @@ const fetchCategoryInfoFromServer = async ( const { dataKey } = await unwrapDataKey(metadata!.dek, masterKey); const name = await decryptString(metadata!.name, metadata!.nameIv, dataKey); - res = await callGetApi(`/api/category/${id}/file/list?recurse=true`); - if (!res.ok) { + let files; + try { + files = await trpc.category.files.query({ id, recurse: true }); + } catch { throw new Error("Failed to fetch category files"); } - const { files }: CategoryFileListResponse = await res.json(); const filesMapped = files.map(({ file, isRecursive }) => ({ id: file, isRecursive })); let isFileRecursive: boolean | undefined = undefined; diff --git a/src/lib/server/db/user.ts b/src/lib/server/db/user.ts index 3964a94..f718d4f 100644 --- a/src/lib/server/db/user.ts +++ b/src/lib/server/db/user.ts @@ -27,10 +27,6 @@ export const getUserByEmail = async (email: string) => { return user ? (user satisfies User) : null; }; -export const setUserNickname = async (userId: number, nickname: string) => { - await db.updateTable("user").set({ nickname }).where("id", "=", userId).execute(); -}; - export const setUserPassword = async (userId: number, password: string) => { await db.updateTable("user").set({ password }).where("id", "=", userId).execute(); }; diff --git a/src/lib/server/modules/filesystem.ts b/src/lib/server/modules/filesystem.ts new file mode 100644 index 0000000..65cb9ec --- /dev/null +++ b/src/lib/server/modules/filesystem.ts @@ -0,0 +1,7 @@ +import { unlink } from "fs/promises"; + +export const safeUnlink = async (path: string | null | undefined) => { + if (path) { + await unlink(path).catch(console.error); + } +}; diff --git a/src/lib/server/schemas/category.ts b/src/lib/server/schemas/category.ts index 55ae413..408af7b 100644 --- a/src/lib/server/schemas/category.ts +++ b/src/lib/server/schemas/category.ts @@ -1,55 +1,3 @@ import { z } from "zod"; export const categoryIdSchema = z.union([z.literal("root"), z.number().int().positive()]); - -export const categoryInfoResponse = z.object({ - metadata: z - .object({ - parent: categoryIdSchema, - mekVersion: z.number().int().positive(), - dek: z.string().base64().nonempty(), - dekVersion: z.string().datetime(), - name: z.string().base64().nonempty(), - nameIv: z.string().base64().nonempty(), - }) - .optional(), - subCategories: z.number().int().positive().array(), -}); -export type CategoryInfoResponse = z.output; - -export const categoryFileAddRequest = z.object({ - file: z.number().int().positive(), -}); -export type CategoryFileAddRequest = z.input; - -export const categoryFileListResponse = z.object({ - files: z.array( - z.object({ - file: z.number().int().positive(), - isRecursive: z.boolean(), - }), - ), -}); -export type CategoryFileListResponse = z.output; - -export const categoryFileRemoveRequest = z.object({ - file: z.number().int().positive(), -}); -export type CategoryFileRemoveRequest = z.input; - -export const categoryRenameRequest = z.object({ - dekVersion: z.string().datetime(), - name: z.string().base64().nonempty(), - nameIv: z.string().base64().nonempty(), -}); -export type CategoryRenameRequest = z.input; - -export const categoryCreateRequest = z.object({ - parent: categoryIdSchema, - mekVersion: z.number().int().positive(), - dek: z.string().base64().nonempty(), - dekVersion: z.string().datetime(), - name: z.string().base64().nonempty(), - nameIv: z.string().base64().nonempty(), -}); -export type CategoryCreateRequest = z.input; diff --git a/src/lib/server/schemas/directory.ts b/src/lib/server/schemas/directory.ts index ffd13bc..107c3ee 100644 --- a/src/lib/server/schemas/directory.ts +++ b/src/lib/server/schemas/directory.ts @@ -1,41 +1,3 @@ import { z } from "zod"; export const directoryIdSchema = z.union([z.literal("root"), z.number().int().positive()]); - -export const directoryInfoResponse = z.object({ - metadata: z - .object({ - parent: directoryIdSchema, - mekVersion: z.number().int().positive(), - dek: z.string().base64().nonempty(), - dekVersion: z.string().datetime(), - name: z.string().base64().nonempty(), - nameIv: z.string().base64().nonempty(), - }) - .optional(), - subDirectories: z.number().int().positive().array(), - files: z.number().int().positive().array(), -}); -export type DirectoryInfoResponse = z.output; - -export const directoryDeleteResponse = z.object({ - deletedFiles: z.number().int().positive().array(), -}); -export type DirectoryDeleteResponse = z.output; - -export const directoryRenameRequest = z.object({ - dekVersion: z.string().datetime(), - name: z.string().base64().nonempty(), - nameIv: z.string().base64().nonempty(), -}); -export type DirectoryRenameRequest = z.input; - -export const directoryCreateRequest = z.object({ - parent: directoryIdSchema, - mekVersion: z.number().int().positive(), - dek: z.string().base64().nonempty(), - dekVersion: z.string().datetime(), - name: z.string().base64().nonempty(), - nameIv: z.string().base64().nonempty(), -}); -export type DirectoryCreateRequest = z.input; diff --git a/src/lib/server/schemas/file.ts b/src/lib/server/schemas/file.ts index 8b9cfe9..5177bbd 100644 --- a/src/lib/server/schemas/file.ts +++ b/src/lib/server/schemas/file.ts @@ -2,67 +2,12 @@ import mime from "mime"; import { z } from "zod"; import { directoryIdSchema } from "./directory"; -export const fileInfoResponse = z.object({ - parent: directoryIdSchema, - mekVersion: z.number().int().positive(), - dek: z.string().base64().nonempty(), - dekVersion: z.string().datetime(), - contentType: z - .string() - .trim() - .nonempty() - .refine((value) => mime.getExtension(value) !== null), // MIME type - contentIv: z.string().base64().nonempty(), - name: z.string().base64().nonempty(), - nameIv: z.string().base64().nonempty(), - createdAt: z.string().base64().nonempty().optional(), - createdAtIv: z.string().base64().nonempty().optional(), - lastModifiedAt: z.string().base64().nonempty(), - lastModifiedAtIv: z.string().base64().nonempty(), - categories: z.number().int().positive().array(), -}); -export type FileInfoResponse = z.output; - -export const fileRenameRequest = z.object({ - dekVersion: z.string().datetime(), - name: z.string().base64().nonempty(), - nameIv: z.string().base64().nonempty(), -}); -export type FileRenameRequest = z.input; - -export const fileThumbnailInfoResponse = z.object({ - updatedAt: z.string().datetime(), - contentIv: z.string().base64().nonempty(), -}); -export type FileThumbnailInfoResponse = z.output; - export const fileThumbnailUploadRequest = z.object({ dekVersion: z.string().datetime(), contentIv: z.string().base64().nonempty(), }); export type FileThumbnailUploadRequest = z.input; -export const fileListResponse = z.object({ - files: z.number().int().positive().array(), -}); -export type FileListResponse = z.output; - -export const duplicateFileScanRequest = z.object({ - hskVersion: z.number().int().positive(), - contentHmac: z.string().base64().nonempty(), -}); -export type DuplicateFileScanRequest = z.input; - -export const duplicateFileScanResponse = z.object({ - files: z.number().int().positive().array(), -}); -export type DuplicateFileScanResponse = z.output; - -export const missingThumbnailFileScanResponse = z.object({ - files: z.number().int().positive().array(), -}); -export type MissingThumbnailFileScanResponse = z.output; - export const fileUploadRequest = z.object({ parent: directoryIdSchema, mekVersion: z.number().int().positive(), diff --git a/src/lib/server/services/category.ts b/src/lib/server/services/category.ts deleted file mode 100644 index cb3db7a..0000000 --- a/src/lib/server/services/category.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { error } from "@sveltejs/kit"; -import { - registerCategory, - getAllCategoriesByParent, - getCategory, - setCategoryEncName, - unregisterCategory, - type CategoryId, - type NewCategory, -} from "$lib/server/db/category"; -import { IntegrityError } from "$lib/server/db/error"; -import { - getAllFilesByCategory, - getFile, - addFileToCategory, - removeFileFromCategory, -} from "$lib/server/db/file"; -import type { Ciphertext } from "$lib/server/db/schema"; - -export const getCategoryInformation = async (userId: number, categoryId: CategoryId) => { - const category = categoryId !== "root" ? await getCategory(userId, categoryId) : undefined; - if (category === null) { - error(404, "Invalid category id"); - } - - const categories = await getAllCategoriesByParent(userId, categoryId); - return { - metadata: category && { - parentId: category.parentId ?? ("root" as const), - mekVersion: category.mekVersion, - encDek: category.encDek, - dekVersion: category.dekVersion, - encName: category.encName, - }, - categories: categories.map(({ id }) => id), - }; -}; - -export const deleteCategory = async (userId: number, categoryId: number) => { - try { - await unregisterCategory(userId, categoryId); - } catch (e) { - if (e instanceof IntegrityError && e.message === "Category not found") { - error(404, "Invalid category id"); - } - throw e; - } -}; - -export const addCategoryFile = async (userId: number, categoryId: number, fileId: number) => { - const category = await getCategory(userId, categoryId); - const file = await getFile(userId, fileId); - if (!category) { - error(404, "Invalid category id"); - } else if (!file) { - error(404, "Invalid file id"); - } - - try { - await addFileToCategory(fileId, categoryId); - } catch (e) { - if (e instanceof IntegrityError && e.message === "File already added to category") { - error(400, "File already added"); - } - throw e; - } -}; - -export const getCategoryFiles = async (userId: number, categoryId: number, recurse: boolean) => { - const category = await getCategory(userId, categoryId); - if (!category) { - error(404, "Invalid category id"); - } - - const files = await getAllFilesByCategory(userId, categoryId, recurse); - return { files }; -}; - -export const removeCategoryFile = async (userId: number, categoryId: number, fileId: number) => { - const category = await getCategory(userId, categoryId); - const file = await getFile(userId, fileId); - if (!category) { - error(404, "Invalid category id"); - } else if (!file) { - error(404, "Invalid file id"); - } - - try { - await removeFileFromCategory(fileId, categoryId); - } catch (e) { - if (e instanceof IntegrityError && e.message === "File not found in category") { - error(400, "File not added"); - } - throw e; - } -}; - -export const renameCategory = async ( - userId: number, - categoryId: number, - dekVersion: Date, - newEncName: Ciphertext, -) => { - try { - await setCategoryEncName(userId, categoryId, dekVersion, newEncName); - } catch (e) { - if (e instanceof IntegrityError) { - if (e.message === "Category not found") { - error(404, "Invalid category id"); - } else if (e.message === "Invalid DEK version") { - error(400, "Invalid DEK version"); - } - } - throw e; - } -}; - -export const createCategory = async (params: NewCategory) => { - const oneMinuteAgo = new Date(Date.now() - 60 * 1000); - const oneMinuteLater = new Date(Date.now() + 60 * 1000); - if (params.dekVersion <= oneMinuteAgo || params.dekVersion >= oneMinuteLater) { - error(400, "Invalid DEK version"); - } - - try { - await registerCategory(params); - } catch (e) { - if (e instanceof IntegrityError && e.message === "Inactive MEK version") { - error(400, "Inactive MEK version"); - } - throw e; - } -}; diff --git a/src/lib/server/services/directory.ts b/src/lib/server/services/directory.ts deleted file mode 100644 index fdab587..0000000 --- a/src/lib/server/services/directory.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { error } from "@sveltejs/kit"; -import { unlink } from "fs/promises"; -import { IntegrityError } from "$lib/server/db/error"; -import { - registerDirectory, - getAllDirectoriesByParent, - getDirectory, - setDirectoryEncName, - unregisterDirectory, - getAllFilesByParent, - type DirectoryId, - type NewDirectory, -} from "$lib/server/db/file"; -import type { Ciphertext } from "$lib/server/db/schema"; - -export const getDirectoryInformation = async (userId: number, directoryId: DirectoryId) => { - const directory = directoryId !== "root" ? await getDirectory(userId, directoryId) : undefined; - if (directory === null) { - error(404, "Invalid directory id"); - } - - const directories = await getAllDirectoriesByParent(userId, directoryId); - const files = await getAllFilesByParent(userId, directoryId); - return { - metadata: directory && { - parentId: directory.parentId ?? ("root" as const), - mekVersion: directory.mekVersion, - encDek: directory.encDek, - dekVersion: directory.dekVersion, - encName: directory.encName, - }, - directories: directories.map(({ id }) => id), - files: files.map(({ id }) => id), - }; -}; - -const safeUnlink = async (path: string | null) => { - if (path) { - await unlink(path).catch(console.error); - } -}; - -export const deleteDirectory = async (userId: number, directoryId: number) => { - try { - const files = await unregisterDirectory(userId, directoryId); - return { - files: files.map(({ id, path, thumbnailPath }) => { - safeUnlink(path); // Intended - safeUnlink(thumbnailPath); // Intended - return id; - }), - }; - } catch (e) { - if (e instanceof IntegrityError && e.message === "Directory not found") { - error(404, "Invalid directory id"); - } - throw e; - } -}; - -export const renameDirectory = async ( - userId: number, - directoryId: number, - dekVersion: Date, - newEncName: Ciphertext, -) => { - try { - await setDirectoryEncName(userId, directoryId, dekVersion, newEncName); - } catch (e) { - if (e instanceof IntegrityError) { - if (e.message === "Directory not found") { - error(404, "Invalid directory id"); - } else if (e.message === "Invalid DEK version") { - error(400, "Invalid DEK version"); - } - } - throw e; - } -}; - -export const createDirectory = async (params: NewDirectory) => { - const oneMinuteAgo = new Date(Date.now() - 60 * 1000); - const oneMinuteLater = new Date(Date.now() + 60 * 1000); - if (params.dekVersion <= oneMinuteAgo || params.dekVersion >= oneMinuteLater) { - error(400, "Invalid DEK version"); - } - - try { - await registerDirectory(params); - } catch (e) { - if (e instanceof IntegrityError && e.message === "Inactive MEK version") { - error(400, "Invalid MEK version"); - } - throw e; - } -}; diff --git a/src/lib/server/services/file.ts b/src/lib/server/services/file.ts index ab98dbf..9032ffb 100644 --- a/src/lib/server/services/file.ts +++ b/src/lib/server/services/file.ts @@ -1,72 +1,17 @@ import { error } from "@sveltejs/kit"; import { createHash } from "crypto"; import { createReadStream, createWriteStream } from "fs"; -import { mkdir, stat, unlink } from "fs/promises"; +import { mkdir, stat } from "fs/promises"; import { dirname } from "path"; import { Readable } from "stream"; import { pipeline } from "stream/promises"; import { v4 as uuidv4 } from "uuid"; -import { IntegrityError } from "$lib/server/db/error"; -import { - registerFile, - getAllFileIds, - getAllFileIdsByContentHmac, - getFile, - setFileEncName, - unregisterFile, - getAllFileCategories, - type NewFile, -} from "$lib/server/db/file"; -import { - updateFileThumbnail, - getFileThumbnail, - getMissingFileThumbnails, -} from "$lib/server/db/media"; -import type { Ciphertext } from "$lib/server/db/schema"; +import { FileRepo, MediaRepo, IntegrityError } from "$lib/server/db"; import env from "$lib/server/loadenv"; - -export const getFileInformation = async (userId: number, fileId: number) => { - const file = await getFile(userId, fileId); - if (!file) { - error(404, "Invalid file id"); - } - - const categories = await getAllFileCategories(fileId); - return { - parentId: file.parentId ?? ("root" as const), - mekVersion: file.mekVersion, - encDek: file.encDek, - dekVersion: file.dekVersion, - contentType: file.contentType, - encContentIv: file.encContentIv, - encName: file.encName, - encCreatedAt: file.encCreatedAt, - encLastModifiedAt: file.encLastModifiedAt, - categories: categories.map(({ id }) => id), - }; -}; - -const safeUnlink = async (path: string | null) => { - if (path) { - await unlink(path).catch(console.error); - } -}; - -export const deleteFile = async (userId: number, fileId: number) => { - try { - const { path, thumbnailPath } = await unregisterFile(userId, fileId); - safeUnlink(path); // Intended - safeUnlink(thumbnailPath); // Intended - } catch (e) { - if (e instanceof IntegrityError && e.message === "File not found") { - error(404, "Invalid file id"); - } - throw e; - } -}; +import { safeUnlink } from "$lib/server/modules/filesystem"; export const getFileStream = async (userId: number, fileId: number) => { - const file = await getFile(userId, fileId); + const file = await FileRepo.getFile(userId, fileId); if (!file) { error(404, "Invalid file id"); } @@ -78,37 +23,8 @@ export const getFileStream = async (userId: number, fileId: number) => { }; }; -export const renameFile = async ( - userId: number, - fileId: number, - dekVersion: Date, - newEncName: Ciphertext, -) => { - try { - await setFileEncName(userId, fileId, dekVersion, newEncName); - } catch (e) { - if (e instanceof IntegrityError) { - if (e.message === "File not found") { - error(404, "Invalid file id"); - } else if (e.message === "Invalid DEK version") { - error(400, "Invalid DEK version"); - } - } - throw e; - } -}; - -export const getFileThumbnailInformation = async (userId: number, fileId: number) => { - const thumbnail = await getFileThumbnail(userId, fileId); - if (!thumbnail) { - error(404, "File or its thumbnail not found"); - } - - return { updatedAt: thumbnail.updatedAt, encContentIv: thumbnail.encContentIv }; -}; - export const getFileThumbnailStream = async (userId: number, fileId: number) => { - const thumbnail = await getFileThumbnail(userId, fileId); + const thumbnail = await MediaRepo.getFileThumbnail(userId, fileId); if (!thumbnail) { error(404, "File or its thumbnail not found"); } @@ -133,7 +49,13 @@ export const uploadFileThumbnail = async ( try { await pipeline(encContentStream, createWriteStream(path, { flags: "wx", mode: 0o600 })); - const oldPath = await updateFileThumbnail(userId, fileId, dekVersion, path, encContentIv); + const oldPath = await MediaRepo.updateFileThumbnail( + userId, + fileId, + dekVersion, + path, + encContentIv, + ); safeUnlink(oldPath); // Intended } catch (e) { await safeUnlink(path); @@ -149,27 +71,8 @@ export const uploadFileThumbnail = async ( } }; -export const getFileList = async (userId: number) => { - const fileIds = await getAllFileIds(userId); - return { files: fileIds }; -}; - -export const scanDuplicateFiles = async ( - userId: number, - hskVersion: number, - contentHmac: string, -) => { - const fileIds = await getAllFileIdsByContentHmac(userId, hskVersion, contentHmac); - return { files: fileIds }; -}; - -export const scanMissingFileThumbnails = async (userId: number) => { - const fileIds = await getMissingFileThumbnails(userId); - return { files: fileIds }; -}; - export const uploadFile = async ( - params: Omit, + params: Omit, encContentStream: Readable, encContentHash: Promise, ) => { @@ -201,7 +104,7 @@ export const uploadFile = async ( throw new Error("Invalid checksum"); } - const { id: fileId } = await registerFile({ + const { id: fileId } = await FileRepo.registerFile({ ...params, path, encContentHash: hash, diff --git a/src/lib/services/category.ts b/src/lib/services/category.ts index 587df3f..c86c93b 100644 --- a/src/lib/services/category.ts +++ b/src/lib/services/category.ts @@ -1,31 +1,40 @@ -import { callPostApi } from "$lib/hooks"; import { generateDataKey, wrapDataKey, encryptString } from "$lib/modules/crypto"; -import type { CategoryCreateRequest, CategoryFileRemoveRequest } from "$lib/server/schemas"; import type { MasterKey } from "$lib/stores"; +import { useTRPC } from "$trpc/client"; export const requestCategoryCreation = async ( name: string, parentId: "root" | number, masterKey: MasterKey, ) => { + const trpc = useTRPC(); const { dataKey, dataKeyVersion } = await generateDataKey(); const nameEncrypted = await encryptString(name, dataKey); - const res = await callPostApi("/api/category/create", { - parent: parentId, - mekVersion: masterKey.version, - dek: await wrapDataKey(dataKey, masterKey.key), - dekVersion: dataKeyVersion.toISOString(), - name: nameEncrypted.ciphertext, - nameIv: nameEncrypted.iv, - }); - return res.ok; + try { + await trpc.category.create.mutate({ + parent: parentId, + mekVersion: masterKey.version, + dek: await wrapDataKey(dataKey, masterKey.key), + dekVersion: dataKeyVersion, + name: nameEncrypted.ciphertext, + nameIv: nameEncrypted.iv, + }); + return true; + } catch { + // TODO: Error Handling + return false; + } }; export const requestFileRemovalFromCategory = async (fileId: number, categoryId: number) => { - const res = await callPostApi( - `/api/category/${categoryId}/file/remove`, - { file: fileId }, - ); - return res.ok; + const trpc = useTRPC(); + + try { + await trpc.category.removeFile.mutate({ id: categoryId, file: fileId }); + return true; + } catch { + // TODO: Error Handling + return false; + } }; diff --git a/src/lib/services/file.ts b/src/lib/services/file.ts index bab3dac..f428e97 100644 --- a/src/lib/services/file.ts +++ b/src/lib/services/file.ts @@ -11,11 +11,8 @@ import { downloadFile, } from "$lib/modules/file"; import { getThumbnailUrl } from "$lib/modules/thumbnail"; -import type { - FileThumbnailInfoResponse, - FileThumbnailUploadRequest, - FileListResponse, -} from "$lib/server/schemas"; +import type { FileThumbnailUploadRequest } from "$lib/server/schemas"; +import { useTRPC } from "$trpc/client"; export const requestFileDownload = async ( fileId: number, @@ -52,12 +49,17 @@ export const requestFileThumbnailDownload = async (fileId: number, dataKey?: Cry const cache = await getFileThumbnailCache(fileId); if (cache || !dataKey) return cache; - let res = await callGetApi(`/api/file/${fileId}/thumbnail`); - if (!res.ok) return null; + const trpc = useTRPC(); + let thumbnailInfo; + try { + thumbnailInfo = await trpc.file.thumbnail.query({ id: fileId }); + } catch { + // TODO: Error Handling + return null; + } + const { contentIv: thumbnailEncryptedIv } = thumbnailInfo; - const { contentIv: thumbnailEncryptedIv }: FileThumbnailInfoResponse = await res.json(); - - res = await callGetApi(`/api/file/${fileId}/thumbnail/download`); + const res = await callGetApi(`/api/file/${fileId}/thumbnail/download`); if (!res.ok) return null; const thumbnailEncrypted = await res.arrayBuffer(); @@ -68,10 +70,15 @@ export const requestFileThumbnailDownload = async (fileId: number, dataKey?: Cry }; export const requestDeletedFilesCleanup = async () => { - const res = await callGetApi("/api/file/list"); - if (!res.ok) return; + const trpc = useTRPC(); + let liveFiles; + try { + liveFiles = await trpc.file.list.query(); + } catch { + // TODO: Error Handling + return; + } - const { files: liveFiles }: FileListResponse = await res.json(); const liveFilesSet = new Set(liveFiles); const maybeCachedFiles = await getAllFileInfos(); diff --git a/src/routes/(fullscreen)/file/[id]/service.ts b/src/routes/(fullscreen)/file/[id]/service.ts index 00614d6..73ca7f1 100644 --- a/src/routes/(fullscreen)/file/[id]/service.ts +++ b/src/routes/(fullscreen)/file/[id]/service.ts @@ -1,8 +1,7 @@ -import { callPostApi } from "$lib/hooks"; import { encryptData } from "$lib/modules/crypto"; import { storeFileThumbnailCache } from "$lib/modules/file"; -import type { CategoryFileAddRequest } from "$lib/server/schemas"; import { requestFileThumbnailUpload } from "$lib/services/file"; +import { useTRPC } from "$trpc/client"; export { requestCategoryCreation, requestFileRemovalFromCategory } from "$lib/services/category"; export { requestFileDownload } from "$lib/services/file"; @@ -23,8 +22,13 @@ export const requestThumbnailUpload = async ( }; export const requestFileAdditionToCategory = async (fileId: number, categoryId: number) => { - const res = await callPostApi(`/api/category/${categoryId}/file/add`, { - file: fileId, - }); - return res.ok; + const trpc = useTRPC(); + + try { + await trpc.category.addFile.mutate({ id: categoryId, file: fileId }); + return true; + } catch { + // TODO: Error Handling + return false; + } }; diff --git a/src/routes/(fullscreen)/settings/thumbnail/+page.ts b/src/routes/(fullscreen)/settings/thumbnail/+page.ts index a16cb8e..3bfa322 100644 --- a/src/routes/(fullscreen)/settings/thumbnail/+page.ts +++ b/src/routes/(fullscreen)/settings/thumbnail/+page.ts @@ -1,14 +1,14 @@ import { error } from "@sveltejs/kit"; -import { callPostApi } from "$lib/hooks"; -import type { MissingThumbnailFileScanResponse } from "$lib/server/schemas"; +import { useTRPC } from "$trpc/client"; import type { PageLoad } from "./$types"; export const load: PageLoad = async ({ fetch }) => { - const res = await callPostApi("/api/file/scanMissingThumbnails", undefined, fetch); - if (!res.ok) { + const trpc = useTRPC(fetch); + + try { + const files = await trpc.file.listWithoutThumbnail.query(); + return { files }; + } catch { error(500, "Internal server error"); } - - const { files }: MissingThumbnailFileScanResponse = await res.json(); - return { files }; }; diff --git a/src/routes/(main)/category/[[id]]/service.svelte.ts b/src/routes/(main)/category/[[id]]/service.svelte.ts index b573041..824de8a 100644 --- a/src/routes/(main)/category/[[id]]/service.svelte.ts +++ b/src/routes/(main)/category/[[id]]/service.svelte.ts @@ -1,8 +1,7 @@ import { getContext, setContext } from "svelte"; -import { callPostApi } from "$lib/hooks"; import { encryptString } from "$lib/modules/crypto"; import type { SelectedCategory } from "$lib/components/molecules"; -import type { CategoryRenameRequest } from "$lib/server/schemas"; +import { useTRPC } from "$trpc/client"; export { requestCategoryCreation, requestFileRemovalFromCategory } from "$lib/services/category"; @@ -18,17 +17,31 @@ export const useContext = () => { }; export const requestCategoryRename = async (category: SelectedCategory, newName: string) => { + const trpc = useTRPC(); const newNameEncrypted = await encryptString(newName, category.dataKey); - const res = await callPostApi(`/api/category/${category.id}/rename`, { - dekVersion: category.dataKeyVersion.toISOString(), - name: newNameEncrypted.ciphertext, - nameIv: newNameEncrypted.iv, - }); - return res.ok; + try { + await trpc.category.rename.mutate({ + id: category.id, + dekVersion: category.dataKeyVersion, + name: newNameEncrypted.ciphertext, + nameIv: newNameEncrypted.iv, + }); + return true; + } catch { + // TODO: Error Handling + return false; + } }; export const requestCategoryDeletion = async (category: SelectedCategory) => { - const res = await callPostApi(`/api/category/${category.id}/delete`); - return res.ok; + const trpc = useTRPC(); + + try { + await trpc.category.delete.mutate({ id: category.id }); + return true; + } catch { + // TODO: Error Handling + return false; + } }; diff --git a/src/routes/(main)/directory/[[id]]/service.svelte.ts b/src/routes/(main)/directory/[[id]]/service.svelte.ts index 40394f7..72a8fdb 100644 --- a/src/routes/(main)/directory/[[id]]/service.svelte.ts +++ b/src/routes/(main)/directory/[[id]]/service.svelte.ts @@ -1,5 +1,4 @@ import { getContext, setContext } from "svelte"; -import { callPostApi } from "$lib/hooks"; import { storeHmacSecrets } from "$lib/indexedDB"; import { generateDataKey, wrapDataKey, unwrapHmacSecret, encryptString } from "$lib/modules/crypto"; import { @@ -9,12 +8,6 @@ import { deleteFileThumbnailCache, uploadFile, } from "$lib/modules/file"; -import type { - DirectoryRenameRequest, - DirectoryCreateRequest, - FileRenameRequest, - DirectoryDeleteResponse, -} from "$lib/server/schemas"; import { hmacSecretStore, type MasterKey, type HmacSecret } from "$lib/stores"; import { useTRPC } from "$trpc/client"; @@ -68,18 +61,24 @@ export const requestDirectoryCreation = async ( parentId: "root" | number, masterKey: MasterKey, ) => { + const trpc = useTRPC(); const { dataKey, dataKeyVersion } = await generateDataKey(); const nameEncrypted = await encryptString(name, dataKey); - const res = await callPostApi("/api/directory/create", { - parent: parentId, - mekVersion: masterKey.version, - dek: await wrapDataKey(dataKey, masterKey.key), - dekVersion: dataKeyVersion.toISOString(), - name: nameEncrypted.ciphertext, - nameIv: nameEncrypted.iv, - }); - return res.ok; + try { + await trpc.directory.create.mutate({ + parent: parentId, + mekVersion: masterKey.version, + dek: await wrapDataKey(dataKey, masterKey.key), + dekVersion: dataKeyVersion, + name: nameEncrypted.ciphertext, + nameIv: nameEncrypted.iv, + }); + return true; + } catch { + // TODO: Error Handling + return false; + } }; export const requestFileUpload = async ( @@ -101,37 +100,51 @@ export const requestFileUpload = async ( }; export const requestEntryRename = async (entry: SelectedEntry, newName: string) => { + const trpc = useTRPC(); const newNameEncrypted = await encryptString(newName, entry.dataKey); - let res; - if (entry.type === "directory") { - res = await callPostApi(`/api/directory/${entry.id}/rename`, { - dekVersion: entry.dataKeyVersion.toISOString(), - name: newNameEncrypted.ciphertext, - nameIv: newNameEncrypted.iv, - }); - } else { - res = await callPostApi(`/api/file/${entry.id}/rename`, { - dekVersion: entry.dataKeyVersion.toISOString(), - name: newNameEncrypted.ciphertext, - nameIv: newNameEncrypted.iv, - }); + try { + if (entry.type === "directory") { + await trpc.directory.rename.mutate({ + id: entry.id, + dekVersion: entry.dataKeyVersion, + name: newNameEncrypted.ciphertext, + nameIv: newNameEncrypted.iv, + }); + } else { + await trpc.file.rename.mutate({ + id: entry.id, + dekVersion: entry.dataKeyVersion, + name: newNameEncrypted.ciphertext, + nameIv: newNameEncrypted.iv, + }); + } + return true; + } catch { + // TODO: Error Handling + return false; } - return res.ok; }; export const requestEntryDeletion = async (entry: SelectedEntry) => { - const res = await callPostApi(`/api/${entry.type}/${entry.id}/delete`); - if (!res.ok) return false; + const trpc = useTRPC(); - if (entry.type === "directory") { - const { deletedFiles }: DirectoryDeleteResponse = await res.json(); - await Promise.all( - deletedFiles.flatMap((fileId) => [deleteFileCache(fileId), deleteFileThumbnailCache(fileId)]), - ); - return true; - } else { - await Promise.all([deleteFileCache(entry.id), deleteFileThumbnailCache(entry.id)]); + try { + if (entry.type === "directory") { + const { deletedFiles } = await trpc.directory.delete.mutate({ id: entry.id }); + await Promise.all( + deletedFiles.flatMap((fileId) => [ + deleteFileCache(fileId), + deleteFileThumbnailCache(fileId), + ]), + ); + } else { + await trpc.file.delete.mutate({ id: entry.id }); + await Promise.all([deleteFileCache(entry.id), deleteFileThumbnailCache(entry.id)]); + } return true; + } catch { + // TODO: Error Handling + return false; } }; diff --git a/src/routes/(main)/menu/+page.ts b/src/routes/(main)/menu/+page.ts index b1582e7..ecd8f0b 100644 --- a/src/routes/(main)/menu/+page.ts +++ b/src/routes/(main)/menu/+page.ts @@ -6,7 +6,7 @@ export const load: PageLoad = async ({ fetch }) => { const trpc = useTRPC(fetch); try { - const { nickname } = await trpc.user.info.query(); + const { nickname } = await trpc.user.get.query(); return { nickname }; } catch { error(500, "Internal server error"); diff --git a/src/routes/api/category/[id]/+server.ts b/src/routes/api/category/[id]/+server.ts deleted file mode 100644 index 4a486fa..0000000 --- a/src/routes/api/category/[id]/+server.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { error, json } from "@sveltejs/kit"; -import { z } from "zod"; -import { authorize } from "$lib/server/modules/auth"; -import { categoryInfoResponse, type CategoryInfoResponse } from "$lib/server/schemas"; -import { getCategoryInformation } from "$lib/server/services/category"; -import type { RequestHandler } from "./$types"; - -export const GET: RequestHandler = async ({ locals, params }) => { - const { userId } = await authorize(locals, "activeClient"); - - const zodRes = z - .object({ - id: z.union([z.enum(["root"]), z.coerce.number().int().positive()]), - }) - .safeParse(params); - if (!zodRes.success) error(400, "Invalid path parameters"); - const { id } = zodRes.data; - - const { metadata, categories } = await getCategoryInformation(userId, id); - return json( - categoryInfoResponse.parse({ - metadata: metadata && { - parent: metadata.parentId, - mekVersion: metadata.mekVersion, - dek: metadata.encDek, - dekVersion: metadata.dekVersion.toISOString(), - name: metadata.encName.ciphertext, - nameIv: metadata.encName.iv, - }, - subCategories: categories, - } satisfies CategoryInfoResponse), - ); -}; diff --git a/src/routes/api/category/[id]/delete/+server.ts b/src/routes/api/category/[id]/delete/+server.ts deleted file mode 100644 index cbbe356..0000000 --- a/src/routes/api/category/[id]/delete/+server.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { error, text } from "@sveltejs/kit"; -import { z } from "zod"; -import { authorize } from "$lib/server/modules/auth"; -import { deleteCategory } from "$lib/server/services/category"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, params }) => { - const { userId } = await authorize(locals, "activeClient"); - - const zodRes = z - .object({ - id: z.coerce.number().int().positive(), - }) - .safeParse(params); - if (!zodRes.success) error(400, "Invalid path parameters"); - const { id } = zodRes.data; - - await deleteCategory(userId, id); - return text("Category deleted", { headers: { "Content-Type": "text/plain" } }); -}; diff --git a/src/routes/api/category/[id]/file/add/+server.ts b/src/routes/api/category/[id]/file/add/+server.ts deleted file mode 100644 index 2eaf2f2..0000000 --- a/src/routes/api/category/[id]/file/add/+server.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { error, text } from "@sveltejs/kit"; -import { z } from "zod"; -import { authorize } from "$lib/server/modules/auth"; -import { categoryFileAddRequest } from "$lib/server/schemas"; -import { addCategoryFile } from "$lib/server/services/category"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, params, request }) => { - const { userId } = await authorize(locals, "activeClient"); - - const paramsZodRes = z - .object({ - id: z.coerce.number().int().positive(), - }) - .safeParse(params); - if (!paramsZodRes.success) error(400, "Invalid path parameters"); - const { id } = paramsZodRes.data; - - const bodyZodRes = categoryFileAddRequest.safeParse(await request.json()); - if (!bodyZodRes.success) error(400, "Invalid request body"); - const { file } = bodyZodRes.data; - - await addCategoryFile(userId, id, file); - return text("File added", { headers: { "Content-Type": "text/plain" } }); -}; diff --git a/src/routes/api/category/[id]/file/list/+server.ts b/src/routes/api/category/[id]/file/list/+server.ts deleted file mode 100644 index e354d8b..0000000 --- a/src/routes/api/category/[id]/file/list/+server.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { error, json } from "@sveltejs/kit"; -import { z } from "zod"; -import { authorize } from "$lib/server/modules/auth"; -import { categoryFileListResponse, type CategoryFileListResponse } from "$lib/server/schemas"; -import { getCategoryFiles } from "$lib/server/services/category"; -import type { RequestHandler } from "./$types"; - -export const GET: RequestHandler = async ({ locals, url, params }) => { - const { userId } = await authorize(locals, "activeClient"); - - const paramsZodRes = z - .object({ - id: z.coerce.number().int().positive(), - }) - .safeParse(params); - if (!paramsZodRes.success) error(400, "Invalid path parameters"); - const { id } = paramsZodRes.data; - - const queryZodRes = z - .object({ - recurse: z - .enum(["true", "false"]) - .transform((value) => value === "true") - .nullable(), - }) - .safeParse({ recurse: url.searchParams.get("recurse") }); - if (!queryZodRes.success) error(400, "Invalid query parameters"); - const { recurse } = queryZodRes.data; - - const { files } = await getCategoryFiles(userId, id, recurse ?? false); - return json( - categoryFileListResponse.parse({ - files: files.map(({ id, isRecursive }) => ({ file: id, isRecursive })), - } satisfies CategoryFileListResponse), - ); -}; diff --git a/src/routes/api/category/[id]/file/remove/+server.ts b/src/routes/api/category/[id]/file/remove/+server.ts deleted file mode 100644 index 6fdcccf..0000000 --- a/src/routes/api/category/[id]/file/remove/+server.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { error, text } from "@sveltejs/kit"; -import { z } from "zod"; -import { authorize } from "$lib/server/modules/auth"; -import { categoryFileRemoveRequest } from "$lib/server/schemas"; -import { removeCategoryFile } from "$lib/server/services/category"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, params, request }) => { - const { userId } = await authorize(locals, "activeClient"); - - const paramsZodRes = z - .object({ - id: z.coerce.number().int().positive(), - }) - .safeParse(params); - if (!paramsZodRes.success) error(400, "Invalid path parameters"); - const { id } = paramsZodRes.data; - - const bodyZodRes = categoryFileRemoveRequest.safeParse(await request.json()); - if (!bodyZodRes.success) error(400, "Invalid request body"); - const { file } = bodyZodRes.data; - - await removeCategoryFile(userId, id, file); - return text("File removed", { headers: { "Content-Type": "text/plain" } }); -}; diff --git a/src/routes/api/category/[id]/rename/+server.ts b/src/routes/api/category/[id]/rename/+server.ts deleted file mode 100644 index 5351544..0000000 --- a/src/routes/api/category/[id]/rename/+server.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { error, text } from "@sveltejs/kit"; -import { z } from "zod"; -import { authorize } from "$lib/server/modules/auth"; -import { categoryRenameRequest } from "$lib/server/schemas"; -import { renameCategory } from "$lib/server/services/category"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, params, request }) => { - const { userId } = await authorize(locals, "activeClient"); - - const paramsZodRes = z - .object({ - id: z.coerce.number().int().positive(), - }) - .safeParse(params); - if (!paramsZodRes.success) error(400, "Invalid path parameters"); - const { id } = paramsZodRes.data; - - const bodyZodRes = categoryRenameRequest.safeParse(await request.json()); - if (!bodyZodRes.success) error(400, "Invalid request body"); - const { dekVersion, name, nameIv } = bodyZodRes.data; - - await renameCategory(userId, id, new Date(dekVersion), { ciphertext: name, iv: nameIv }); - return text("Category renamed", { headers: { "Content-Type": "text/plain" } }); -}; diff --git a/src/routes/api/category/create/+server.ts b/src/routes/api/category/create/+server.ts deleted file mode 100644 index 216d850..0000000 --- a/src/routes/api/category/create/+server.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { error, text } from "@sveltejs/kit"; -import { authorize } from "$lib/server/modules/auth"; -import { categoryCreateRequest } from "$lib/server/schemas"; -import { createCategory } from "$lib/server/services/category"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, request }) => { - const { userId } = await authorize(locals, "activeClient"); - - const zodRes = categoryCreateRequest.safeParse(await request.json()); - if (!zodRes.success) error(400, "Invalid request body"); - const { parent, mekVersion, dek, dekVersion, name, nameIv } = zodRes.data; - - await createCategory({ - userId, - parentId: parent, - mekVersion, - encDek: dek, - dekVersion: new Date(dekVersion), - encName: { ciphertext: name, iv: nameIv }, - }); - return text("Category created", { headers: { "Content-Type": "text/plain" } }); -}; diff --git a/src/routes/api/directory/[id]/+server.ts b/src/routes/api/directory/[id]/+server.ts deleted file mode 100644 index 8189160..0000000 --- a/src/routes/api/directory/[id]/+server.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { error, json } from "@sveltejs/kit"; -import { z } from "zod"; -import { authorize } from "$lib/server/modules/auth"; -import { directoryInfoResponse, type DirectoryInfoResponse } from "$lib/server/schemas"; -import { getDirectoryInformation } from "$lib/server/services/directory"; -import type { RequestHandler } from "./$types"; - -export const GET: RequestHandler = async ({ locals, params }) => { - const { userId } = await authorize(locals, "activeClient"); - - const zodRes = z - .object({ - id: z.union([z.enum(["root"]), z.coerce.number().int().positive()]), - }) - .safeParse(params); - if (!zodRes.success) error(400, "Invalid path parameters"); - const { id } = zodRes.data; - - const { metadata, directories, files } = await getDirectoryInformation(userId, id); - return json( - directoryInfoResponse.parse({ - metadata: metadata && { - parent: metadata.parentId, - mekVersion: metadata.mekVersion, - dek: metadata.encDek, - dekVersion: metadata.dekVersion.toISOString(), - name: metadata.encName.ciphertext, - nameIv: metadata.encName.iv, - }, - subDirectories: directories, - files, - } satisfies DirectoryInfoResponse), - ); -}; diff --git a/src/routes/api/directory/[id]/delete/+server.ts b/src/routes/api/directory/[id]/delete/+server.ts deleted file mode 100644 index 4d29fd8..0000000 --- a/src/routes/api/directory/[id]/delete/+server.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { error, json } from "@sveltejs/kit"; -import { z } from "zod"; -import { authorize } from "$lib/server/modules/auth"; -import { directoryDeleteResponse, type DirectoryDeleteResponse } from "$lib/server/schemas"; -import { deleteDirectory } from "$lib/server/services/directory"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, params }) => { - const { userId } = await authorize(locals, "activeClient"); - - const zodRes = z - .object({ - id: z.coerce.number().int().positive(), - }) - .safeParse(params); - if (!zodRes.success) error(400, "Invalid path parameters"); - const { id } = zodRes.data; - - const { files } = await deleteDirectory(userId, id); - return json( - directoryDeleteResponse.parse({ deletedFiles: files } satisfies DirectoryDeleteResponse), - ); -}; diff --git a/src/routes/api/directory/[id]/rename/+server.ts b/src/routes/api/directory/[id]/rename/+server.ts deleted file mode 100644 index cc50b2f..0000000 --- a/src/routes/api/directory/[id]/rename/+server.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { error, text } from "@sveltejs/kit"; -import { z } from "zod"; -import { authorize } from "$lib/server/modules/auth"; -import { directoryRenameRequest } from "$lib/server/schemas"; -import { renameDirectory } from "$lib/server/services/directory"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, params, request }) => { - const { userId } = await authorize(locals, "activeClient"); - - const paramsZodRes = z - .object({ - id: z.coerce.number().int().positive(), - }) - .safeParse(params); - if (!paramsZodRes.success) error(400, "Invalid path parameters"); - const { id } = paramsZodRes.data; - - const bodyZodRes = directoryRenameRequest.safeParse(await request.json()); - if (!bodyZodRes.success) error(400, "Invalid request body"); - const { dekVersion, name, nameIv } = bodyZodRes.data; - - await renameDirectory(userId, id, new Date(dekVersion), { ciphertext: name, iv: nameIv }); - return text("Directory renamed", { headers: { "Content-Type": "text/plain" } }); -}; diff --git a/src/routes/api/directory/create/+server.ts b/src/routes/api/directory/create/+server.ts deleted file mode 100644 index 7c65436..0000000 --- a/src/routes/api/directory/create/+server.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { error, text } from "@sveltejs/kit"; -import { authorize } from "$lib/server/modules/auth"; -import { directoryCreateRequest } from "$lib/server/schemas"; -import { createDirectory } from "$lib/server/services/directory"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, request }) => { - const { userId } = await authorize(locals, "activeClient"); - - const zodRes = directoryCreateRequest.safeParse(await request.json()); - if (!zodRes.success) error(400, "Invalid request body"); - const { parent, mekVersion, dek, dekVersion, name, nameIv } = zodRes.data; - - await createDirectory({ - userId, - parentId: parent, - mekVersion, - encDek: dek, - dekVersion: new Date(dekVersion), - encName: { ciphertext: name, iv: nameIv }, - }); - return text("Directory created", { headers: { "Content-Type": "text/plain" } }); -}; diff --git a/src/routes/api/file/[id]/+server.ts b/src/routes/api/file/[id]/+server.ts deleted file mode 100644 index 23e9385..0000000 --- a/src/routes/api/file/[id]/+server.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { error, json } from "@sveltejs/kit"; -import { z } from "zod"; -import { authorize } from "$lib/server/modules/auth"; -import { fileInfoResponse, type FileInfoResponse } from "$lib/server/schemas"; -import { getFileInformation } from "$lib/server/services/file"; -import type { RequestHandler } from "./$types"; - -export const GET: RequestHandler = async ({ locals, params }) => { - const { userId } = await authorize(locals, "activeClient"); - - const zodRes = z - .object({ - id: z.coerce.number().int().positive(), - }) - .safeParse(params); - if (!zodRes.success) error(400, "Invalid path parameters"); - const { id } = zodRes.data; - - const { - parentId, - mekVersion, - encDek, - dekVersion, - contentType, - encContentIv, - encName, - encCreatedAt, - encLastModifiedAt, - categories, - } = await getFileInformation(userId, id); - return json( - fileInfoResponse.parse({ - parent: parentId, - mekVersion, - dek: encDek, - dekVersion: dekVersion.toISOString(), - contentType: contentType, - contentIv: encContentIv, - name: encName.ciphertext, - nameIv: encName.iv, - createdAt: encCreatedAt?.ciphertext, - createdAtIv: encCreatedAt?.iv, - lastModifiedAt: encLastModifiedAt.ciphertext, - lastModifiedAtIv: encLastModifiedAt.iv, - categories, - } satisfies FileInfoResponse), - ); -}; diff --git a/src/routes/api/file/[id]/delete/+server.ts b/src/routes/api/file/[id]/delete/+server.ts deleted file mode 100644 index 7baac25..0000000 --- a/src/routes/api/file/[id]/delete/+server.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { error, text } from "@sveltejs/kit"; -import { z } from "zod"; -import { authorize } from "$lib/server/modules/auth"; -import { deleteFile } from "$lib/server/services/file"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, params }) => { - const { userId } = await authorize(locals, "activeClient"); - - const zodRes = z - .object({ - id: z.coerce.number().int().positive(), - }) - .safeParse(params); - if (!zodRes.success) error(400, "Invalid path parameters"); - const { id } = zodRes.data; - - await deleteFile(userId, id); - return text("File deleted", { headers: { "Content-Type": "text/plain" } }); -}; diff --git a/src/routes/api/file/[id]/rename/+server.ts b/src/routes/api/file/[id]/rename/+server.ts deleted file mode 100644 index 343f146..0000000 --- a/src/routes/api/file/[id]/rename/+server.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { error, text } from "@sveltejs/kit"; -import { z } from "zod"; -import { authorize } from "$lib/server/modules/auth"; -import { fileRenameRequest } from "$lib/server/schemas"; -import { renameFile } from "$lib/server/services/file"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, params, request }) => { - const { userId } = await authorize(locals, "activeClient"); - - const paramsZodRes = z - .object({ - id: z.coerce.number().int().positive(), - }) - .safeParse(params); - if (!paramsZodRes.success) error(400, "Invalid path parameters"); - const { id } = paramsZodRes.data; - - const bodyZodRes = fileRenameRequest.safeParse(await request.json()); - if (!bodyZodRes.success) error(400, "Invalid request body"); - const { dekVersion, name, nameIv } = bodyZodRes.data; - - await renameFile(userId, id, new Date(dekVersion), { ciphertext: name, iv: nameIv }); - return text("File renamed", { headers: { "Content-Type": "text/plain" } }); -}; diff --git a/src/routes/api/file/[id]/thumbnail/+server.ts b/src/routes/api/file/[id]/thumbnail/+server.ts deleted file mode 100644 index 12c9347..0000000 --- a/src/routes/api/file/[id]/thumbnail/+server.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { error, json } from "@sveltejs/kit"; -import { z } from "zod"; -import { authorize } from "$lib/server/modules/auth"; -import { fileThumbnailInfoResponse, type FileThumbnailInfoResponse } from "$lib/server/schemas"; -import { getFileThumbnailInformation } from "$lib/server/services/file"; -import type { RequestHandler } from "./$types"; - -export const GET: RequestHandler = async ({ locals, params }) => { - const { userId } = await authorize(locals, "activeClient"); - - const zodRes = z - .object({ - id: z.coerce.number().int().positive(), - }) - .safeParse(params); - if (!zodRes.success) error(400, "Invalid path parameters"); - const { id } = zodRes.data; - - const { updatedAt, encContentIv } = await getFileThumbnailInformation(userId, id); - return json( - fileThumbnailInfoResponse.parse({ - updatedAt: updatedAt.toISOString(), - contentIv: encContentIv, - } satisfies FileThumbnailInfoResponse), - ); -}; diff --git a/src/routes/api/file/list/+server.ts b/src/routes/api/file/list/+server.ts deleted file mode 100644 index c1b6888..0000000 --- a/src/routes/api/file/list/+server.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { json } from "@sveltejs/kit"; -import { authorize } from "$lib/server/modules/auth"; -import { fileListResponse, type FileListResponse } from "$lib/server/schemas"; -import { getFileList } from "$lib/server/services/file"; -import type { RequestHandler } from "./$types"; - -export const GET: RequestHandler = async ({ locals }) => { - const { userId } = await authorize(locals, "activeClient"); - const { files } = await getFileList(userId); - return json(fileListResponse.parse({ files } satisfies FileListResponse)); -}; diff --git a/src/routes/api/file/scanDuplicates/+server.ts b/src/routes/api/file/scanDuplicates/+server.ts deleted file mode 100644 index fb41b43..0000000 --- a/src/routes/api/file/scanDuplicates/+server.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { error, json } from "@sveltejs/kit"; -import { authorize } from "$lib/server/modules/auth"; -import { - duplicateFileScanRequest, - duplicateFileScanResponse, - type DuplicateFileScanResponse, -} from "$lib/server/schemas"; -import { scanDuplicateFiles } from "$lib/server/services/file"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, request }) => { - const { userId } = await authorize(locals, "activeClient"); - - const zodRes = duplicateFileScanRequest.safeParse(await request.json()); - if (!zodRes.success) error(400, "Invalid request body"); - const { hskVersion, contentHmac } = zodRes.data; - - const { files } = await scanDuplicateFiles(userId, hskVersion, contentHmac); - return json(duplicateFileScanResponse.parse({ files } satisfies DuplicateFileScanResponse)); -}; diff --git a/src/routes/api/file/scanMissingThumbnails/+server.ts b/src/routes/api/file/scanMissingThumbnails/+server.ts deleted file mode 100644 index bf2a2a6..0000000 --- a/src/routes/api/file/scanMissingThumbnails/+server.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { json } from "@sveltejs/kit"; -import { authorize } from "$lib/server/modules/auth"; -import { - missingThumbnailFileScanResponse, - type MissingThumbnailFileScanResponse, -} from "$lib/server/schemas/file"; -import { scanMissingFileThumbnails } from "$lib/server/services/file"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals }) => { - const { userId } = await authorize(locals, "activeClient"); - const { files } = await scanMissingFileThumbnails(userId); - return json( - missingThumbnailFileScanResponse.parse({ files } satisfies MissingThumbnailFileScanResponse), - ); -}; diff --git a/src/trpc/client.ts b/src/trpc/client.ts index dbf4e80..cb1e8c5 100644 --- a/src/trpc/client.ts +++ b/src/trpc/client.ts @@ -1,4 +1,5 @@ import { createTRPCClient, httpBatchLink } from "@trpc/client"; +import superjson from "superjson"; import { browser } from "$app/environment"; import type { AppRouter } from "./router.server"; @@ -7,6 +8,7 @@ const createClient = (fetch: typeof globalThis.fetch) => links: [ httpBatchLink({ url: "/api/trpc", + transformer: superjson, fetch, }), ], diff --git a/src/trpc/init.server.ts b/src/trpc/init.server.ts index 15a35fa..8b88157 100644 --- a/src/trpc/init.server.ts +++ b/src/trpc/init.server.ts @@ -1,10 +1,12 @@ import type { RequestEvent } from "@sveltejs/kit"; import { initTRPC, TRPCError } from "@trpc/server"; +import superjson from "superjson"; import { authorizeMiddleware, authorizeClientMiddleware } from "./middlewares/authorize"; -export const createContext = (event: RequestEvent) => event; +export type Context = Awaited>; -export const t = initTRPC.context>>().create(); +export const createContext = (event: RequestEvent) => event; +export const t = initTRPC.context().create({ transformer: superjson }); export const router = t.router; export const publicProcedure = t.procedure; diff --git a/src/trpc/router.server.ts b/src/trpc/router.server.ts index 3c44bea..3d05e93 100644 --- a/src/trpc/router.server.ts +++ b/src/trpc/router.server.ts @@ -1,10 +1,21 @@ import type { RequestEvent } from "@sveltejs/kit"; import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server"; import { createContext, router } from "./init.server"; -import { clientRouter, hskRouter, mekRouter, userRouter } from "./routers"; +import { + categoryRouter, + clientRouter, + directoryRouter, + fileRouter, + hskRouter, + mekRouter, + userRouter, +} from "./routers"; export const appRouter = router({ + category: categoryRouter, client: clientRouter, + directory: directoryRouter, + file: fileRouter, hsk: hskRouter, mek: mekRouter, user: userRouter, diff --git a/src/trpc/routers/category.ts b/src/trpc/routers/category.ts new file mode 100644 index 0000000..f002421 --- /dev/null +++ b/src/trpc/routers/category.ts @@ -0,0 +1,194 @@ +import { TRPCError } from "@trpc/server"; +import { z } from "zod"; +import { CategoryRepo, FileRepo, IntegrityError } from "$lib/server/db"; +import { categoryIdSchema } from "$lib/server/schemas"; +import { router, roleProcedure } from "../init.server"; + +const categoryRouter = router({ + get: roleProcedure["activeClient"] + .input( + z.object({ + id: categoryIdSchema, + }), + ) + .query(async ({ ctx, input }) => { + const category = + input.id !== "root" + ? await CategoryRepo.getCategory(ctx.session.userId, input.id) + : undefined; + if (category === null) { + throw new TRPCError({ code: "NOT_FOUND", message: "Invalid category id" }); + } + + const categories = await CategoryRepo.getAllCategoriesByParent(ctx.session.userId, input.id); + return { + metadata: category && { + parent: category.parentId, + mekVersion: category.mekVersion, + dek: category.encDek, + dekVersion: category.dekVersion, + name: category.encName.ciphertext, + nameIv: category.encName.iv, + }, + subCategories: categories.map(({ id }) => id), + }; + }), + + create: roleProcedure["activeClient"] + .input( + z.object({ + parent: categoryIdSchema, + mekVersion: z.number().int().positive(), + dek: z.string().base64().nonempty(), + dekVersion: z.date(), + name: z.string().base64().nonempty(), + nameIv: z.string().base64().nonempty(), + }), + ) + .mutation(async ({ ctx, input }) => { + const oneMinuteAgo = new Date(Date.now() - 60 * 1000); + const oneMinuteLater = new Date(Date.now() + 60 * 1000); + if (input.dekVersion <= oneMinuteAgo || input.dekVersion >= oneMinuteLater) { + throw new TRPCError({ code: "BAD_REQUEST", message: "Invalid DEK version" }); + } + + try { + await CategoryRepo.registerCategory({ + parentId: input.parent, + userId: ctx.session.userId, + mekVersion: input.mekVersion, + encDek: input.dek, + dekVersion: input.dekVersion, + encName: { ciphertext: input.name, iv: input.nameIv }, + }); + } catch (e) { + if (e instanceof IntegrityError && e.message === "Inactive MEK version") { + throw new TRPCError({ code: "BAD_REQUEST", message: e.message }); + } + throw e; + } + }), + + rename: roleProcedure["activeClient"] + .input( + z.object({ + id: z.number().int().positive(), + dekVersion: z.date(), + name: z.string().base64().nonempty(), + nameIv: z.string().base64().nonempty(), + }), + ) + .mutation(async ({ ctx, input }) => { + try { + await CategoryRepo.setCategoryEncName(ctx.session.userId, input.id, input.dekVersion, { + ciphertext: input.name, + iv: input.nameIv, + }); + } catch (e) { + if (e instanceof IntegrityError) { + if (e.message === "Category not found") { + throw new TRPCError({ code: "NOT_FOUND", message: "Invalid category id" }); + } else if (e.message === "Invalid DEK version") { + throw new TRPCError({ code: "BAD_REQUEST", message: e.message }); + } + } + throw e; + } + }), + + delete: roleProcedure["activeClient"] + .input( + z.object({ + id: z.number().int().positive(), + }), + ) + .mutation(async ({ ctx, input }) => { + try { + await CategoryRepo.unregisterCategory(ctx.session.userId, input.id); + } catch (e) { + if (e instanceof IntegrityError && e.message === "Category not found") { + throw new TRPCError({ code: "NOT_FOUND", message: "Invalid category id" }); + } + throw e; + } + }), + + files: roleProcedure["activeClient"] + .input( + z.object({ + id: z.number().int().positive(), + recurse: z.boolean().default(false), + }), + ) + .query(async ({ ctx, input }) => { + const category = await CategoryRepo.getCategory(ctx.session.userId, input.id); + if (!category) { + throw new TRPCError({ code: "NOT_FOUND", message: "Invalid category id" }); + } + + const files = await FileRepo.getAllFilesByCategory( + ctx.session.userId, + input.id, + input.recurse, + ); + return files.map(({ id, isRecursive }) => ({ file: id, isRecursive })); + }), + + addFile: roleProcedure["activeClient"] + .input( + z.object({ + id: z.number().int().positive(), + file: z.number().int().positive(), + }), + ) + .mutation(async ({ ctx, input }) => { + const [category, file] = await Promise.all([ + CategoryRepo.getCategory(ctx.session.userId, input.id), + FileRepo.getFile(ctx.session.userId, input.file), + ]); + if (!category) { + throw new TRPCError({ code: "NOT_FOUND", message: "Invalid category id" }); + } else if (!file) { + throw new TRPCError({ code: "NOT_FOUND", message: "Invalid file id" }); + } + + try { + await FileRepo.addFileToCategory(input.file, input.id); + } catch (e) { + if (e instanceof IntegrityError && e.message === "File already added to category") { + throw new TRPCError({ code: "BAD_REQUEST", message: "File already added" }); + } + throw e; + } + }), + + removeFile: roleProcedure["activeClient"] + .input( + z.object({ + id: z.number().int().positive(), + file: z.number().int().positive(), + }), + ) + .mutation(async ({ ctx, input }) => { + const [category, file] = await Promise.all([ + CategoryRepo.getCategory(ctx.session.userId, input.id), + FileRepo.getFile(ctx.session.userId, input.file), + ]); + if (!category) { + throw new TRPCError({ code: "NOT_FOUND", message: "Invalid category id" }); + } else if (!file) { + throw new TRPCError({ code: "NOT_FOUND", message: "Invalid file id" }); + } + + try { + await FileRepo.removeFileFromCategory(input.file, input.id); + } catch (e) { + if (e instanceof IntegrityError && e.message === "File not found in category") { + throw new TRPCError({ code: "BAD_REQUEST", message: "File not added" }); + } + throw e; + } + }), +}); + +export default categoryRouter; diff --git a/src/trpc/routers/directory.ts b/src/trpc/routers/directory.ts new file mode 100644 index 0000000..70e3663 --- /dev/null +++ b/src/trpc/routers/directory.ts @@ -0,0 +1,125 @@ +import { TRPCError } from "@trpc/server"; +import { z } from "zod"; +import { FileRepo, IntegrityError } from "$lib/server/db"; +import { safeUnlink } from "$lib/server/modules/filesystem"; +import { directoryIdSchema } from "$lib/server/schemas"; +import { router, roleProcedure } from "../init.server"; + +const directoryRouter = router({ + get: roleProcedure["activeClient"] + .input( + z.object({ + id: directoryIdSchema, + }), + ) + .query(async ({ ctx, input }) => { + const directory = + input.id !== "root" ? await FileRepo.getDirectory(ctx.session.userId, input.id) : undefined; + if (directory === null) { + throw new TRPCError({ code: "NOT_FOUND", message: "Invalid directory id" }); + } + + const [directories, files] = await Promise.all([ + FileRepo.getAllDirectoriesByParent(ctx.session.userId, input.id), + FileRepo.getAllFilesByParent(ctx.session.userId, input.id), + ]); + return { + metadata: directory && { + parent: directory.parentId, + mekVersion: directory.mekVersion, + dek: directory.encDek, + dekVersion: directory.dekVersion, + name: directory.encName.ciphertext, + nameIv: directory.encName.iv, + }, + subDirectories: directories.map(({ id }) => id), + files: files.map(({ id }) => id), + }; + }), + + create: roleProcedure["activeClient"] + .input( + z.object({ + parent: directoryIdSchema, + mekVersion: z.number().int().positive(), + dek: z.string().base64().nonempty(), + dekVersion: z.date(), + name: z.string().base64().nonempty(), + nameIv: z.string().base64().nonempty(), + }), + ) + .mutation(async ({ ctx, input }) => { + const oneMinuteAgo = new Date(Date.now() - 60 * 1000); + const oneMinuteLater = new Date(Date.now() + 60 * 1000); + if (input.dekVersion <= oneMinuteAgo || input.dekVersion >= oneMinuteLater) { + throw new TRPCError({ code: "BAD_REQUEST", message: "Invalid DEK version" }); + } + + try { + await FileRepo.registerDirectory({ + parentId: input.parent, + userId: ctx.session.userId, + mekVersion: input.mekVersion, + encDek: input.dek, + dekVersion: input.dekVersion, + encName: { ciphertext: input.name, iv: input.nameIv }, + }); + } catch (e) { + if (e instanceof IntegrityError && e.message === "Inactive MEK version") { + throw new TRPCError({ code: "BAD_REQUEST", message: e.message }); + } + throw e; + } + }), + + rename: roleProcedure["activeClient"] + .input( + z.object({ + id: z.number().int().positive(), + dekVersion: z.date(), + name: z.string().base64().nonempty(), + nameIv: z.string().base64().nonempty(), + }), + ) + .mutation(async ({ ctx, input }) => { + try { + await FileRepo.setDirectoryEncName(ctx.session.userId, input.id, input.dekVersion, { + ciphertext: input.name, + iv: input.nameIv, + }); + } catch (e) { + if (e instanceof IntegrityError) { + if (e.message === "Directory not found") { + throw new TRPCError({ code: "NOT_FOUND", message: "Invalid directory id" }); + } else if (e.message === "Invalid DEK version") { + throw new TRPCError({ code: "BAD_REQUEST", message: e.message }); + } + } + throw e; + } + }), + + delete: roleProcedure["activeClient"] + .input( + z.object({ + id: z.number().int().positive(), + }), + ) + .mutation(async ({ ctx, input }) => { + try { + const files = await FileRepo.unregisterDirectory(ctx.session.userId, input.id); + files.forEach(({ path, thumbnailPath }) => { + safeUnlink(path); // Intended + safeUnlink(thumbnailPath); // Intended + }); + return { deletedFiles: files.map(({ id }) => id) }; + } catch (e) { + if (e instanceof IntegrityError && e.message === "Directory not found") { + throw new TRPCError({ code: "NOT_FOUND", message: "Invalid directory id" }); + } + throw e; + } + }), +}); + +export default directoryRouter; diff --git a/src/trpc/routers/file.ts b/src/trpc/routers/file.ts new file mode 100644 index 0000000..b37032b --- /dev/null +++ b/src/trpc/routers/file.ts @@ -0,0 +1,122 @@ +import { TRPCError } from "@trpc/server"; +import { z } from "zod"; +import { FileRepo, MediaRepo, IntegrityError } from "$lib/server/db"; +import { safeUnlink } from "$lib/server/modules/filesystem"; +import { router, roleProcedure } from "../init.server"; + +const fileRouter = router({ + get: roleProcedure["activeClient"] + .input( + z.object({ + id: z.number().int().positive(), + }), + ) + .query(async ({ ctx, input }) => { + const file = await FileRepo.getFile(ctx.session.userId, input.id); + if (!file) { + throw new TRPCError({ code: "NOT_FOUND", message: "Invalid file id" }); + } + + const categories = await FileRepo.getAllFileCategories(input.id); + return { + parent: file.parentId, + mekVersion: file.mekVersion, + dek: file.encDek, + dekVersion: file.dekVersion, + contentType: file.contentType, + contentIv: file.encContentIv, + name: file.encName.ciphertext, + nameIv: file.encName.iv, + createdAt: file.encCreatedAt?.ciphertext, + createdAtIv: file.encCreatedAt?.iv, + lastModifiedAt: file.encLastModifiedAt.ciphertext, + lastModifiedAtIv: file.encLastModifiedAt.iv, + categories: categories.map(({ id }) => id), + }; + }), + + list: roleProcedure["activeClient"].query(async ({ ctx }) => { + return await FileRepo.getAllFileIds(ctx.session.userId); + }), + + listByHash: roleProcedure["activeClient"] + .input( + z.object({ + hskVersion: z.number().int().positive(), + contentHmac: z.string().base64().nonempty(), + }), + ) + .query(async ({ ctx, input }) => { + return await FileRepo.getAllFileIdsByContentHmac( + ctx.session.userId, + input.hskVersion, + input.contentHmac, + ); + }), + + listWithoutThumbnail: roleProcedure["activeClient"].query(async ({ ctx }) => { + return await MediaRepo.getMissingFileThumbnails(ctx.session.userId); + }), + + rename: roleProcedure["activeClient"] + .input( + z.object({ + id: z.number().int().positive(), + dekVersion: z.date(), + name: z.string().base64().nonempty(), + nameIv: z.string().base64().nonempty(), + }), + ) + .mutation(async ({ ctx, input }) => { + try { + await FileRepo.setFileEncName(ctx.session.userId, input.id, input.dekVersion, { + ciphertext: input.name, + iv: input.nameIv, + }); + } catch (e) { + if (e instanceof IntegrityError) { + if (e.message === "File not found") { + throw new TRPCError({ code: "NOT_FOUND", message: "Invalid file id" }); + } else if (e.message === "Invalid DEK version") { + throw new TRPCError({ code: "BAD_REQUEST", message: e.message }); + } + } + throw e; + } + }), + + delete: roleProcedure["activeClient"] + .input( + z.object({ + id: z.number().int().positive(), + }), + ) + .mutation(async ({ ctx, input }) => { + try { + const { path, thumbnailPath } = await FileRepo.unregisterFile(ctx.session.userId, input.id); + safeUnlink(path); // Intended + safeUnlink(thumbnailPath); // Intended + } catch (e) { + if (e instanceof IntegrityError && e.message === "File not found") { + throw new TRPCError({ code: "NOT_FOUND", message: "Invalid file id" }); + } + throw e; + } + }), + + thumbnail: roleProcedure["activeClient"] + .input( + z.object({ + id: z.number().int().positive(), + }), + ) + .query(async ({ ctx, input }) => { + const thumbnail = await MediaRepo.getFileThumbnail(ctx.session.userId, input.id); + if (!thumbnail) { + throw new TRPCError({ code: "NOT_FOUND", message: "File or its thumbnail not found" }); + } + return { updatedAt: thumbnail.updatedAt, contentIv: thumbnail.encContentIv }; + }), +}); + +export default fileRouter; diff --git a/src/trpc/routers/index.ts b/src/trpc/routers/index.ts index 26ac7b2..c943728 100644 --- a/src/trpc/routers/index.ts +++ b/src/trpc/routers/index.ts @@ -1,4 +1,7 @@ +export { default as categoryRouter } from "./category"; export { default as clientRouter } from "./client"; +export { default as directoryRouter } from "./directory"; +export { default as fileRouter } from "./file"; export { default as hskRouter } from "./hsk"; export { default as mekRouter } from "./mek"; export { default as userRouter } from "./user"; diff --git a/src/trpc/routers/user.ts b/src/trpc/routers/user.ts index 37b2460..ec514f1 100644 --- a/src/trpc/routers/user.ts +++ b/src/trpc/routers/user.ts @@ -1,10 +1,9 @@ import { TRPCError } from "@trpc/server"; -import { z } from "zod"; import { UserRepo } from "$lib/server/db"; import { router, roleProcedure } from "../init.server"; const userRouter = router({ - info: roleProcedure.any.query(async ({ ctx }) => { + get: roleProcedure["any"].query(async ({ ctx }) => { const user = await UserRepo.getUser(ctx.session.userId); if (!user) { throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Invalid session id" }); @@ -12,16 +11,6 @@ const userRouter = router({ return { email: user.email, nickname: user.nickname }; }), - - changeNickname: roleProcedure.any - .input( - z.object({ - newNickname: z.string().trim().min(2).max(8), - }), - ) - .mutation(async ({ ctx, input }) => { - await UserRepo.setUserNickname(ctx.session.userId, input.newNickname); - }), }); export default userRouter; From b92b4a0b1bf52acf6d0f40ce4f1731f73454c448 Mon Sep 17 00:00:00 2001 From: static Date: Thu, 25 Dec 2025 22:53:51 +0900 Subject: [PATCH 10/23] =?UTF-8?q?Zod=204=20=EB=A7=88=EC=9D=B4=EA=B7=B8?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 42 +- pnpm-lock.yaml | 1334 ++++++++++++--------------- src/lib/modules/key.ts | 10 +- src/lib/server/schemas/auth.ts | 14 +- src/lib/server/schemas/category.ts | 2 +- src/lib/server/schemas/directory.ts | 2 +- src/lib/server/schemas/file.ts | 30 +- src/trpc/routers/category.ts | 26 +- src/trpc/routers/client.ts | 8 +- src/trpc/routers/directory.ts | 16 +- src/trpc/routers/file.ts | 16 +- src/trpc/routers/hsk.ts | 4 +- src/trpc/routers/mek.ts | 4 +- 13 files changed, 655 insertions(+), 853 deletions(-) diff --git a/package.json b/package.json index b1425b0..ce46336 100644 --- a/package.json +++ b/package.json @@ -17,52 +17,52 @@ }, "devDependencies": { "@eslint/compat": "^1.4.1", - "@iconify-json/material-symbols": "^1.2.44", + "@iconify-json/material-symbols": "^1.2.50", "@sveltejs/adapter-node": "^5.4.0", - "@sveltejs/kit": "^2.48.4", + "@sveltejs/kit": "^2.49.2", "@sveltejs/vite-plugin-svelte": "^6.2.1", - "@trpc/client": "^11.7.1", + "@trpc/client": "^11.8.1", "@types/file-saver": "^2.0.7", "@types/ms": "^0.7.34", "@types/node-schedule": "^2.1.8", - "@types/pg": "^8.15.6", - "autoprefixer": "^10.4.21", - "axios": "^1.13.1", + "@types/pg": "^8.16.0", + "autoprefixer": "^10.4.23", + "axios": "^1.13.2", "dexie": "^4.2.1", - "eslint": "^9.39.0", + "eslint": "^9.39.2", "eslint-config-prettier": "^10.1.8", - "eslint-plugin-svelte": "^3.13.0", + "eslint-plugin-svelte": "^3.13.1", "eslint-plugin-tailwindcss": "^3.18.2", - "exifreader": "^4.32.0", + "exifreader": "^4.33.1", "file-saver": "^2.0.5", "globals": "^16.5.0", "heic2any": "^0.0.4", "kysely-ctl": "^0.19.0", - "lru-cache": "^11.2.2", + "lru-cache": "^11.2.4", "mime": "^4.1.0", "p-limit": "^7.2.0", - "prettier": "^3.6.2", - "prettier-plugin-svelte": "^3.4.0", - "prettier-plugin-tailwindcss": "^0.7.1", - "svelte": "^5.43.2", - "svelte-check": "^4.3.3", - "tailwindcss": "^3.4.18", + "prettier": "^3.7.4", + "prettier-plugin-svelte": "^3.4.1", + "prettier-plugin-tailwindcss": "^0.7.2", + "svelte": "^5.46.1", + "svelte-check": "^4.3.5", + "tailwindcss": "^3.4.19", "typescript": "^5.9.3", - "typescript-eslint": "^8.46.2", + "typescript-eslint": "^8.50.1", "unplugin-icons": "^22.5.0", - "vite": "^7.1.12" + "vite": "^7.3.0" }, "dependencies": { "@fastify/busboy": "^3.2.0", - "@trpc/server": "^11.7.1", + "@trpc/server": "^11.8.1", "argon2": "^0.44.0", - "kysely": "^0.28.8", + "kysely": "^0.28.9", "ms": "^2.1.3", "node-schedule": "^2.1.1", "pg": "^8.16.3", "superjson": "^2.2.6", "uuid": "^13.0.0", - "zod": "^3.25.76" + "zod": "^4.2.1" }, "engines": { "node": "^22.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b428b0f..e3de21a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,14 +12,14 @@ importers: specifier: ^3.2.0 version: 3.2.0 '@trpc/server': - specifier: ^11.7.1 - version: 11.7.1(typescript@5.9.3) + specifier: ^11.8.1 + version: 11.8.1(typescript@5.9.3) argon2: specifier: ^0.44.0 version: 0.44.0 kysely: - specifier: ^0.28.8 - version: 0.28.8 + specifier: ^0.28.9 + version: 0.28.9 ms: specifier: ^2.1.3 version: 2.1.3 @@ -36,27 +36,27 @@ importers: specifier: ^13.0.0 version: 13.0.0 zod: - specifier: ^3.25.76 - version: 3.25.76 + specifier: ^4.2.1 + version: 4.2.1 devDependencies: '@eslint/compat': specifier: ^1.4.1 - version: 1.4.1(eslint@9.39.0(jiti@1.21.7)) + version: 1.4.1(eslint@9.39.2(jiti@1.21.7)) '@iconify-json/material-symbols': - specifier: ^1.2.44 - version: 1.2.44 + specifier: ^1.2.50 + version: 1.2.50 '@sveltejs/adapter-node': specifier: ^5.4.0 - version: 5.4.0(@sveltejs/kit@2.48.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0))) + version: 5.4.0(@sveltejs/kit@2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0))) '@sveltejs/kit': - specifier: ^2.48.4 - version: 2.48.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)) + specifier: ^2.49.2 + version: 2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0)) '@sveltejs/vite-plugin-svelte': specifier: ^6.2.1 - version: 6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)) + version: 6.2.1(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0)) '@trpc/client': - specifier: ^11.7.1 - version: 11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3) + specifier: ^11.8.1 + version: 11.8.1(@trpc/server@11.8.1(typescript@5.9.3))(typescript@5.9.3) '@types/file-saver': specifier: ^2.0.7 version: 2.0.7 @@ -67,32 +67,32 @@ importers: specifier: ^2.1.8 version: 2.1.8 '@types/pg': - specifier: ^8.15.6 - version: 8.15.6 + specifier: ^8.16.0 + version: 8.16.0 autoprefixer: - specifier: ^10.4.21 - version: 10.4.21(postcss@8.5.6) + specifier: ^10.4.23 + version: 10.4.23(postcss@8.5.6) axios: - specifier: ^1.13.1 - version: 1.13.1 + specifier: ^1.13.2 + version: 1.13.2 dexie: specifier: ^4.2.1 version: 4.2.1 eslint: - specifier: ^9.39.0 - version: 9.39.0(jiti@1.21.7) + specifier: ^9.39.2 + version: 9.39.2(jiti@1.21.7) eslint-config-prettier: specifier: ^10.1.8 - version: 10.1.8(eslint@9.39.0(jiti@1.21.7)) + version: 10.1.8(eslint@9.39.2(jiti@1.21.7)) eslint-plugin-svelte: - specifier: ^3.13.0 - version: 3.13.0(eslint@9.39.0(jiti@1.21.7))(svelte@5.43.2) + specifier: ^3.13.1 + version: 3.13.1(eslint@9.39.2(jiti@1.21.7))(svelte@5.46.1) eslint-plugin-tailwindcss: specifier: ^3.18.2 - version: 3.18.2(tailwindcss@3.4.18(yaml@2.8.0)) + version: 3.18.2(tailwindcss@3.4.19(yaml@2.8.0)) exifreader: - specifier: ^4.32.0 - version: 4.32.0 + specifier: ^4.33.1 + version: 4.33.1 file-saver: specifier: ^2.0.5 version: 2.0.5 @@ -104,10 +104,10 @@ importers: version: 0.0.4 kysely-ctl: specifier: ^0.19.0 - version: 0.19.0(kysely@0.28.8)(typescript@5.9.3) + version: 0.19.0(kysely@0.28.9)(typescript@5.9.3) lru-cache: - specifier: ^11.2.2 - version: 11.2.2 + specifier: ^11.2.4 + version: 11.2.4 mime: specifier: ^4.1.0 version: 4.1.0 @@ -115,35 +115,35 @@ importers: specifier: ^7.2.0 version: 7.2.0 prettier: - specifier: ^3.6.2 - version: 3.6.2 + specifier: ^3.7.4 + version: 3.7.4 prettier-plugin-svelte: - specifier: ^3.4.0 - version: 3.4.0(prettier@3.6.2)(svelte@5.43.2) + specifier: ^3.4.1 + version: 3.4.1(prettier@3.7.4)(svelte@5.46.1) prettier-plugin-tailwindcss: - specifier: ^0.7.1 - version: 0.7.1(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.43.2))(prettier@3.6.2) + specifier: ^0.7.2 + version: 0.7.2(prettier-plugin-svelte@3.4.1(prettier@3.7.4)(svelte@5.46.1))(prettier@3.7.4) svelte: - specifier: ^5.43.2 - version: 5.43.2 + specifier: ^5.46.1 + version: 5.46.1 svelte-check: - specifier: ^4.3.3 - version: 4.3.3(picomatch@4.0.3)(svelte@5.43.2)(typescript@5.9.3) + specifier: ^4.3.5 + version: 4.3.5(picomatch@4.0.3)(svelte@5.46.1)(typescript@5.9.3) tailwindcss: - specifier: ^3.4.18 - version: 3.4.18(yaml@2.8.0) + specifier: ^3.4.19 + version: 3.4.19(yaml@2.8.0) typescript: specifier: ^5.9.3 version: 5.9.3 typescript-eslint: - specifier: ^8.46.2 - version: 8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3) + specifier: ^8.50.1 + version: 8.50.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) unplugin-icons: specifier: ^22.5.0 - version: 22.5.0(svelte@5.43.2) + version: 22.5.0(svelte@5.46.1) vite: - specifier: ^7.1.12 - version: 7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0) + specifier: ^7.3.0 + version: 7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0) packages: @@ -154,164 +154,161 @@ packages: '@antfu/install-pkg@1.1.0': resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==} - '@antfu/utils@9.3.0': - resolution: {integrity: sha512-9hFT4RauhcUzqOE4f1+frMKLZrgNog5b06I7VmZQV1BkvwvqrbC8EBZf3L1eEL2AKb6rNKjER0sEvJiSP1FXEA==} - '@epic-web/invariant@1.0.0': resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==} - '@esbuild/aix-ppc64@0.25.11': - resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==} + '@esbuild/aix-ppc64@0.27.2': + resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.11': - resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==} + '@esbuild/android-arm64@0.27.2': + resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.11': - resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==} + '@esbuild/android-arm@0.27.2': + resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.11': - resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==} + '@esbuild/android-x64@0.27.2': + resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.11': - resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==} + '@esbuild/darwin-arm64@0.27.2': + resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.11': - resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==} + '@esbuild/darwin-x64@0.27.2': + resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.11': - resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==} + '@esbuild/freebsd-arm64@0.27.2': + resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.11': - resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==} + '@esbuild/freebsd-x64@0.27.2': + resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.11': - resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==} + '@esbuild/linux-arm64@0.27.2': + resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.11': - resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==} + '@esbuild/linux-arm@0.27.2': + resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.11': - resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==} + '@esbuild/linux-ia32@0.27.2': + resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.11': - resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==} + '@esbuild/linux-loong64@0.27.2': + resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.11': - resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==} + '@esbuild/linux-mips64el@0.27.2': + resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.11': - resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==} + '@esbuild/linux-ppc64@0.27.2': + resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.11': - resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==} + '@esbuild/linux-riscv64@0.27.2': + resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.11': - resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==} + '@esbuild/linux-s390x@0.27.2': + resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.11': - resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==} + '@esbuild/linux-x64@0.27.2': + resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.11': - resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==} + '@esbuild/netbsd-arm64@0.27.2': + resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.11': - resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==} + '@esbuild/netbsd-x64@0.27.2': + resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.11': - resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==} + '@esbuild/openbsd-arm64@0.27.2': + resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.11': - resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==} + '@esbuild/openbsd-x64@0.27.2': + resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.11': - resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==} + '@esbuild/openharmony-arm64@0.27.2': + resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.25.11': - resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==} + '@esbuild/sunos-x64@0.27.2': + resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.11': - resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==} + '@esbuild/win32-arm64@0.27.2': + resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.11': - resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==} + '@esbuild/win32-ia32@0.27.2': + resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.11': - resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==} + '@esbuild/win32-x64@0.27.2': + resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -347,12 +344,12 @@ packages: resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/eslintrc@3.3.1': - resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + '@eslint/eslintrc@3.3.3': + resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.39.0': - resolution: {integrity: sha512-BIhe0sW91JGPiaF1mOuPy5v8NflqfjIcDNpC+LbW9f609WVRX1rArrhi6Z2ymvrAry9jw+5POTj4t2t62o8Bmw==} + '@eslint/js@9.39.2': + resolution: {integrity: sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.7': @@ -382,18 +379,14 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@iconify-json/material-symbols@1.2.44': - resolution: {integrity: sha512-NAJjhswaK9FxBeIzFFsNygws7wHtmAkBWhF4YEwn1NZIMbA+LNITqhUiq6sP5mOdKQqnoritFTlQaZ47a5BgBg==} + '@iconify-json/material-symbols@1.2.50': + resolution: {integrity: sha512-71tjHR70h46LHtBFab3fAd2V/wPTO7JMV5lKnRn3IcF303LaFgAlO0BZeTJDcmCv9d0snRZmnoLZAJVD7/eisw==} '@iconify/types@2.0.0': resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==} - '@iconify/utils@3.0.2': - resolution: {integrity: sha512-EfJS0rLfVuRuJRn4psJHtK2A9TqVnkxPpHY6lYHiB9+8eSuudsxbwMiavocG45ujOo6FJ+CIRlRnlOGinzkaGQ==} - - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} + '@iconify/utils@3.1.0': + resolution: {integrity: sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==} '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -427,10 +420,6 @@ packages: resolution: {integrity: sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==} engines: {node: '>=10'} - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} @@ -470,121 +459,121 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.52.5': - resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} + '@rollup/rollup-android-arm-eabi@4.54.0': + resolution: {integrity: sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.52.5': - resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==} + '@rollup/rollup-android-arm64@4.54.0': + resolution: {integrity: sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.52.5': - resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==} + '@rollup/rollup-darwin-arm64@4.54.0': + resolution: {integrity: sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.52.5': - resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==} + '@rollup/rollup-darwin-x64@4.54.0': + resolution: {integrity: sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.52.5': - resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==} + '@rollup/rollup-freebsd-arm64@4.54.0': + resolution: {integrity: sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.52.5': - resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==} + '@rollup/rollup-freebsd-x64@4.54.0': + resolution: {integrity: sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.52.5': - resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.54.0': + resolution: {integrity: sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.52.5': - resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} + '@rollup/rollup-linux-arm-musleabihf@4.54.0': + resolution: {integrity: sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.52.5': - resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} + '@rollup/rollup-linux-arm64-gnu@4.54.0': + resolution: {integrity: sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.52.5': - resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} + '@rollup/rollup-linux-arm64-musl@4.54.0': + resolution: {integrity: sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.52.5': - resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} + '@rollup/rollup-linux-loong64-gnu@4.54.0': + resolution: {integrity: sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.52.5': - resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} + '@rollup/rollup-linux-ppc64-gnu@4.54.0': + resolution: {integrity: sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.52.5': - resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} + '@rollup/rollup-linux-riscv64-gnu@4.54.0': + resolution: {integrity: sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.52.5': - resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} + '@rollup/rollup-linux-riscv64-musl@4.54.0': + resolution: {integrity: sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.52.5': - resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} + '@rollup/rollup-linux-s390x-gnu@4.54.0': + resolution: {integrity: sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.52.5': - resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} + '@rollup/rollup-linux-x64-gnu@4.54.0': + resolution: {integrity: sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.52.5': - resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} + '@rollup/rollup-linux-x64-musl@4.54.0': + resolution: {integrity: sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.52.5': - resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} + '@rollup/rollup-openharmony-arm64@4.54.0': + resolution: {integrity: sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.52.5': - resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==} + '@rollup/rollup-win32-arm64-msvc@4.54.0': + resolution: {integrity: sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.52.5': - resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==} + '@rollup/rollup-win32-ia32-msvc@4.54.0': + resolution: {integrity: sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.52.5': - resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==} + '@rollup/rollup-win32-x64-gnu@4.54.0': + resolution: {integrity: sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.52.5': - resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==} + '@rollup/rollup-win32-x64-msvc@4.54.0': + resolution: {integrity: sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg==} cpu: [x64] os: [win32] - '@standard-schema/spec@1.0.0': - resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@sveltejs/acorn-typescript@1.0.6': - resolution: {integrity: sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ==} + '@sveltejs/acorn-typescript@1.0.8': + resolution: {integrity: sha512-esgN+54+q0NjB0Y/4BomT9samII7jGwNy/2a3wNZbT2A2RpmXsXwUt24LvLhx6jUq2gVk4cWEvcRO6MFQbOfNA==} peerDependencies: acorn: ^8.9.0 @@ -593,8 +582,8 @@ packages: peerDependencies: '@sveltejs/kit': ^2.4.0 - '@sveltejs/kit@2.48.4': - resolution: {integrity: sha512-TGFX1pZUt9qqY20Cv5NyYvy0iLWHf2jXi8s+eCGsig7jQMdwZWKUFMR6TbvFNhfDSUpc1sH/Y5EHv20g3HHA3g==} + '@sveltejs/kit@2.49.2': + resolution: {integrity: sha512-Vp3zX/qlwerQmHMP6x0Ry1oY7eKKRcOWGc2P59srOp4zcqyn+etJyQpELgOi4+ZSUgteX8Y387NuwruLgGXLUQ==} engines: {node: '>=18.13'} hasBin: true peerDependencies: @@ -621,14 +610,14 @@ packages: svelte: ^5.0.0 vite: ^6.3.0 || ^7.0.0 - '@trpc/client@11.7.1': - resolution: {integrity: sha512-uOnAjElKI892/U6aQMcBHYs3x7mme3Cvv1F87ytBL56rBvs7+DyK7r43zgaXKf13+GtPEI6ex5xjVUfyDW8XcQ==} + '@trpc/client@11.8.1': + resolution: {integrity: sha512-L/SJFGanr9xGABmuDoeXR4xAdHJmsXsiF9OuH+apecJ+8sUITzVT1EPeqp0ebqA6lBhEl5pPfg3rngVhi/h60Q==} peerDependencies: - '@trpc/server': 11.7.1 + '@trpc/server': 11.8.1 typescript: '>=5.7.2' - '@trpc/server@11.7.1': - resolution: {integrity: sha512-N3U8LNLIP4g9C7LJ/sLkjuPHwqlvE3bnspzC4DEFVdvx2+usbn70P80E3wj5cjOTLhmhRiwJCSXhlB+MHfGeCw==} + '@trpc/server@11.8.1': + resolution: {integrity: sha512-P4rzZRpEL7zDFgjxK65IdyH0e41FMFfTkQkuq0BA5tKcr7E6v9/v38DEklCpoDN6sPiB1Sigy/PUEzHENhswDA==} peerDependencies: typescript: '>=5.7.2' @@ -650,72 +639,72 @@ packages: '@types/node-schedule@2.1.8': resolution: {integrity: sha512-k00g6Yj/oUg/CDC+MeLHUzu0+OFxWbIqrFfDiLi6OPKxTujvpv29mHGM8GtKr7B+9Vv92FcK/8mRqi1DK5f3hA==} - '@types/node@24.9.2': - resolution: {integrity: sha512-uWN8YqxXxqFMX2RqGOrumsKeti4LlmIMIyV0lgut4jx7KQBcBiW6vkDtIBvHnHIquwNfJhk8v2OtmO8zXWHfPA==} + '@types/node@25.0.3': + resolution: {integrity: sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==} - '@types/pg@8.15.6': - resolution: {integrity: sha512-NoaMtzhxOrubeL/7UZuNTrejB4MPAJ0RpxZqXQf2qXuVlTPuG6Y8p4u9dKRaue4yjmC7ZhzVO2/Yyyn25znrPQ==} + '@types/pg@8.16.0': + resolution: {integrity: sha512-RmhMd/wD+CF8Dfo+cVIy3RR5cl8CyfXQ0tGgW6XBL8L4LM/UTEbNXYRbLwU6w+CgrKBNbrQWt4FUtTfaU5jSYQ==} '@types/resolve@1.20.2': resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} - '@typescript-eslint/eslint-plugin@8.46.2': - resolution: {integrity: sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w==} + '@typescript-eslint/eslint-plugin@8.50.1': + resolution: {integrity: sha512-PKhLGDq3JAg0Jk/aK890knnqduuI/Qj+udH7wCf0217IGi4gt+acgCyPVe79qoT+qKUvHMDQkwJeKW9fwl8Cyw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.46.2 + '@typescript-eslint/parser': ^8.50.1 eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/parser@8.46.2': - resolution: {integrity: sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==} + '@typescript-eslint/parser@8.50.1': + resolution: {integrity: sha512-hM5faZwg7aVNa819m/5r7D0h0c9yC4DUlWAOvHAtISdFTc8xB86VmX5Xqabrama3wIPJ/q9RbGS1worb6JfnMg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/project-service@8.46.2': - resolution: {integrity: sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==} + '@typescript-eslint/project-service@8.50.1': + resolution: {integrity: sha512-E1ur1MCVf+YiP89+o4Les/oBAVzmSbeRB0MQLfSlYtbWU17HPxZ6Bhs5iYmKZRALvEuBoXIZMOIRRc/P++Ortg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/scope-manager@8.46.2': - resolution: {integrity: sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==} + '@typescript-eslint/scope-manager@8.50.1': + resolution: {integrity: sha512-mfRx06Myt3T4vuoHaKi8ZWNTPdzKPNBhiblze5N50//TSHOAQQevl/aolqA/BcqqbJ88GUnLqjjcBc8EWdBcVw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.46.2': - resolution: {integrity: sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==} + '@typescript-eslint/tsconfig-utils@8.50.1': + resolution: {integrity: sha512-ooHmotT/lCWLXi55G4mvaUF60aJa012QzvLK0Y+Mp4WdSt17QhMhWOaBWeGTFVkb2gDgBe19Cxy1elPXylslDw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/type-utils@8.46.2': - resolution: {integrity: sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==} + '@typescript-eslint/type-utils@8.50.1': + resolution: {integrity: sha512-7J3bf022QZE42tYMO6SL+6lTPKFk/WphhRPe9Tw/el+cEwzLz1Jjz2PX3GtGQVxooLDKeMVmMt7fWpYRdG5Etg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/types@8.46.2': - resolution: {integrity: sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==} + '@typescript-eslint/types@8.50.1': + resolution: {integrity: sha512-v5lFIS2feTkNyMhd7AucE/9j/4V9v5iIbpVRncjk/K0sQ6Sb+Np9fgYS/63n6nwqahHQvbmujeBL7mp07Q9mlA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.46.2': - resolution: {integrity: sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==} + '@typescript-eslint/typescript-estree@8.50.1': + resolution: {integrity: sha512-woHPdW+0gj53aM+cxchymJCrh0cyS7BTIdcDxWUNsclr9VDkOSbqC13juHzxOmQ22dDkMZEpZB+3X1WpUvzgVQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/utils@8.46.2': - resolution: {integrity: sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==} + '@typescript-eslint/utils@8.50.1': + resolution: {integrity: sha512-lCLp8H1T9T7gPbEuJSnHwnSuO9mDf8mfK/Nion5mZmiEaQD9sWf9W4dfeFqRyqRjF06/kBuTmAqcs9sewM2NbQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' - '@typescript-eslint/visitor-keys@8.46.2': - resolution: {integrity: sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==} + '@typescript-eslint/visitor-keys@8.50.1': + resolution: {integrity: sha512-IrDKrw7pCRUR94zeuCSUWQ+w8JEf5ZX5jl/e6AHGSLi1/zIr0lgutfn/7JpfCey+urpgQEdrZVYzCaVVKiTwhQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@xmldom/xmldom@0.9.8': @@ -735,22 +724,10 @@ packages: ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-regex@6.2.2: - resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} - engines: {node: '>=12'} - ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@6.2.3: - resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} - engines: {node: '>=12'} - any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} @@ -775,15 +752,15 @@ packages: asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - autoprefixer@10.4.21: - resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==} + autoprefixer@10.4.23: + resolution: {integrity: sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: postcss: ^8.1.0 - axios@1.13.1: - resolution: {integrity: sha512-hU4EGxxt+j7TQijx1oYdAjw4xuIp1wRQSsbMFwSthCWeBQur1eF+qJ5iQ5sN3Tw8YRzQNKb8jszgBdMDVqwJcw==} + axios@1.13.2: + resolution: {integrity: sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==} axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} @@ -792,8 +769,8 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - baseline-browser-mapping@2.8.23: - resolution: {integrity: sha512-616V5YX4bepJFzNyOfce5Fa8fDJMfoxzOIzDCZwaGL8MKVpFrXqfNUoIpRn9YMI5pXf/VKgzjB4htFMsFKKdiQ==} + baseline-browser-mapping@2.9.11: + resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==} hasBin: true binary-extensions@2.3.0: @@ -810,15 +787,15 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.27.0: - resolution: {integrity: sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==} + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - c12@3.3.1: - resolution: {integrity: sha512-LcWQ01LT9tkoUINHgpIOv3mMs+Abv7oVCrtpMRi1PaapVEpWoMga5WuT7/DqFTu7URP9ftbOmimNw1KNIGh9DQ==} + c12@3.3.3: + resolution: {integrity: sha512-750hTRvgBy5kcMNPdh95Qo+XUBeGo8C7nsKSmedDmaQI+E0r82DwHeM6vBewDe4rGFbnxoa4V9pw+sPh5+Iz8Q==} peerDependencies: - magicast: ^0.3.5 + magicast: '*' peerDependenciesMeta: magicast: optional: true @@ -835,8 +812,8 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - caniuse-lite@1.0.30001752: - resolution: {integrity: sha512-vKUk7beoukxE47P5gcVNKkDRzXdVofotshHwfR9vmpeFKxmI5PBpgOMC18LUJUA/DvJ70Y7RveasIBraqsyO/g==} + caniuse-lite@1.0.30001761: + resolution: {integrity: sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -850,6 +827,10 @@ packages: resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} engines: {node: '>= 14.16.0'} + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} + citty@0.1.6: resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} @@ -940,8 +921,8 @@ packages: destr@2.0.5: resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} - devalue@5.4.2: - resolution: {integrity: sha512-MwPZTKEPK2k8Qgfmqrd48ZKVvzSQjgW0lXLxiIBA8dQjtf/6mw6pggHNLcyDKyf+fI6eXxlQwPsfaCMTU5U+Bw==} + devalue@5.6.1: + resolution: {integrity: sha512-jDwizj+IlEZBunHcOuuFVBnIMPAEHvTsJj0BcIp94xYguLRVBcXO853px/MyIJvbVzWdsGvrRweIUWJw8hBP7A==} dexie@4.2.1: resolution: {integrity: sha512-Ckej0NS6jxQ4Po3OrSQBFddayRhTCic2DoCAG5zacOfOVB9P2Q5Xc5uL/nVa7ZVs+HdMnvUPzLFCB/JwpB6Csg==} @@ -960,17 +941,8 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - - electron-to-chromium@1.5.244: - resolution: {integrity: sha512-OszpBN7xZX4vWMPJwB9illkN/znA8M36GQqQxi6MNy9axWxhOfJyZZJtSLQCpEFLHP2xK33BiWx9aIuIEXVCcw==} - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} @@ -988,8 +960,8 @@ packages: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} - esbuild@0.25.11: - resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==} + esbuild@0.27.2: + resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} engines: {node: '>=18'} hasBin: true @@ -1007,8 +979,8 @@ packages: peerDependencies: eslint: '>=7.0.0' - eslint-plugin-svelte@3.13.0: - resolution: {integrity: sha512-2ohCCQJJTNbIpQCSDSTWj+FN0OVfPmSO03lmSNT7ytqMaWF6kpT86LdzDqtm4sh7TVPl/OEWJ/d7R87bXP2Vjg==} + eslint-plugin-svelte@3.13.1: + resolution: {integrity: sha512-Ng+kV/qGS8P/isbNYVE3sJORtubB+yLEcYICMkUWNaDTb0SwZni/JhAYXh/Dz/q2eThUwWY0VMPZ//KYD1n3eQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.1 || ^9.0.0 @@ -1035,8 +1007,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.39.0: - resolution: {integrity: sha512-iy2GE3MHrYTL5lrCtMZ0X1KLEKKUjmK0kzwcnefhR66txcEmXZD2YWgR5GNdcEwkNx3a0siYkSvl0vIC+Svjmg==} + eslint@9.39.2: + resolution: {integrity: sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -1056,8 +1028,8 @@ packages: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} - esrap@2.1.2: - resolution: {integrity: sha512-DgvlIQeowRNyvLPWW4PT7Gu13WznY288Du086E751mwwbsgr29ytBiYeLzAGIo0qk3Ujob0SDk8TiSaM5WQzNg==} + esrap@2.2.1: + resolution: {integrity: sha512-GiYWG34AN/4CUyaWAgunGt0Rxvr1PTMlGC0vvEov/uOQYWne2bpN03Um+k8jT+q3op33mKouP2zeJ6OlM+qeUg==} esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} @@ -1074,11 +1046,11 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} - exifreader@4.32.0: - resolution: {integrity: sha512-sj1PzjpaPwSE/2MeUqoAYcfc2u7AZOGSby0FzmAkB4jjeCXgDryxzVgMwV+tJKGIkGdWkkWiUWoLSJoPHJ6V5Q==} + exifreader@4.33.1: + resolution: {integrity: sha512-KsVc4bRfZW255PSst5Opt5jUeLp+SD2+q6fmXQkMMkphpFCDBFjzNAvswgQa1YcMrXq+9Na6HJ6gS3wo2x7RRw==} - exsolve@1.0.7: - resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} + exsolve@1.0.8: + resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -1093,8 +1065,8 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} @@ -1136,16 +1108,12 @@ packages: debug: optional: true - foreground-child@3.3.1: - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} - engines: {node: '>=14'} - - form-data@4.0.4: - resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} - fraction.js@4.3.7: - resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} @@ -1175,18 +1143,10 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} - hasBin: true - globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} - globals@15.15.0: - resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==} - engines: {node: '>=18'} - globals@16.5.0: resolution: {integrity: sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==} engines: {node: '>=18'} @@ -1195,9 +1155,6 @@ packages: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} - graphemer@1.4.0: - resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} - has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -1245,10 +1202,6 @@ packages: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -1273,9 +1226,6 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jiti@1.21.7: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true @@ -1284,8 +1234,8 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true - js-yaml@4.1.0: - resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} hasBin: true json-buffer@3.0.1: @@ -1307,9 +1257,6 @@ packages: known-css-properties@0.37.0: resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==} - kolorist@1.8.0: - resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} - kysely-ctl@0.19.0: resolution: {integrity: sha512-89hzOd1cy/H063jB2E9wYHq+uKYpaHv6Mb5RiNFpRZL6BYCah9ncsdl3x5b52eirxry4UyWSmGNN3sFv+gK+ig==} engines: {node: '>=20'} @@ -1327,8 +1274,8 @@ packages: kysely-prisma-postgres: optional: true - kysely@0.28.8: - resolution: {integrity: sha512-QUOgl5ZrS9IRuhq5FvOKFSsD/3+IA6MLE81/bOOTRA/YQpKDza2sFdN5g6JCB9BOpqMJDGefLCQ9F12hRS13TA==} + kysely@0.28.9: + resolution: {integrity: sha512-3BeXMoiOhpOwu62CiVpO6lxfq4eS6KMYfQdMsN/2kUCRNuF2YiEr7u0HLHaQU+O4Xu8YXE3bHVkwaQ85i72EuA==} engines: {node: '>=20.0.0'} levn@0.4.1: @@ -1363,11 +1310,8 @@ packages: long-timeout@0.1.1: resolution: {integrity: sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==} - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - - lru-cache@11.2.2: - resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==} + lru-cache@11.2.4: + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} engines: {node: 20 || >=22} luxon@3.7.2: @@ -1409,10 +1353,6 @@ packages: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - mlly@1.8.0: resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} @@ -1460,10 +1400,6 @@ packages: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - normalize-range@0.1.2: - resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} - engines: {node: '>=0.10.0'} - nypm@0.6.2: resolution: {integrity: sha512-7eM+hpOtrKrBDCh7Ypu2lJ9Z7PNZBdi/8AT3AX8xoCj43BBVHD0hPSTEvMtkMpfs8FCqBGhxB+uToIQimA111g==} engines: {node: ^14.16.0 || >=16.10.0} @@ -1499,11 +1435,8 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - - package-manager-detector@1.5.0: - resolution: {integrity: sha512-uBj69dVlYe/+wxj8JOpr97XfsxH/eumMt6HqjNTmJDf/6NO9s+0uxeOneIz3AsPt2m6y9PqzDzd3ATcU17MNfw==} + package-manager-detector@1.6.0: + resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==} parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} @@ -1520,10 +1453,6 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} - pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -1653,8 +1582,8 @@ packages: resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} engines: {node: '>=4'} - postcss-selector-parser@7.1.0: - resolution: {integrity: sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==} + postcss-selector-parser@7.1.1: + resolution: {integrity: sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==} engines: {node: '>=4'} postcss-value-parser@4.2.0: @@ -1668,8 +1597,8 @@ packages: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} - postgres-bytea@1.0.0: - resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + postgres-bytea@1.0.1: + resolution: {integrity: sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==} engines: {node: '>=0.10.0'} postgres-date@1.0.7: @@ -1684,14 +1613,14 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier-plugin-svelte@3.4.0: - resolution: {integrity: sha512-pn1ra/0mPObzqoIQn/vUTR3ZZI6UuZ0sHqMK5x2jMLGrs53h0sXhkVuDcrlssHwIMk7FYrMjHBPoUSyyEEDlBQ==} + prettier-plugin-svelte@3.4.1: + resolution: {integrity: sha512-xL49LCloMoZRvSwa6IEdN2GV6cq2IqpYGstYtMT+5wmml1/dClEoI0MZR78MiVPpu6BdQFfN0/y73yO6+br5Pg==} peerDependencies: prettier: ^3.0.0 svelte: ^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0 - prettier-plugin-tailwindcss@0.7.1: - resolution: {integrity: sha512-Bzv1LZcuiR1Sk02iJTS1QzlFNp/o5l2p3xkopwOrbPmtMeh3fK9rVW5M3neBQzHq+kGKj/4LGQMTNcTH4NGPtQ==} + prettier-plugin-tailwindcss@0.7.2: + resolution: {integrity: sha512-LkphyK3Fw+q2HdMOoiEHWf93fNtYJwfamoKPl7UwtjFQdei/iIBoX11G6j706FzN3ymX9mPVi97qIY8328vdnA==} engines: {node: '>=20.19'} peerDependencies: '@ianvs/prettier-plugin-sort-imports': '*' @@ -1745,8 +1674,8 @@ packages: prettier-plugin-svelte: optional: true - prettier@3.6.2: - resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + prettier@3.7.4: + resolution: {integrity: sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==} engines: {node: '>=14'} hasBin: true @@ -1777,6 +1706,10 @@ packages: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -1790,8 +1723,8 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.52.5: - resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==} + rollup@4.54.0: + resolution: {integrity: sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -1818,10 +1751,6 @@ packages: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - sirv@3.0.2: resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} engines: {node: '>=18'} @@ -1840,28 +1769,12 @@ packages: std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.1.2: - resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} - engines: {node: '>=12'} - strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - sucrase@3.35.0: - resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true @@ -1877,29 +1790,29 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svelte-check@4.3.3: - resolution: {integrity: sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg==} + svelte-check@4.3.5: + resolution: {integrity: sha512-e4VWZETyXaKGhpkxOXP+B/d0Fp/zKViZoJmneZWe/05Y2aqSKj3YN2nLfYPJBQ87WEiY4BQCQ9hWGu9mPT1a1Q==} engines: {node: '>= 18.0.0'} hasBin: true peerDependencies: svelte: ^4.0.0 || ^5.0.0-next.0 typescript: '>=5.0.0' - svelte-eslint-parser@1.4.0: - resolution: {integrity: sha512-fjPzOfipR5S7gQ/JvI9r2H8y9gMGXO3JtmrylHLLyahEMquXI0lrebcjT+9/hNgDej0H7abTyox5HpHmW1PSWA==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0, pnpm: 10.18.3} + svelte-eslint-parser@1.4.1: + resolution: {integrity: sha512-1eqkfQ93goAhjAXxZiu1SaKI9+0/sxp4JIWQwUpsz7ybehRE5L8dNuz7Iry7K22R47p5/+s9EM+38nHV2OlgXA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0, pnpm: 10.24.0} peerDependencies: svelte: ^3.37.0 || ^4.0.0 || ^5.0.0 peerDependenciesMeta: svelte: optional: true - svelte@5.43.2: - resolution: {integrity: sha512-ro1umEzX8rT5JpCmlf0PPv7ncD8MdVob9e18bhwqTKNoLjS8kDvhVpaoYVPc+qMwDAOfcwJtyY7ZFSDbOaNPgA==} + svelte@5.46.1: + resolution: {integrity: sha512-ynjfCHD3nP2el70kN5Pmg37sSi0EjOm9FgHYQdC4giWG/hzO3AatzXXJJgP305uIhGQxSufJLuYWtkY8uK/8RA==} engines: {node: '>=18'} - tailwindcss@3.4.18: - resolution: {integrity: sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==} + tailwindcss@3.4.19: + resolution: {integrity: sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==} engines: {node: '>=14.0.0'} hasBin: true @@ -1910,8 +1823,9 @@ packages: thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} - tinyexec@1.0.1: - resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} @@ -1948,8 +1862,8 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - typescript-eslint@8.46.2: - resolution: {integrity: sha512-vbw8bOmiuYNdzzV3lsiWv6sRwjyuKJMQqWulBOU7M0RrxedXledX8G8kBbQeiOYDnTfiXz0Y4081E1QMNB6iQg==} + typescript-eslint@8.50.1: + resolution: {integrity: sha512-ytTHO+SoYSbhAH9CrYnMhiLx8To6PSSvqnvXyPUgPETCvB6eBKmTI9w6XMPS3HsBRGkwTVBX+urA8dYQx6bHfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -1989,12 +1903,12 @@ packages: vue-template-es2015-compiler: optional: true - unplugin@2.3.10: - resolution: {integrity: sha512-6NCPkv1ClwH+/BGE9QeoTIl09nuiAt0gS28nn1PvYXsGKRwM2TCbFA2QiilmehPDTXIe684k4rZI1yl3A1PCUw==} + unplugin@2.3.11: + resolution: {integrity: sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==} engines: {node: '>=18.12.0'} - update-browserslist-db@1.1.4: - resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -2009,8 +1923,8 @@ packages: resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} hasBin: true - vite@7.1.12: - resolution: {integrity: sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==} + vite@7.3.0: + resolution: {integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -2069,14 +1983,6 @@ packages: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -2094,15 +2000,15 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - yocto-queue@1.2.1: - resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} + yocto-queue@1.2.2: + resolution: {integrity: sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==} engines: {node: '>=12.20'} zimmerframe@1.1.4: resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==} - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@4.2.1: + resolution: {integrity: sha512-0wZ1IRqGGhMP76gLqz8EyfBXKk0J2qo2+H3fi4mcUP/KtTocoX08nmIAHl1Z2kJIZbZee8KOpBCSNPRgauucjw==} snapshots: @@ -2110,103 +2016,101 @@ snapshots: '@antfu/install-pkg@1.1.0': dependencies: - package-manager-detector: 1.5.0 - tinyexec: 1.0.1 - - '@antfu/utils@9.3.0': {} + package-manager-detector: 1.6.0 + tinyexec: 1.0.2 '@epic-web/invariant@1.0.0': {} - '@esbuild/aix-ppc64@0.25.11': + '@esbuild/aix-ppc64@0.27.2': optional: true - '@esbuild/android-arm64@0.25.11': + '@esbuild/android-arm64@0.27.2': optional: true - '@esbuild/android-arm@0.25.11': + '@esbuild/android-arm@0.27.2': optional: true - '@esbuild/android-x64@0.25.11': + '@esbuild/android-x64@0.27.2': optional: true - '@esbuild/darwin-arm64@0.25.11': + '@esbuild/darwin-arm64@0.27.2': optional: true - '@esbuild/darwin-x64@0.25.11': + '@esbuild/darwin-x64@0.27.2': optional: true - '@esbuild/freebsd-arm64@0.25.11': + '@esbuild/freebsd-arm64@0.27.2': optional: true - '@esbuild/freebsd-x64@0.25.11': + '@esbuild/freebsd-x64@0.27.2': optional: true - '@esbuild/linux-arm64@0.25.11': + '@esbuild/linux-arm64@0.27.2': optional: true - '@esbuild/linux-arm@0.25.11': + '@esbuild/linux-arm@0.27.2': optional: true - '@esbuild/linux-ia32@0.25.11': + '@esbuild/linux-ia32@0.27.2': optional: true - '@esbuild/linux-loong64@0.25.11': + '@esbuild/linux-loong64@0.27.2': optional: true - '@esbuild/linux-mips64el@0.25.11': + '@esbuild/linux-mips64el@0.27.2': optional: true - '@esbuild/linux-ppc64@0.25.11': + '@esbuild/linux-ppc64@0.27.2': optional: true - '@esbuild/linux-riscv64@0.25.11': + '@esbuild/linux-riscv64@0.27.2': optional: true - '@esbuild/linux-s390x@0.25.11': + '@esbuild/linux-s390x@0.27.2': optional: true - '@esbuild/linux-x64@0.25.11': + '@esbuild/linux-x64@0.27.2': optional: true - '@esbuild/netbsd-arm64@0.25.11': + '@esbuild/netbsd-arm64@0.27.2': optional: true - '@esbuild/netbsd-x64@0.25.11': + '@esbuild/netbsd-x64@0.27.2': optional: true - '@esbuild/openbsd-arm64@0.25.11': + '@esbuild/openbsd-arm64@0.27.2': optional: true - '@esbuild/openbsd-x64@0.25.11': + '@esbuild/openbsd-x64@0.27.2': optional: true - '@esbuild/openharmony-arm64@0.25.11': + '@esbuild/openharmony-arm64@0.27.2': optional: true - '@esbuild/sunos-x64@0.25.11': + '@esbuild/sunos-x64@0.27.2': optional: true - '@esbuild/win32-arm64@0.25.11': + '@esbuild/win32-arm64@0.27.2': optional: true - '@esbuild/win32-ia32@0.25.11': + '@esbuild/win32-ia32@0.27.2': optional: true - '@esbuild/win32-x64@0.25.11': + '@esbuild/win32-x64@0.27.2': optional: true - '@eslint-community/eslint-utils@4.9.0(eslint@9.39.0(jiti@1.21.7))': + '@eslint-community/eslint-utils@4.9.0(eslint@9.39.2(jiti@1.21.7))': dependencies: - eslint: 9.39.0(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} - '@eslint/compat@1.4.1(eslint@9.39.0(jiti@1.21.7))': + '@eslint/compat@1.4.1(eslint@9.39.2(jiti@1.21.7))': dependencies: '@eslint/core': 0.17.0 optionalDependencies: - eslint: 9.39.0(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) '@eslint/config-array@0.21.1': dependencies: @@ -2224,7 +2128,7 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 - '@eslint/eslintrc@3.3.1': + '@eslint/eslintrc@3.3.3': dependencies: ajv: 6.12.6 debug: 4.4.3 @@ -2232,13 +2136,13 @@ snapshots: globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.1.0 + js-yaml: 4.1.1 minimatch: 3.1.2 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color - '@eslint/js@9.39.0': {} + '@eslint/js@9.39.2': {} '@eslint/object-schema@2.1.7': {} @@ -2260,33 +2164,17 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@iconify-json/material-symbols@1.2.44': + '@iconify-json/material-symbols@1.2.50': dependencies: '@iconify/types': 2.0.0 '@iconify/types@2.0.0': {} - '@iconify/utils@3.0.2': + '@iconify/utils@3.1.0': dependencies: '@antfu/install-pkg': 1.1.0 - '@antfu/utils': 9.3.0 '@iconify/types': 2.0.0 - debug: 4.4.3 - globals: 15.15.0 - kolorist: 1.8.0 - local-pkg: 1.1.2 mlly: 1.8.0 - transitivePeerDependencies: - - supports-color - - '@isaacs/cliui@8.0.2': - dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.2 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 '@jridgewell/gen-mapping@0.3.13': dependencies: @@ -2317,18 +2205,15 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 + fastq: 1.20.1 '@phc/format@1.0.0': {} - '@pkgjs/parseargs@0.11.0': - optional: true - '@polka/url@1.0.0-next.29': {} - '@rollup/plugin-commonjs@28.0.9(rollup@4.52.5)': + '@rollup/plugin-commonjs@28.0.9(rollup@4.54.0)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.52.5) + '@rollup/pluginutils': 5.3.0(rollup@4.54.0) commondir: 1.0.1 estree-walker: 2.0.2 fdir: 6.5.0(picomatch@4.0.3) @@ -2336,121 +2221,121 @@ snapshots: magic-string: 0.30.21 picomatch: 4.0.3 optionalDependencies: - rollup: 4.52.5 + rollup: 4.54.0 - '@rollup/plugin-json@6.1.0(rollup@4.52.5)': + '@rollup/plugin-json@6.1.0(rollup@4.54.0)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.52.5) + '@rollup/pluginutils': 5.3.0(rollup@4.54.0) optionalDependencies: - rollup: 4.52.5 + rollup: 4.54.0 - '@rollup/plugin-node-resolve@16.0.3(rollup@4.52.5)': + '@rollup/plugin-node-resolve@16.0.3(rollup@4.54.0)': dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.52.5) + '@rollup/pluginutils': 5.3.0(rollup@4.54.0) '@types/resolve': 1.20.2 deepmerge: 4.3.1 is-module: 1.0.0 resolve: 1.22.11 optionalDependencies: - rollup: 4.52.5 + rollup: 4.54.0 - '@rollup/pluginutils@5.3.0(rollup@4.52.5)': + '@rollup/pluginutils@5.3.0(rollup@4.54.0)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.52.5 + rollup: 4.54.0 - '@rollup/rollup-android-arm-eabi@4.52.5': + '@rollup/rollup-android-arm-eabi@4.54.0': optional: true - '@rollup/rollup-android-arm64@4.52.5': + '@rollup/rollup-android-arm64@4.54.0': optional: true - '@rollup/rollup-darwin-arm64@4.52.5': + '@rollup/rollup-darwin-arm64@4.54.0': optional: true - '@rollup/rollup-darwin-x64@4.52.5': + '@rollup/rollup-darwin-x64@4.54.0': optional: true - '@rollup/rollup-freebsd-arm64@4.52.5': + '@rollup/rollup-freebsd-arm64@4.54.0': optional: true - '@rollup/rollup-freebsd-x64@4.52.5': + '@rollup/rollup-freebsd-x64@4.54.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.52.5': + '@rollup/rollup-linux-arm-gnueabihf@4.54.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.52.5': + '@rollup/rollup-linux-arm-musleabihf@4.54.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.52.5': + '@rollup/rollup-linux-arm64-gnu@4.54.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.52.5': + '@rollup/rollup-linux-arm64-musl@4.54.0': optional: true - '@rollup/rollup-linux-loong64-gnu@4.52.5': + '@rollup/rollup-linux-loong64-gnu@4.54.0': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.52.5': + '@rollup/rollup-linux-ppc64-gnu@4.54.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.52.5': + '@rollup/rollup-linux-riscv64-gnu@4.54.0': optional: true - '@rollup/rollup-linux-riscv64-musl@4.52.5': + '@rollup/rollup-linux-riscv64-musl@4.54.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.52.5': + '@rollup/rollup-linux-s390x-gnu@4.54.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.52.5': + '@rollup/rollup-linux-x64-gnu@4.54.0': optional: true - '@rollup/rollup-linux-x64-musl@4.52.5': + '@rollup/rollup-linux-x64-musl@4.54.0': optional: true - '@rollup/rollup-openharmony-arm64@4.52.5': + '@rollup/rollup-openharmony-arm64@4.54.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.52.5': + '@rollup/rollup-win32-arm64-msvc@4.54.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.52.5': + '@rollup/rollup-win32-ia32-msvc@4.54.0': optional: true - '@rollup/rollup-win32-x64-gnu@4.52.5': + '@rollup/rollup-win32-x64-gnu@4.54.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.52.5': + '@rollup/rollup-win32-x64-msvc@4.54.0': optional: true - '@standard-schema/spec@1.0.0': {} + '@standard-schema/spec@1.1.0': {} - '@sveltejs/acorn-typescript@1.0.6(acorn@8.15.0)': + '@sveltejs/acorn-typescript@1.0.8(acorn@8.15.0)': dependencies: acorn: 8.15.0 - '@sveltejs/adapter-node@5.4.0(@sveltejs/kit@2.48.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)))': + '@sveltejs/adapter-node@5.4.0(@sveltejs/kit@2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0)))': dependencies: - '@rollup/plugin-commonjs': 28.0.9(rollup@4.52.5) - '@rollup/plugin-json': 6.1.0(rollup@4.52.5) - '@rollup/plugin-node-resolve': 16.0.3(rollup@4.52.5) - '@sveltejs/kit': 2.48.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)) - rollup: 4.52.5 + '@rollup/plugin-commonjs': 28.0.9(rollup@4.54.0) + '@rollup/plugin-json': 6.1.0(rollup@4.54.0) + '@rollup/plugin-node-resolve': 16.0.3(rollup@4.54.0) + '@sveltejs/kit': 2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0)) + rollup: 4.54.0 - '@sveltejs/kit@2.48.4(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0))': + '@sveltejs/kit@2.49.2(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0))': dependencies: - '@standard-schema/spec': 1.0.0 - '@sveltejs/acorn-typescript': 1.0.6(acorn@8.15.0) - '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)) + '@standard-schema/spec': 1.1.0 + '@sveltejs/acorn-typescript': 1.0.8(acorn@8.15.0) + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0)) '@types/cookie': 0.6.0 acorn: 8.15.0 cookie: 0.6.0 - devalue: 5.4.2 + devalue: 5.6.1 esm-env: 1.2.2 kleur: 4.1.5 magic-string: 0.30.21 @@ -2458,36 +2343,36 @@ snapshots: sade: 1.8.1 set-cookie-parser: 2.7.2 sirv: 3.0.2 - svelte: 5.43.2 - vite: 7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0) + svelte: 5.46.1 + vite: 7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0) - '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0))': + '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0))': dependencies: - '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)) + '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0)) debug: 4.4.3 - svelte: 5.43.2 - vite: 7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0) + svelte: 5.46.1 + vite: 7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0))': + '@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.43.2)(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)) + '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0)))(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0)) debug: 4.4.3 deepmerge: 4.3.1 magic-string: 0.30.21 - svelte: 5.43.2 - vite: 7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0) - vitefu: 1.1.1(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)) + svelte: 5.46.1 + vite: 7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0) + vitefu: 1.1.1(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0)) transitivePeerDependencies: - supports-color - '@trpc/client@11.7.1(@trpc/server@11.7.1(typescript@5.9.3))(typescript@5.9.3)': + '@trpc/client@11.8.1(@trpc/server@11.8.1(typescript@5.9.3))(typescript@5.9.3)': dependencies: - '@trpc/server': 11.7.1(typescript@5.9.3) + '@trpc/server': 11.8.1(typescript@5.9.3) typescript: 5.9.3 - '@trpc/server@11.7.1(typescript@5.9.3)': + '@trpc/server@11.8.1(typescript@5.9.3)': dependencies: typescript: 5.9.3 @@ -2503,30 +2388,29 @@ snapshots: '@types/node-schedule@2.1.8': dependencies: - '@types/node': 24.9.2 + '@types/node': 25.0.3 - '@types/node@24.9.2': + '@types/node@25.0.3': dependencies: undici-types: 7.16.0 - '@types/pg@8.15.6': + '@types/pg@8.16.0': dependencies: - '@types/node': 24.9.2 + '@types/node': 25.0.3 pg-protocol: 1.10.3 pg-types: 2.2.0 '@types/resolve@1.20.2': {} - '@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.50.1(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.46.2 - '@typescript-eslint/type-utils': 8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.46.2 - eslint: 9.39.0(jiti@1.21.7) - graphemer: 1.4.0 + '@typescript-eslint/parser': 8.50.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.50.1 + '@typescript-eslint/type-utils': 8.50.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.1 + eslint: 9.39.2(jiti@1.21.7) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.1.0(typescript@5.9.3) @@ -2534,80 +2418,79 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3)': + '@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.46.2 - '@typescript-eslint/types': 8.46.2 - '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.46.2 + '@typescript-eslint/scope-manager': 8.50.1 + '@typescript-eslint/types': 8.50.1 + '@typescript-eslint/typescript-estree': 8.50.1(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.50.1 debug: 4.4.3 - eslint: 9.39.0(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.46.2(typescript@5.9.3)': + '@typescript-eslint/project-service@8.50.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3) - '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/tsconfig-utils': 8.50.1(typescript@5.9.3) + '@typescript-eslint/types': 8.50.1 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.46.2': + '@typescript-eslint/scope-manager@8.50.1': dependencies: - '@typescript-eslint/types': 8.46.2 - '@typescript-eslint/visitor-keys': 8.46.2 + '@typescript-eslint/types': 8.50.1 + '@typescript-eslint/visitor-keys': 8.50.1 - '@typescript-eslint/tsconfig-utils@8.46.2(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.50.1(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.50.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.46.2 - '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) - '@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/types': 8.50.1 + '@typescript-eslint/typescript-estree': 8.50.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) debug: 4.4.3 - eslint: 9.39.0(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) ts-api-utils: 2.1.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.46.2': {} + '@typescript-eslint/types@8.50.1': {} - '@typescript-eslint/typescript-estree@8.46.2(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.50.1(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.46.2(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3) - '@typescript-eslint/types': 8.46.2 - '@typescript-eslint/visitor-keys': 8.46.2 + '@typescript-eslint/project-service': 8.50.1(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.50.1(typescript@5.9.3) + '@typescript-eslint/types': 8.50.1 + '@typescript-eslint/visitor-keys': 8.50.1 debug: 4.4.3 - fast-glob: 3.3.3 - is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.3 + tinyglobby: 0.2.15 ts-api-utils: 2.1.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3)': + '@typescript-eslint/utils@8.50.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.0(jiti@1.21.7)) - '@typescript-eslint/scope-manager': 8.46.2 - '@typescript-eslint/types': 8.46.2 - '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) - eslint: 9.39.0(jiti@1.21.7) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@1.21.7)) + '@typescript-eslint/scope-manager': 8.50.1 + '@typescript-eslint/types': 8.50.1 + '@typescript-eslint/typescript-estree': 8.50.1(typescript@5.9.3) + eslint: 9.39.2(jiti@1.21.7) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.46.2': + '@typescript-eslint/visitor-keys@8.50.1': dependencies: - '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/types': 8.50.1 eslint-visitor-keys: 4.2.1 '@xmldom/xmldom@0.9.8': @@ -2626,16 +2509,10 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ansi-regex@5.0.1: {} - - ansi-regex@6.2.2: {} - ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - ansi-styles@6.2.3: {} - any-promise@1.3.0: {} anymatch@3.1.3: @@ -2658,20 +2535,19 @@ snapshots: asynckit@0.4.0: {} - autoprefixer@10.4.21(postcss@8.5.6): + autoprefixer@10.4.23(postcss@8.5.6): dependencies: - browserslist: 4.27.0 - caniuse-lite: 1.0.30001752 - fraction.js: 4.3.7 - normalize-range: 0.1.2 + browserslist: 4.28.1 + caniuse-lite: 1.0.30001761 + fraction.js: 5.3.4 picocolors: 1.1.1 postcss: 8.5.6 postcss-value-parser: 4.2.0 - axios@1.13.1: + axios@1.13.2: dependencies: follow-redirects: 1.15.11 - form-data: 4.0.4 + form-data: 4.0.5 proxy-from-env: 1.1.0 transitivePeerDependencies: - debug @@ -2680,7 +2556,7 @@ snapshots: balanced-match@1.0.2: {} - baseline-browser-mapping@2.8.23: {} + baseline-browser-mapping@2.9.11: {} binary-extensions@2.3.0: {} @@ -2697,21 +2573,21 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.27.0: + browserslist@4.28.1: dependencies: - baseline-browser-mapping: 2.8.23 - caniuse-lite: 1.0.30001752 - electron-to-chromium: 1.5.244 + baseline-browser-mapping: 2.9.11 + caniuse-lite: 1.0.30001761 + electron-to-chromium: 1.5.267 node-releases: 2.0.27 - update-browserslist-db: 1.1.4(browserslist@4.27.0) + update-browserslist-db: 1.2.3(browserslist@4.28.1) - c12@3.3.1: + c12@3.3.3: dependencies: - chokidar: 4.0.3 + chokidar: 5.0.0 confbox: 0.2.2 defu: 6.1.4 dotenv: 17.2.3 - exsolve: 1.0.7 + exsolve: 1.0.8 giget: 2.0.0 jiti: 2.6.1 ohash: 2.0.11 @@ -2729,7 +2605,7 @@ snapshots: camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001752: {} + caniuse-lite@1.0.30001761: {} chalk@4.1.2: dependencies: @@ -2752,6 +2628,10 @@ snapshots: dependencies: readdirp: 4.1.2 + chokidar@5.0.0: + dependencies: + readdirp: 5.0.0 + citty@0.1.6: dependencies: consola: 3.4.2 @@ -2817,7 +2697,7 @@ snapshots: destr@2.0.5: {} - devalue@5.4.2: {} + devalue@5.6.1: {} dexie@4.2.1: {} @@ -2833,13 +2713,7 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - eastasianwidth@0.2.0: {} - - electron-to-chromium@1.5.244: {} - - emoji-regex@8.0.0: {} - - emoji-regex@9.2.2: {} + electron-to-chromium@1.5.267: {} es-define-property@1.0.1: {} @@ -2856,48 +2730,48 @@ snapshots: has-tostringtag: 1.0.2 hasown: 2.0.2 - esbuild@0.25.11: + esbuild@0.27.2: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.11 - '@esbuild/android-arm': 0.25.11 - '@esbuild/android-arm64': 0.25.11 - '@esbuild/android-x64': 0.25.11 - '@esbuild/darwin-arm64': 0.25.11 - '@esbuild/darwin-x64': 0.25.11 - '@esbuild/freebsd-arm64': 0.25.11 - '@esbuild/freebsd-x64': 0.25.11 - '@esbuild/linux-arm': 0.25.11 - '@esbuild/linux-arm64': 0.25.11 - '@esbuild/linux-ia32': 0.25.11 - '@esbuild/linux-loong64': 0.25.11 - '@esbuild/linux-mips64el': 0.25.11 - '@esbuild/linux-ppc64': 0.25.11 - '@esbuild/linux-riscv64': 0.25.11 - '@esbuild/linux-s390x': 0.25.11 - '@esbuild/linux-x64': 0.25.11 - '@esbuild/netbsd-arm64': 0.25.11 - '@esbuild/netbsd-x64': 0.25.11 - '@esbuild/openbsd-arm64': 0.25.11 - '@esbuild/openbsd-x64': 0.25.11 - '@esbuild/openharmony-arm64': 0.25.11 - '@esbuild/sunos-x64': 0.25.11 - '@esbuild/win32-arm64': 0.25.11 - '@esbuild/win32-ia32': 0.25.11 - '@esbuild/win32-x64': 0.25.11 + '@esbuild/aix-ppc64': 0.27.2 + '@esbuild/android-arm': 0.27.2 + '@esbuild/android-arm64': 0.27.2 + '@esbuild/android-x64': 0.27.2 + '@esbuild/darwin-arm64': 0.27.2 + '@esbuild/darwin-x64': 0.27.2 + '@esbuild/freebsd-arm64': 0.27.2 + '@esbuild/freebsd-x64': 0.27.2 + '@esbuild/linux-arm': 0.27.2 + '@esbuild/linux-arm64': 0.27.2 + '@esbuild/linux-ia32': 0.27.2 + '@esbuild/linux-loong64': 0.27.2 + '@esbuild/linux-mips64el': 0.27.2 + '@esbuild/linux-ppc64': 0.27.2 + '@esbuild/linux-riscv64': 0.27.2 + '@esbuild/linux-s390x': 0.27.2 + '@esbuild/linux-x64': 0.27.2 + '@esbuild/netbsd-arm64': 0.27.2 + '@esbuild/netbsd-x64': 0.27.2 + '@esbuild/openbsd-arm64': 0.27.2 + '@esbuild/openbsd-x64': 0.27.2 + '@esbuild/openharmony-arm64': 0.27.2 + '@esbuild/sunos-x64': 0.27.2 + '@esbuild/win32-arm64': 0.27.2 + '@esbuild/win32-ia32': 0.27.2 + '@esbuild/win32-x64': 0.27.2 escalade@3.2.0: {} escape-string-regexp@4.0.0: {} - eslint-config-prettier@10.1.8(eslint@9.39.0(jiti@1.21.7)): + eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@1.21.7)): dependencies: - eslint: 9.39.0(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) - eslint-plugin-svelte@3.13.0(eslint@9.39.0(jiti@1.21.7))(svelte@5.43.2): + eslint-plugin-svelte@3.13.1(eslint@9.39.2(jiti@1.21.7))(svelte@5.46.1): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.0(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@1.21.7)) '@jridgewell/sourcemap-codec': 1.5.5 - eslint: 9.39.0(jiti@1.21.7) + eslint: 9.39.2(jiti@1.21.7) esutils: 2.0.3 globals: 16.5.0 known-css-properties: 0.37.0 @@ -2905,17 +2779,17 @@ snapshots: postcss-load-config: 3.1.4(postcss@8.5.6) postcss-safe-parser: 7.0.1(postcss@8.5.6) semver: 7.7.3 - svelte-eslint-parser: 1.4.0(svelte@5.43.2) + svelte-eslint-parser: 1.4.1(svelte@5.46.1) optionalDependencies: - svelte: 5.43.2 + svelte: 5.46.1 transitivePeerDependencies: - ts-node - eslint-plugin-tailwindcss@3.18.2(tailwindcss@3.4.18(yaml@2.8.0)): + eslint-plugin-tailwindcss@3.18.2(tailwindcss@3.4.19(yaml@2.8.0)): dependencies: fast-glob: 3.3.3 postcss: 8.5.6 - tailwindcss: 3.4.18(yaml@2.8.0) + tailwindcss: 3.4.19(yaml@2.8.0) eslint-scope@8.4.0: dependencies: @@ -2926,15 +2800,15 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.39.0(jiti@1.21.7): + eslint@9.39.2(jiti@1.21.7): dependencies: - '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.0(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.9.0(eslint@9.39.2(jiti@1.21.7)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.21.1 '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.17.0 - '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.39.0 + '@eslint/eslintrc': 3.3.3 + '@eslint/js': 9.39.2 '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 @@ -2979,7 +2853,7 @@ snapshots: dependencies: estraverse: 5.3.0 - esrap@2.1.2: + esrap@2.2.1: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -2993,11 +2867,11 @@ snapshots: esutils@2.0.3: {} - exifreader@4.32.0: + exifreader@4.33.1: optionalDependencies: '@xmldom/xmldom': 0.9.8 - exsolve@1.0.7: {} + exsolve@1.0.8: {} fast-deep-equal@3.1.3: {} @@ -3013,7 +2887,7 @@ snapshots: fast-levenshtein@2.0.6: {} - fastq@1.19.1: + fastq@1.20.1: dependencies: reusify: 1.1.0 @@ -3045,12 +2919,7 @@ snapshots: follow-redirects@1.15.11: {} - foreground-child@3.3.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - - form-data@4.0.4: + form-data@4.0.5: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 @@ -3058,7 +2927,7 @@ snapshots: hasown: 2.0.2 mime-types: 2.1.35 - fraction.js@4.3.7: {} + fraction.js@5.3.4: {} fsevents@2.3.3: optional: true @@ -3100,25 +2969,12 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@10.4.5: - dependencies: - foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 - globals@14.0.0: {} - globals@15.15.0: {} - globals@16.5.0: {} gopd@1.2.0: {} - graphemer@1.4.0: {} - has-flag@4.0.0: {} has-symbols@1.1.0: {} @@ -3154,8 +3010,6 @@ snapshots: is-extglob@2.1.1: {} - is-fullwidth-code-point@3.0.0: {} - is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -3176,17 +3030,11 @@ snapshots: isexe@2.0.0: {} - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - jiti@1.21.7: {} jiti@2.6.1: {} - js-yaml@4.1.0: + js-yaml@4.1.1: dependencies: argparse: 2.0.1 @@ -3204,16 +3052,14 @@ snapshots: known-css-properties@0.37.0: {} - kolorist@1.8.0: {} - - kysely-ctl@0.19.0(kysely@0.28.8)(typescript@5.9.3): + kysely-ctl@0.19.0(kysely@0.28.9)(typescript@5.9.3): dependencies: - c12: 3.3.1 + c12: 3.3.3 citty: 0.1.6 confbox: 0.2.2 consola: 3.4.2 jiti: 2.6.1 - kysely: 0.28.8 + kysely: 0.28.9 nypm: 0.6.2 ofetch: 1.5.1 pathe: 2.0.3 @@ -3224,7 +3070,7 @@ snapshots: - magicast - typescript - kysely@0.28.8: {} + kysely@0.28.9: {} levn@0.4.1: dependencies: @@ -3253,9 +3099,7 @@ snapshots: long-timeout@0.1.1: {} - lru-cache@10.4.3: {} - - lru-cache@11.2.2: {} + lru-cache@11.2.4: {} luxon@3.7.2: {} @@ -3288,8 +3132,6 @@ snapshots: dependencies: brace-expansion: 2.0.2 - minipass@7.1.2: {} - mlly@1.8.0: dependencies: acorn: 8.15.0 @@ -3329,15 +3171,13 @@ snapshots: normalize-path@3.0.0: {} - normalize-range@0.1.2: {} - nypm@0.6.2: dependencies: citty: 0.1.6 consola: 3.4.2 pathe: 2.0.3 pkg-types: 2.3.0 - tinyexec: 1.0.1 + tinyexec: 1.0.2 object-assign@4.1.1: {} @@ -3366,15 +3206,13 @@ snapshots: p-limit@7.2.0: dependencies: - yocto-queue: 1.2.1 + yocto-queue: 1.2.2 p-locate@5.0.0: dependencies: p-limit: 3.1.0 - package-json-from-dist@1.0.1: {} - - package-manager-detector@1.5.0: {} + package-manager-detector@1.6.0: {} parent-module@1.0.1: dependencies: @@ -3386,11 +3224,6 @@ snapshots: path-parse@1.0.7: {} - path-scurry@1.11.1: - dependencies: - lru-cache: 10.4.3 - minipass: 7.1.2 - pathe@2.0.3: {} perfect-debounce@2.0.0: {} @@ -3412,7 +3245,7 @@ snapshots: dependencies: pg-int8: 1.0.1 postgres-array: 2.0.0 - postgres-bytea: 1.0.0 + postgres-bytea: 1.0.1 postgres-date: 1.0.7 postgres-interval: 1.2.0 @@ -3449,7 +3282,7 @@ snapshots: pkg-types@2.3.0: dependencies: confbox: 0.2.2 - exsolve: 1.0.7 + exsolve: 1.0.8 pathe: 2.0.3 postcss-import@15.1.0(postcss@8.5.6): @@ -3497,7 +3330,7 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-selector-parser@7.1.0: + postcss-selector-parser@7.1.1: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 @@ -3512,7 +3345,7 @@ snapshots: postgres-array@2.0.0: {} - postgres-bytea@1.0.0: {} + postgres-bytea@1.0.1: {} postgres-date@1.0.7: {} @@ -3522,18 +3355,18 @@ snapshots: prelude-ls@1.2.1: {} - prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.43.2): + prettier-plugin-svelte@3.4.1(prettier@3.7.4)(svelte@5.46.1): dependencies: - prettier: 3.6.2 - svelte: 5.43.2 + prettier: 3.7.4 + svelte: 5.46.1 - prettier-plugin-tailwindcss@0.7.1(prettier-plugin-svelte@3.4.0(prettier@3.6.2)(svelte@5.43.2))(prettier@3.6.2): + prettier-plugin-tailwindcss@0.7.2(prettier-plugin-svelte@3.4.1(prettier@3.7.4)(svelte@5.46.1))(prettier@3.7.4): dependencies: - prettier: 3.6.2 + prettier: 3.7.4 optionalDependencies: - prettier-plugin-svelte: 3.4.0(prettier@3.6.2)(svelte@5.43.2) + prettier-plugin-svelte: 3.4.1(prettier@3.7.4)(svelte@5.46.1) - prettier@3.6.2: {} + prettier@3.7.4: {} proxy-from-env@1.1.0: {} @@ -3558,6 +3391,8 @@ snapshots: readdirp@4.1.2: {} + readdirp@5.0.0: {} + resolve-from@4.0.0: {} resolve@1.22.11: @@ -3568,32 +3403,32 @@ snapshots: reusify@1.1.0: {} - rollup@4.52.5: + rollup@4.54.0: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.52.5 - '@rollup/rollup-android-arm64': 4.52.5 - '@rollup/rollup-darwin-arm64': 4.52.5 - '@rollup/rollup-darwin-x64': 4.52.5 - '@rollup/rollup-freebsd-arm64': 4.52.5 - '@rollup/rollup-freebsd-x64': 4.52.5 - '@rollup/rollup-linux-arm-gnueabihf': 4.52.5 - '@rollup/rollup-linux-arm-musleabihf': 4.52.5 - '@rollup/rollup-linux-arm64-gnu': 4.52.5 - '@rollup/rollup-linux-arm64-musl': 4.52.5 - '@rollup/rollup-linux-loong64-gnu': 4.52.5 - '@rollup/rollup-linux-ppc64-gnu': 4.52.5 - '@rollup/rollup-linux-riscv64-gnu': 4.52.5 - '@rollup/rollup-linux-riscv64-musl': 4.52.5 - '@rollup/rollup-linux-s390x-gnu': 4.52.5 - '@rollup/rollup-linux-x64-gnu': 4.52.5 - '@rollup/rollup-linux-x64-musl': 4.52.5 - '@rollup/rollup-openharmony-arm64': 4.52.5 - '@rollup/rollup-win32-arm64-msvc': 4.52.5 - '@rollup/rollup-win32-ia32-msvc': 4.52.5 - '@rollup/rollup-win32-x64-gnu': 4.52.5 - '@rollup/rollup-win32-x64-msvc': 4.52.5 + '@rollup/rollup-android-arm-eabi': 4.54.0 + '@rollup/rollup-android-arm64': 4.54.0 + '@rollup/rollup-darwin-arm64': 4.54.0 + '@rollup/rollup-darwin-x64': 4.54.0 + '@rollup/rollup-freebsd-arm64': 4.54.0 + '@rollup/rollup-freebsd-x64': 4.54.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.54.0 + '@rollup/rollup-linux-arm-musleabihf': 4.54.0 + '@rollup/rollup-linux-arm64-gnu': 4.54.0 + '@rollup/rollup-linux-arm64-musl': 4.54.0 + '@rollup/rollup-linux-loong64-gnu': 4.54.0 + '@rollup/rollup-linux-ppc64-gnu': 4.54.0 + '@rollup/rollup-linux-riscv64-gnu': 4.54.0 + '@rollup/rollup-linux-riscv64-musl': 4.54.0 + '@rollup/rollup-linux-s390x-gnu': 4.54.0 + '@rollup/rollup-linux-x64-gnu': 4.54.0 + '@rollup/rollup-linux-x64-musl': 4.54.0 + '@rollup/rollup-openharmony-arm64': 4.54.0 + '@rollup/rollup-win32-arm64-msvc': 4.54.0 + '@rollup/rollup-win32-ia32-msvc': 4.54.0 + '@rollup/rollup-win32-x64-gnu': 4.54.0 + '@rollup/rollup-win32-x64-msvc': 4.54.0 fsevents: 2.3.3 run-parallel@1.2.0: @@ -3614,8 +3449,6 @@ snapshots: shebang-regex@3.0.0: {} - signal-exit@4.1.0: {} - sirv@3.0.2: dependencies: '@polka/url': 1.0.0-next.29 @@ -3630,36 +3463,16 @@ snapshots: std-env@3.10.0: {} - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.2 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-ansi@7.1.2: - dependencies: - ansi-regex: 6.2.2 - strip-json-comments@3.1.1: {} - sucrase@3.35.0: + sucrase@3.35.1: dependencies: '@jridgewell/gen-mapping': 0.3.13 commander: 4.1.1 - glob: 10.4.5 lines-and-columns: 1.2.4 mz: 2.7.0 pirates: 4.0.7 + tinyglobby: 0.2.15 ts-interface-checker: 0.1.13 superjson@2.2.6: @@ -3672,47 +3485,48 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-check@4.3.3(picomatch@4.0.3)(svelte@5.43.2)(typescript@5.9.3): + svelte-check@4.3.5(picomatch@4.0.3)(svelte@5.46.1)(typescript@5.9.3): dependencies: '@jridgewell/trace-mapping': 0.3.31 chokidar: 4.0.3 fdir: 6.5.0(picomatch@4.0.3) picocolors: 1.1.1 sade: 1.8.1 - svelte: 5.43.2 + svelte: 5.46.1 typescript: 5.9.3 transitivePeerDependencies: - picomatch - svelte-eslint-parser@1.4.0(svelte@5.43.2): + svelte-eslint-parser@1.4.1(svelte@5.46.1): dependencies: eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 espree: 10.4.0 postcss: 8.5.6 postcss-scss: 4.0.9(postcss@8.5.6) - postcss-selector-parser: 7.1.0 + postcss-selector-parser: 7.1.1 optionalDependencies: - svelte: 5.43.2 + svelte: 5.46.1 - svelte@5.43.2: + svelte@5.46.1: dependencies: '@jridgewell/remapping': 2.3.5 '@jridgewell/sourcemap-codec': 1.5.5 - '@sveltejs/acorn-typescript': 1.0.6(acorn@8.15.0) + '@sveltejs/acorn-typescript': 1.0.8(acorn@8.15.0) '@types/estree': 1.0.8 acorn: 8.15.0 aria-query: 5.3.2 axobject-query: 4.1.0 clsx: 2.1.1 + devalue: 5.6.1 esm-env: 1.2.2 - esrap: 2.1.2 + esrap: 2.2.1 is-reference: 3.0.3 locate-character: 3.0.0 magic-string: 0.30.21 zimmerframe: 1.1.4 - tailwindcss@3.4.18(yaml@2.8.0): + tailwindcss@3.4.19(yaml@2.8.0): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -3735,7 +3549,7 @@ snapshots: postcss-nested: 6.2.0(postcss@8.5.6) postcss-selector-parser: 6.1.2 resolve: 1.22.11 - sucrase: 3.35.0 + sucrase: 3.35.1 transitivePeerDependencies: - tsx - yaml @@ -3748,7 +3562,7 @@ snapshots: dependencies: any-promise: 1.3.0 - tinyexec@1.0.1: {} + tinyexec@1.0.2: {} tinyglobby@0.2.15: dependencies: @@ -3775,13 +3589,13 @@ snapshots: dependencies: prelude-ls: 1.2.1 - typescript-eslint@8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3): + typescript-eslint@8.50.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/parser': 8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) - '@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@1.21.7))(typescript@5.9.3) - eslint: 9.39.0(jiti@1.21.7) + '@typescript-eslint/eslint-plugin': 8.50.1(@typescript-eslint/parser@8.50.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/parser': 8.50.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.50.1(typescript@5.9.3) + '@typescript-eslint/utils': 8.50.1(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint: 9.39.2(jiti@1.21.7) typescript: 5.9.3 transitivePeerDependencies: - supports-color @@ -3792,28 +3606,28 @@ snapshots: undici-types@7.16.0: {} - unplugin-icons@22.5.0(svelte@5.43.2): + unplugin-icons@22.5.0(svelte@5.46.1): dependencies: '@antfu/install-pkg': 1.1.0 - '@iconify/utils': 3.0.2 + '@iconify/utils': 3.1.0 debug: 4.4.3 local-pkg: 1.1.2 - unplugin: 2.3.10 + unplugin: 2.3.11 optionalDependencies: - svelte: 5.43.2 + svelte: 5.46.1 transitivePeerDependencies: - supports-color - unplugin@2.3.10: + unplugin@2.3.11: dependencies: '@jridgewell/remapping': 2.3.5 acorn: 8.15.0 picomatch: 4.0.3 webpack-virtual-modules: 0.6.2 - update-browserslist-db@1.1.4(browserslist@4.27.0): + update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: - browserslist: 4.27.0 + browserslist: 4.28.1 escalade: 3.2.0 picocolors: 1.1.1 @@ -3825,23 +3639,23 @@ snapshots: uuid@13.0.0: {} - vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0): + vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0): dependencies: - esbuild: 0.25.11 + esbuild: 0.27.2 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.52.5 + rollup: 4.54.0 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 24.9.2 + '@types/node': 25.0.3 fsevents: 2.3.3 jiti: 1.21.7 yaml: 2.8.0 - vitefu@1.1.1(vite@7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0)): + vitefu@1.1.1(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0)): optionalDependencies: - vite: 7.1.12(@types/node@24.9.2)(jiti@1.21.7)(yaml@2.8.0) + vite: 7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0) webpack-virtual-modules@0.6.2: {} @@ -3851,18 +3665,6 @@ snapshots: word-wrap@1.2.5: {} - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.3 - string-width: 5.1.2 - strip-ansi: 7.1.2 - xtend@4.0.2: {} yaml@1.10.2: {} @@ -3872,8 +3674,8 @@ snapshots: yocto-queue@0.1.0: {} - yocto-queue@1.2.1: {} + yocto-queue@1.2.2: {} zimmerframe@1.1.4: {} - zod@3.25.76: {} + zod@4.2.1: {} diff --git a/src/lib/modules/key.ts b/src/lib/modules/key.ts index 945902c..d5276a5 100644 --- a/src/lib/modules/key.ts +++ b/src/lib/modules/key.ts @@ -5,14 +5,14 @@ import type { ClientKeys } from "$lib/stores"; const serializedClientKeysSchema = z.intersection( z.object({ generator: z.literal("ArkVault"), - exportedAt: z.string().datetime(), + exportedAt: z.iso.datetime(), }), z.object({ version: z.literal(1), - encryptKey: z.string().base64().nonempty(), - decryptKey: z.string().base64().nonempty(), - signKey: z.string().base64().nonempty(), - verifyKey: z.string().base64().nonempty(), + encryptKey: z.base64().nonempty(), + decryptKey: z.base64().nonempty(), + signKey: z.base64().nonempty(), + verifyKey: z.base64().nonempty(), }), ); diff --git a/src/lib/server/schemas/auth.ts b/src/lib/server/schemas/auth.ts index b413f9d..d4972d1 100644 --- a/src/lib/server/schemas/auth.ts +++ b/src/lib/server/schemas/auth.ts @@ -7,26 +7,26 @@ export const passwordChangeRequest = z.object({ export type PasswordChangeRequest = z.input; export const loginRequest = z.object({ - email: z.string().email(), + email: z.email(), password: z.string().trim().nonempty(), }); export type LoginRequest = z.input; export const sessionUpgradeRequest = z.object({ - encPubKey: z.string().base64().nonempty(), - sigPubKey: z.string().base64().nonempty(), + encPubKey: z.base64().nonempty(), + sigPubKey: z.base64().nonempty(), }); export type SessionUpgradeRequest = z.input; export const sessionUpgradeResponse = z.object({ - id: z.number().int().positive(), - challenge: z.string().base64().nonempty(), + id: z.int().positive(), + challenge: z.base64().nonempty(), }); export type SessionUpgradeResponse = z.output; export const sessionUpgradeVerifyRequest = z.object({ - id: z.number().int().positive(), - answerSig: z.string().base64().nonempty(), + id: z.int().positive(), + answerSig: z.base64().nonempty(), force: z.boolean().default(false), }); export type SessionUpgradeVerifyRequest = z.input; diff --git a/src/lib/server/schemas/category.ts b/src/lib/server/schemas/category.ts index 408af7b..0bb07a7 100644 --- a/src/lib/server/schemas/category.ts +++ b/src/lib/server/schemas/category.ts @@ -1,3 +1,3 @@ import { z } from "zod"; -export const categoryIdSchema = z.union([z.literal("root"), z.number().int().positive()]); +export const categoryIdSchema = z.union([z.literal("root"), z.int().positive()]); diff --git a/src/lib/server/schemas/directory.ts b/src/lib/server/schemas/directory.ts index 107c3ee..dba44b9 100644 --- a/src/lib/server/schemas/directory.ts +++ b/src/lib/server/schemas/directory.ts @@ -1,3 +1,3 @@ import { z } from "zod"; -export const directoryIdSchema = z.union([z.literal("root"), z.number().int().positive()]); +export const directoryIdSchema = z.union([z.literal("root"), z.int().positive()]); diff --git a/src/lib/server/schemas/file.ts b/src/lib/server/schemas/file.ts index 5177bbd..811e590 100644 --- a/src/lib/server/schemas/file.ts +++ b/src/lib/server/schemas/file.ts @@ -3,34 +3,34 @@ import { z } from "zod"; import { directoryIdSchema } from "./directory"; export const fileThumbnailUploadRequest = z.object({ - dekVersion: z.string().datetime(), - contentIv: z.string().base64().nonempty(), + dekVersion: z.iso.datetime(), + contentIv: z.base64().nonempty(), }); export type FileThumbnailUploadRequest = z.input; export const fileUploadRequest = z.object({ parent: directoryIdSchema, - mekVersion: z.number().int().positive(), - dek: z.string().base64().nonempty(), - dekVersion: z.string().datetime(), - hskVersion: z.number().int().positive(), - contentHmac: z.string().base64().nonempty(), + mekVersion: z.int().positive(), + dek: z.base64().nonempty(), + dekVersion: z.iso.datetime(), + hskVersion: z.int().positive(), + contentHmac: z.base64().nonempty(), contentType: z .string() .trim() .nonempty() .refine((value) => mime.getExtension(value) !== null), // MIME type - contentIv: z.string().base64().nonempty(), - name: z.string().base64().nonempty(), - nameIv: z.string().base64().nonempty(), - createdAt: z.string().base64().nonempty().optional(), - createdAtIv: z.string().base64().nonempty().optional(), - lastModifiedAt: z.string().base64().nonempty(), - lastModifiedAtIv: z.string().base64().nonempty(), + contentIv: z.base64().nonempty(), + name: z.base64().nonempty(), + nameIv: z.base64().nonempty(), + createdAt: z.base64().nonempty().optional(), + createdAtIv: z.base64().nonempty().optional(), + lastModifiedAt: z.base64().nonempty(), + lastModifiedAtIv: z.base64().nonempty(), }); export type FileUploadRequest = z.input; export const fileUploadResponse = z.object({ - file: z.number().int().positive(), + file: z.int().positive(), }); export type FileUploadResponse = z.output; diff --git a/src/trpc/routers/category.ts b/src/trpc/routers/category.ts index f002421..2be80c8 100644 --- a/src/trpc/routers/category.ts +++ b/src/trpc/routers/category.ts @@ -38,11 +38,11 @@ const categoryRouter = router({ .input( z.object({ parent: categoryIdSchema, - mekVersion: z.number().int().positive(), - dek: z.string().base64().nonempty(), + mekVersion: z.int().positive(), + dek: z.base64().nonempty(), dekVersion: z.date(), - name: z.string().base64().nonempty(), - nameIv: z.string().base64().nonempty(), + name: z.base64().nonempty(), + nameIv: z.base64().nonempty(), }), ) .mutation(async ({ ctx, input }) => { @@ -72,10 +72,10 @@ const categoryRouter = router({ rename: roleProcedure["activeClient"] .input( z.object({ - id: z.number().int().positive(), + id: z.int().positive(), dekVersion: z.date(), - name: z.string().base64().nonempty(), - nameIv: z.string().base64().nonempty(), + name: z.base64().nonempty(), + nameIv: z.base64().nonempty(), }), ) .mutation(async ({ ctx, input }) => { @@ -99,7 +99,7 @@ const categoryRouter = router({ delete: roleProcedure["activeClient"] .input( z.object({ - id: z.number().int().positive(), + id: z.int().positive(), }), ) .mutation(async ({ ctx, input }) => { @@ -116,7 +116,7 @@ const categoryRouter = router({ files: roleProcedure["activeClient"] .input( z.object({ - id: z.number().int().positive(), + id: z.int().positive(), recurse: z.boolean().default(false), }), ) @@ -137,8 +137,8 @@ const categoryRouter = router({ addFile: roleProcedure["activeClient"] .input( z.object({ - id: z.number().int().positive(), - file: z.number().int().positive(), + id: z.int().positive(), + file: z.int().positive(), }), ) .mutation(async ({ ctx, input }) => { @@ -165,8 +165,8 @@ const categoryRouter = router({ removeFile: roleProcedure["activeClient"] .input( z.object({ - id: z.number().int().positive(), - file: z.number().int().positive(), + id: z.int().positive(), + file: z.int().positive(), }), ) .mutation(async ({ ctx, input }) => { diff --git a/src/trpc/routers/client.ts b/src/trpc/routers/client.ts index 8add385..37c5bc6 100644 --- a/src/trpc/routers/client.ts +++ b/src/trpc/routers/client.ts @@ -26,8 +26,8 @@ const clientRouter = router({ register: roleProcedure["notClient"] .input( z.object({ - encPubKey: z.string().base64().nonempty(), - sigPubKey: z.string().base64().nonempty(), + encPubKey: z.base64().nonempty(), + sigPubKey: z.base64().nonempty(), }), ) .mutation(async ({ ctx, input }) => { @@ -66,8 +66,8 @@ const clientRouter = router({ verify: roleProcedure["notClient"] .input( z.object({ - id: z.number().int().positive(), - answerSig: z.string().base64().nonempty(), + id: z.int().positive(), + answerSig: z.base64().nonempty(), }), ) .mutation(async ({ ctx, input }) => { diff --git a/src/trpc/routers/directory.ts b/src/trpc/routers/directory.ts index 70e3663..bcd31c7 100644 --- a/src/trpc/routers/directory.ts +++ b/src/trpc/routers/directory.ts @@ -41,11 +41,11 @@ const directoryRouter = router({ .input( z.object({ parent: directoryIdSchema, - mekVersion: z.number().int().positive(), - dek: z.string().base64().nonempty(), + mekVersion: z.int().positive(), + dek: z.base64().nonempty(), dekVersion: z.date(), - name: z.string().base64().nonempty(), - nameIv: z.string().base64().nonempty(), + name: z.base64().nonempty(), + nameIv: z.base64().nonempty(), }), ) .mutation(async ({ ctx, input }) => { @@ -75,10 +75,10 @@ const directoryRouter = router({ rename: roleProcedure["activeClient"] .input( z.object({ - id: z.number().int().positive(), + id: z.int().positive(), dekVersion: z.date(), - name: z.string().base64().nonempty(), - nameIv: z.string().base64().nonempty(), + name: z.base64().nonempty(), + nameIv: z.base64().nonempty(), }), ) .mutation(async ({ ctx, input }) => { @@ -102,7 +102,7 @@ const directoryRouter = router({ delete: roleProcedure["activeClient"] .input( z.object({ - id: z.number().int().positive(), + id: z.int().positive(), }), ) .mutation(async ({ ctx, input }) => { diff --git a/src/trpc/routers/file.ts b/src/trpc/routers/file.ts index b37032b..7c4e425 100644 --- a/src/trpc/routers/file.ts +++ b/src/trpc/routers/file.ts @@ -8,7 +8,7 @@ const fileRouter = router({ get: roleProcedure["activeClient"] .input( z.object({ - id: z.number().int().positive(), + id: z.int().positive(), }), ) .query(async ({ ctx, input }) => { @@ -42,8 +42,8 @@ const fileRouter = router({ listByHash: roleProcedure["activeClient"] .input( z.object({ - hskVersion: z.number().int().positive(), - contentHmac: z.string().base64().nonempty(), + hskVersion: z.int().positive(), + contentHmac: z.base64().nonempty(), }), ) .query(async ({ ctx, input }) => { @@ -61,10 +61,10 @@ const fileRouter = router({ rename: roleProcedure["activeClient"] .input( z.object({ - id: z.number().int().positive(), + id: z.int().positive(), dekVersion: z.date(), - name: z.string().base64().nonempty(), - nameIv: z.string().base64().nonempty(), + name: z.base64().nonempty(), + nameIv: z.base64().nonempty(), }), ) .mutation(async ({ ctx, input }) => { @@ -88,7 +88,7 @@ const fileRouter = router({ delete: roleProcedure["activeClient"] .input( z.object({ - id: z.number().int().positive(), + id: z.int().positive(), }), ) .mutation(async ({ ctx, input }) => { @@ -107,7 +107,7 @@ const fileRouter = router({ thumbnail: roleProcedure["activeClient"] .input( z.object({ - id: z.number().int().positive(), + id: z.int().positive(), }), ) .query(async ({ ctx, input }) => { diff --git a/src/trpc/routers/hsk.ts b/src/trpc/routers/hsk.ts index eed9d25..1e39af0 100644 --- a/src/trpc/routers/hsk.ts +++ b/src/trpc/routers/hsk.ts @@ -17,8 +17,8 @@ const hskRouter = router({ registerInitial: roleProcedure["activeClient"] .input( z.object({ - mekVersion: z.number().int().positive(), - hsk: z.string().base64().nonempty(), + mekVersion: z.int().positive(), + hsk: z.base64().nonempty(), }), ) .mutation(async ({ ctx, input }) => { diff --git a/src/trpc/routers/mek.ts b/src/trpc/routers/mek.ts index fa264a5..002d200 100644 --- a/src/trpc/routers/mek.ts +++ b/src/trpc/routers/mek.ts @@ -37,8 +37,8 @@ const mekRouter = router({ registerInitial: roleProcedure["pendingClient"] .input( z.object({ - mek: z.string().base64().nonempty(), - mekSig: z.string().base64().nonempty(), + mek: z.base64().nonempty(), + mekSig: z.base64().nonempty(), }), ) .mutation(async ({ ctx, input }) => { From 3fc29cf8dbd5623a79f52f00a5b40a130486beb3 Mon Sep 17 00:00:00 2001 From: static Date: Thu, 25 Dec 2025 23:44:23 +0900 Subject: [PATCH 11/23] =?UTF-8?q?/api/auth=20=EC=95=84=EB=9E=98=EC=9D=98?= =?UTF-8?q?=20Endpoint=EB=93=A4=EC=9D=84=20tRPC=EB=A1=9C=20=EB=A7=88?= =?UTF-8?q?=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/server/middlewares/authenticate.ts | 6 +- src/lib/server/schemas/auth.ts | 32 ---- src/lib/server/schemas/index.ts | 1 - src/lib/server/services/auth.ts | 122 -------------- src/lib/services/auth.ts | 60 ++++--- .../auth/changePassword/service.ts | 17 +- src/routes/(fullscreen)/auth/login/service.ts | 14 +- src/routes/api/auth/changePassword/+server.ts | 16 -- src/routes/api/auth/login/+server.ts | 21 --- src/routes/api/auth/logout/+server.ts | 13 -- src/routes/api/auth/upgradeSession/+server.ts | 26 --- .../api/auth/upgradeSession/verify/+server.ts | 16 -- src/trpc/router.server.ts | 2 + src/trpc/routers/auth.ts | 153 ++++++++++++++++++ src/trpc/routers/index.ts | 1 + 15 files changed, 214 insertions(+), 286 deletions(-) delete mode 100644 src/lib/server/schemas/auth.ts delete mode 100644 src/lib/server/services/auth.ts delete mode 100644 src/routes/api/auth/changePassword/+server.ts delete mode 100644 src/routes/api/auth/login/+server.ts delete mode 100644 src/routes/api/auth/logout/+server.ts delete mode 100644 src/routes/api/auth/upgradeSession/+server.ts delete mode 100644 src/routes/api/auth/upgradeSession/verify/+server.ts create mode 100644 src/trpc/routers/auth.ts diff --git a/src/lib/server/middlewares/authenticate.ts b/src/lib/server/middlewares/authenticate.ts index cc635b4..49f6545 100644 --- a/src/lib/server/middlewares/authenticate.ts +++ b/src/lib/server/middlewares/authenticate.ts @@ -3,11 +3,6 @@ import env from "$lib/server/loadenv"; import { authenticate, AuthenticationError } from "$lib/server/modules/auth"; export const authenticateMiddleware: Handle = async ({ event, resolve }) => { - const { pathname, search } = event.url; - if (pathname === "/api/auth/login") { - return await resolve(event); - } - try { const sessionIdSigned = event.cookies.get("sessionId"); if (!sessionIdSigned) { @@ -24,6 +19,7 @@ export const authenticateMiddleware: Handle = async ({ event, resolve }) => { }); } catch (e) { if (e instanceof AuthenticationError) { + const { pathname, search } = event.url; if (pathname === "/auth/login" || pathname.startsWith("/api/trpc")) { return await resolve(event); } else if (pathname.startsWith("/api")) { diff --git a/src/lib/server/schemas/auth.ts b/src/lib/server/schemas/auth.ts deleted file mode 100644 index d4972d1..0000000 --- a/src/lib/server/schemas/auth.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { z } from "zod"; - -export const passwordChangeRequest = z.object({ - oldPassword: z.string().trim().nonempty(), - newPassword: z.string().trim().nonempty(), -}); -export type PasswordChangeRequest = z.input; - -export const loginRequest = z.object({ - email: z.email(), - password: z.string().trim().nonempty(), -}); -export type LoginRequest = z.input; - -export const sessionUpgradeRequest = z.object({ - encPubKey: z.base64().nonempty(), - sigPubKey: z.base64().nonempty(), -}); -export type SessionUpgradeRequest = z.input; - -export const sessionUpgradeResponse = z.object({ - id: z.int().positive(), - challenge: z.base64().nonempty(), -}); -export type SessionUpgradeResponse = z.output; - -export const sessionUpgradeVerifyRequest = z.object({ - id: z.int().positive(), - answerSig: z.base64().nonempty(), - force: z.boolean().default(false), -}); -export type SessionUpgradeVerifyRequest = z.input; diff --git a/src/lib/server/schemas/index.ts b/src/lib/server/schemas/index.ts index d9ddce7..f7a2bc1 100644 --- a/src/lib/server/schemas/index.ts +++ b/src/lib/server/schemas/index.ts @@ -1,4 +1,3 @@ -export * from "./auth"; export * from "./category"; export * from "./directory"; export * from "./file"; diff --git a/src/lib/server/services/auth.ts b/src/lib/server/services/auth.ts deleted file mode 100644 index 1c6867f..0000000 --- a/src/lib/server/services/auth.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { error } from "@sveltejs/kit"; -import argon2 from "argon2"; -import { getClient, getClientByPubKeys, getUserClient } from "$lib/server/db/client"; -import { IntegrityError } from "$lib/server/db/error"; -import { - upgradeSession, - deleteSession, - deleteAllOtherSessions, - registerSessionUpgradeChallenge, - consumeSessionUpgradeChallenge, -} from "$lib/server/db/session"; -import { getUser, getUserByEmail, setUserPassword } from "$lib/server/db/user"; -import env from "$lib/server/loadenv"; -import { startSession } from "$lib/server/modules/auth"; -import { verifySignature, generateChallenge } from "$lib/server/modules/crypto"; - -const hashPassword = async (password: string) => { - return await argon2.hash(password); -}; - -const verifyPassword = async (hash: string, password: string) => { - return await argon2.verify(hash, password); -}; - -export const changePassword = async ( - userId: number, - sessionId: string, - oldPassword: string, - newPassword: string, -) => { - if (oldPassword === newPassword) { - error(400, "Same passwords"); - } else if (newPassword.length < 8) { - error(400, "Too short password"); - } - - const user = await getUser(userId); - if (!user) { - error(500, "Invalid session id"); - } else if (!(await verifyPassword(user.password, oldPassword))) { - error(403, "Invalid password"); - } - - await setUserPassword(userId, await hashPassword(newPassword)); - await deleteAllOtherSessions(userId, sessionId); -}; - -export const login = async (email: string, password: string, ip: string, userAgent: string) => { - const user = await getUserByEmail(email); - if (!user || !(await verifyPassword(user.password, password))) { - error(401, "Invalid email or password"); - } - - return { sessionIdSigned: await startSession(user.id, ip, userAgent) }; -}; - -export const logout = async (sessionId: string) => { - await deleteSession(sessionId); -}; - -export const createSessionUpgradeChallenge = async ( - sessionId: string, - userId: number, - ip: string, - encPubKey: string, - sigPubKey: string, -) => { - const client = await getClientByPubKeys(encPubKey, sigPubKey); - const userClient = client ? await getUserClient(userId, client.id) : undefined; - if (!client) { - error(401, "Invalid public key(s)"); - } else if (!userClient || userClient.state === "challenging") { - error(403, "Unregistered client"); - } - - const { answer, challenge } = await generateChallenge(32, encPubKey); - const { id } = await registerSessionUpgradeChallenge( - sessionId, - client.id, - answer.toString("base64"), - ip, - new Date(Date.now() + env.challenge.sessionUpgradeExp), - ); - - return { id, challenge: challenge.toString("base64") }; -}; - -export const verifySessionUpgradeChallenge = async ( - sessionId: string, - userId: number, - ip: string, - challengeId: number, - answerSig: string, - force: boolean, -) => { - const challenge = await consumeSessionUpgradeChallenge(challengeId, sessionId, ip); - if (!challenge) { - error(403, "Invalid challenge answer"); - } - - const client = await getClient(challenge.clientId); - if (!client) { - error(500, "Invalid challenge answer"); - } else if ( - !verifySignature(Buffer.from(challenge.answer, "base64"), answerSig, client.sigPubKey) - ) { - error(403, "Invalid challenge answer signature"); - } - - try { - await upgradeSession(userId, sessionId, client.id, force); - } catch (e) { - if (e instanceof IntegrityError) { - if (e.message === "Session not found") { - error(500, "Invalid challenge answer"); - } else if (!force && e.message === "Session already exists") { - error(409, "Already logged in"); - } - } - throw e; - } -}; diff --git a/src/lib/services/auth.ts b/src/lib/services/auth.ts index d1975f4..5d2d01a 100644 --- a/src/lib/services/auth.ts +++ b/src/lib/services/auth.ts @@ -1,10 +1,6 @@ -import { callPostApi } from "$lib/hooks"; +import { TRPCClientError } from "@trpc/client"; import { encodeToBase64, decryptChallenge, signMessageRSA } from "$lib/modules/crypto"; -import type { - SessionUpgradeRequest, - SessionUpgradeResponse, - SessionUpgradeVerifyRequest, -} from "$lib/server/schemas"; +import { useTRPC } from "$trpc/client"; export const requestSessionUpgrade = async ( encryptKeyBase64: string, @@ -13,27 +9,45 @@ export const requestSessionUpgrade = async ( signKey: CryptoKey, force = false, ) => { - let res = await callPostApi("/api/auth/upgradeSession", { - encPubKey: encryptKeyBase64, - sigPubKey: verifyKeyBase64, - }); - if (res.status === 403) return [false, "Unregistered client"] as const; - else if (!res.ok) return [false] as const; - - const { id, challenge }: SessionUpgradeResponse = await res.json(); + const trpc = useTRPC(); + let id, challenge; + try { + ({ id, challenge } = await trpc.auth.upgradeSession.mutate({ + encPubKey: encryptKeyBase64, + sigPubKey: verifyKeyBase64, + })); + } catch (e) { + if (e instanceof TRPCClientError && e.data?.code === "FORBIDDEN") { + return [false, "Unregistered client"] as const; + } + return [false] as const; + } const answer = await decryptChallenge(challenge, decryptKey); const answerSig = await signMessageRSA(answer, signKey); - res = await callPostApi("/api/auth/upgradeSession/verify", { - id, - answerSig: encodeToBase64(answerSig), - force, - }); - if (res.status === 409) return [false, "Already logged in"] as const; - else return [res.ok] as const; + try { + await trpc.auth.verifySessionUpgrade.mutate({ + id, + answerSig: encodeToBase64(answerSig), + force, + }); + } catch (e) { + if (e instanceof TRPCClientError && e.data?.code === "CONFLICT") { + return [false, "Already logged in"] as const; + } + return [false] as const; + } + + return [true] as const; }; export const requestLogout = async () => { - const res = await callPostApi("/api/auth/logout"); - return res.ok; + const trpc = useTRPC(); + try { + await trpc.auth.logout.mutate(); + return true; + } catch { + // TODO: Error Handling + return false; + } }; diff --git a/src/routes/(fullscreen)/auth/changePassword/service.ts b/src/routes/(fullscreen)/auth/changePassword/service.ts index 37380a4..699ec7f 100644 --- a/src/routes/(fullscreen)/auth/changePassword/service.ts +++ b/src/routes/(fullscreen)/auth/changePassword/service.ts @@ -1,10 +1,13 @@ -import { callPostApi } from "$lib/hooks"; -import type { PasswordChangeRequest } from "$lib/server/schemas"; +import { useTRPC } from "$trpc/client"; export const requestPasswordChange = async (oldPassword: string, newPassword: string) => { - const res = await callPostApi("/api/auth/changePassword", { - oldPassword, - newPassword, - }); - return res.ok; + const trpc = useTRPC(); + + try { + await trpc.auth.changePassword.mutate({ oldPassword, newPassword }); + return true; + } catch { + // TODO: Error Handling + return false; + } }; diff --git a/src/routes/(fullscreen)/auth/login/service.ts b/src/routes/(fullscreen)/auth/login/service.ts index 88613f1..0d57545 100644 --- a/src/routes/(fullscreen)/auth/login/service.ts +++ b/src/routes/(fullscreen)/auth/login/service.ts @@ -1,5 +1,4 @@ -import { callPostApi } from "$lib/hooks"; -import type { LoginRequest } from "$lib/server/schemas"; +import { useTRPC } from "$trpc/client"; export { requestLogout } from "$lib/services/auth"; export { requestDeletedFilesCleanup } from "$lib/services/file"; @@ -9,6 +8,13 @@ export { } from "$lib/services/key"; export const requestLogin = async (email: string, password: string) => { - const res = await callPostApi("/api/auth/login", { email, password }); - return res.ok; + const trpc = useTRPC(); + + try { + await trpc.auth.login.mutate({ email, password }); + return true; + } catch { + // TODO: Error Handling + return false; + } }; diff --git a/src/routes/api/auth/changePassword/+server.ts b/src/routes/api/auth/changePassword/+server.ts deleted file mode 100644 index 59129b8..0000000 --- a/src/routes/api/auth/changePassword/+server.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { error, text } from "@sveltejs/kit"; -import { authorize } from "$lib/server/modules/auth"; -import { passwordChangeRequest } from "$lib/server/schemas"; -import { changePassword } from "$lib/server/services/auth"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, request }) => { - const { sessionId, userId } = await authorize(locals, "any"); - - const zodRes = passwordChangeRequest.safeParse(await request.json()); - if (!zodRes.success) error(400, "Invalid request body"); - const { oldPassword, newPassword } = zodRes.data; - - await changePassword(userId, sessionId, oldPassword, newPassword); - return text("Password changed", { headers: { "Content-Type": "text/plain" } }); -}; diff --git a/src/routes/api/auth/login/+server.ts b/src/routes/api/auth/login/+server.ts deleted file mode 100644 index d748f6a..0000000 --- a/src/routes/api/auth/login/+server.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { error, text } from "@sveltejs/kit"; -import env from "$lib/server/loadenv"; -import { loginRequest } from "$lib/server/schemas"; -import { login } from "$lib/server/services/auth"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, request, cookies }) => { - const zodRes = loginRequest.safeParse(await request.json()); - if (!zodRes.success) error(400, "Invalid request body"); - const { email, password } = zodRes.data; - - const { sessionIdSigned } = await login(email, password, locals.ip, locals.userAgent); - cookies.set("sessionId", sessionIdSigned, { - path: "/", - maxAge: env.session.exp / 1000, - secure: true, - sameSite: "strict", - }); - - return text("Logged in", { headers: { "Content-Type": "text/plain" } }); -}; diff --git a/src/routes/api/auth/logout/+server.ts b/src/routes/api/auth/logout/+server.ts deleted file mode 100644 index b5c1f11..0000000 --- a/src/routes/api/auth/logout/+server.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { text } from "@sveltejs/kit"; -import { authorize } from "$lib/server/modules/auth"; -import { logout } from "$lib/server/services/auth"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, cookies }) => { - const { sessionId } = await authorize(locals, "any"); - - await logout(sessionId); - cookies.delete("sessionId", { path: "/" }); - - return text("Logged out", { headers: { "Content-Type": "text/plain" } }); -}; diff --git a/src/routes/api/auth/upgradeSession/+server.ts b/src/routes/api/auth/upgradeSession/+server.ts deleted file mode 100644 index fa0b6cf..0000000 --- a/src/routes/api/auth/upgradeSession/+server.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { error, json } from "@sveltejs/kit"; -import { authorize } from "$lib/server/modules/auth"; -import { - sessionUpgradeRequest, - sessionUpgradeResponse, - type SessionUpgradeResponse, -} from "$lib/server/schemas"; -import { createSessionUpgradeChallenge } from "$lib/server/services/auth"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, request }) => { - const { sessionId, userId } = await authorize(locals, "notClient"); - - const zodRes = sessionUpgradeRequest.safeParse(await request.json()); - if (!zodRes.success) error(400, "Invalid request body"); - const { encPubKey, sigPubKey } = zodRes.data; - - const { id, challenge } = await createSessionUpgradeChallenge( - sessionId, - userId, - locals.ip, - encPubKey, - sigPubKey, - ); - return json(sessionUpgradeResponse.parse({ id, challenge } satisfies SessionUpgradeResponse)); -}; diff --git a/src/routes/api/auth/upgradeSession/verify/+server.ts b/src/routes/api/auth/upgradeSession/verify/+server.ts deleted file mode 100644 index bbaedca..0000000 --- a/src/routes/api/auth/upgradeSession/verify/+server.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { error, text } from "@sveltejs/kit"; -import { authorize } from "$lib/server/modules/auth"; -import { sessionUpgradeVerifyRequest } from "$lib/server/schemas"; -import { verifySessionUpgradeChallenge } from "$lib/server/services/auth"; -import type { RequestHandler } from "./$types"; - -export const POST: RequestHandler = async ({ locals, request }) => { - const { sessionId, userId } = await authorize(locals, "notClient"); - - const zodRes = sessionUpgradeVerifyRequest.safeParse(await request.json()); - if (!zodRes.success) error(400, "Invalid request body"); - const { id, answerSig, force } = zodRes.data; - - await verifySessionUpgradeChallenge(sessionId, userId, locals.ip, id, answerSig, force); - return text("Session upgraded", { headers: { "Content-Type": "text/plain" } }); -}; diff --git a/src/trpc/router.server.ts b/src/trpc/router.server.ts index 3d05e93..64d25c7 100644 --- a/src/trpc/router.server.ts +++ b/src/trpc/router.server.ts @@ -2,6 +2,7 @@ import type { RequestEvent } from "@sveltejs/kit"; import type { inferRouterInputs, inferRouterOutputs } from "@trpc/server"; import { createContext, router } from "./init.server"; import { + authRouter, categoryRouter, clientRouter, directoryRouter, @@ -12,6 +13,7 @@ import { } from "./routers"; export const appRouter = router({ + auth: authRouter, category: categoryRouter, client: clientRouter, directory: directoryRouter, diff --git a/src/trpc/routers/auth.ts b/src/trpc/routers/auth.ts new file mode 100644 index 0000000..e0f3a7f --- /dev/null +++ b/src/trpc/routers/auth.ts @@ -0,0 +1,153 @@ +import { TRPCError } from "@trpc/server"; +import argon2 from "argon2"; +import { z } from "zod"; +import { ClientRepo, SessionRepo, UserRepo, IntegrityError } from "$lib/server/db"; +import env from "$lib/server/loadenv"; +import { startSession } from "$lib/server/modules/auth"; +import { generateChallenge, verifySignature } from "$lib/server/modules/crypto"; +import { publicProcedure, roleProcedure, router } from "../init.server"; + +const hashPassword = async (password: string) => { + return await argon2.hash(password); +}; + +const verifyPassword = async (hash: string, password: string) => { + return await argon2.verify(hash, password); +}; + +const authRouter = router({ + login: publicProcedure + .input( + z.object({ + email: z.email(), + password: z.string().trim().nonempty(), + }), + ) + .mutation(async ({ ctx, input }) => { + const user = await UserRepo.getUserByEmail(input.email); + if (!user || !(await verifyPassword(user.password, input.password))) { + throw new TRPCError({ code: "UNAUTHORIZED", message: "Invalid email or password" }); + } + + const sessionIdSigned = await startSession(user.id, ctx.locals.ip, ctx.locals.userAgent); + ctx.cookies.set("sessionId", sessionIdSigned, { + path: "/", + maxAge: env.session.exp / 1000, + secure: true, + sameSite: "strict", + }); + }), + + logout: roleProcedure["any"].mutation(async ({ ctx }) => { + await SessionRepo.deleteSession(ctx.session.sessionId); + ctx.cookies.delete("sessionId", { path: "/" }); + }), + + changePassword: roleProcedure["any"] + .input( + z.object({ + oldPassword: z.string().trim().nonempty(), + newPassword: z.string().trim().nonempty(), + }), + ) + .mutation(async ({ ctx, input }) => { + if (input.oldPassword === input.newPassword) { + throw new TRPCError({ code: "BAD_REQUEST", message: "Same passwords" }); + } else if (input.newPassword.length < 8) { + throw new TRPCError({ code: "BAD_REQUEST", message: "Too short password" }); + } + + const user = await UserRepo.getUser(ctx.session.userId); + if (!user) { + throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Invalid session id" }); + } else if (!(await verifyPassword(user.password, input.oldPassword))) { + throw new TRPCError({ code: "FORBIDDEN", message: "Invalid password" }); + } + + await UserRepo.setUserPassword(ctx.session.userId, await hashPassword(input.newPassword)); + await SessionRepo.deleteAllOtherSessions(ctx.session.userId, ctx.session.sessionId); + }), + + upgradeSession: roleProcedure["notClient"] + .input( + z.object({ + encPubKey: z.base64().nonempty(), + sigPubKey: z.base64().nonempty(), + }), + ) + .mutation(async ({ ctx, input }) => { + const client = await ClientRepo.getClientByPubKeys(input.encPubKey, input.sigPubKey); + const userClient = client + ? await ClientRepo.getUserClient(ctx.session.userId, client.id) + : undefined; + if (!client) { + throw new TRPCError({ code: "UNAUTHORIZED", message: "Invalid public key(s)" }); + } else if (!userClient || userClient.state === "challenging") { + throw new TRPCError({ code: "FORBIDDEN", message: "Unregistered client" }); + } + + const { answer, challenge } = await generateChallenge(32, input.encPubKey); + const { id } = await SessionRepo.registerSessionUpgradeChallenge( + ctx.session.sessionId, + client.id, + answer.toString("base64"), + ctx.locals.ip, + new Date(Date.now() + env.challenge.sessionUpgradeExp), + ); + return { id, challenge: challenge.toString("base64") }; + }), + + verifySessionUpgrade: roleProcedure["notClient"] + .input( + z.object({ + id: z.int().positive(), + answerSig: z.base64().nonempty(), + force: z.boolean().default(false), + }), + ) + .mutation(async ({ ctx, input }) => { + const challenge = await SessionRepo.consumeSessionUpgradeChallenge( + input.id, + ctx.session.sessionId, + ctx.locals.ip, + ); + if (!challenge) { + throw new TRPCError({ code: "FORBIDDEN", message: "Invalid challenge answer" }); + } + + const client = await ClientRepo.getClient(challenge.clientId); + if (!client) { + throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Invalid challenge answer" }); + } else if ( + !verifySignature(Buffer.from(challenge.answer, "base64"), input.answerSig, client.sigPubKey) + ) { + throw new TRPCError({ + code: "FORBIDDEN", + message: "Invalid challenge answer signature", + }); + } + + try { + await SessionRepo.upgradeSession( + ctx.session.userId, + ctx.session.sessionId, + client.id, + input.force, + ); + } catch (e) { + if (e instanceof IntegrityError) { + if (e.message === "Session not found") { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Invalid challenge answer", + }); + } else if (!input.force && e.message === "Session already exists") { + throw new TRPCError({ code: "CONFLICT", message: "Already logged in" }); + } + } + throw e; + } + }), +}); + +export default authRouter; diff --git a/src/trpc/routers/index.ts b/src/trpc/routers/index.ts index c943728..ab5b6a0 100644 --- a/src/trpc/routers/index.ts +++ b/src/trpc/routers/index.ts @@ -1,3 +1,4 @@ +export { default as authRouter } from "./auth"; export { default as categoryRouter } from "./category"; export { default as clientRouter } from "./client"; export { default as directoryRouter } from "./directory"; From d94d14cf83ddccc3948306ecad7f7b6e5ab229cc Mon Sep 17 00:00:00 2001 From: static Date: Fri, 26 Dec 2025 15:07:59 +0900 Subject: [PATCH 12/23] =?UTF-8?q?=EC=82=AC=EC=86=8C=ED=95=9C=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/server/middlewares/authenticate.ts | 10 +---- src/lib/server/modules/auth.ts | 25 ++++++------- src/lib/services/auth.ts | 4 +- src/trpc/routers/auth.ts | 43 ++++++++-------------- src/trpc/routers/client.ts | 1 + 5 files changed, 33 insertions(+), 50 deletions(-) diff --git a/src/lib/server/middlewares/authenticate.ts b/src/lib/server/middlewares/authenticate.ts index 49f6545..ecd1adf 100644 --- a/src/lib/server/middlewares/authenticate.ts +++ b/src/lib/server/middlewares/authenticate.ts @@ -1,6 +1,5 @@ import { error, redirect, type Handle } from "@sveltejs/kit"; -import env from "$lib/server/loadenv"; -import { authenticate, AuthenticationError } from "$lib/server/modules/auth"; +import { cookieOptions, authenticate, AuthenticationError } from "$lib/server/modules/auth"; export const authenticateMiddleware: Handle = async ({ event, resolve }) => { try { @@ -11,12 +10,7 @@ export const authenticateMiddleware: Handle = async ({ event, resolve }) => { const { ip, userAgent } = event.locals; event.locals.session = await authenticate(sessionIdSigned, ip, userAgent); - event.cookies.set("sessionId", sessionIdSigned, { - path: "/", - maxAge: env.session.exp / 1000, - secure: true, - sameSite: "strict", - }); + event.cookies.set("sessionId", sessionIdSigned, cookieOptions); } catch (e) { if (e instanceof AuthenticationError) { const { pathname, search } = event.url; diff --git a/src/lib/server/modules/auth.ts b/src/lib/server/modules/auth.ts index 6ae3865..b736a49 100644 --- a/src/lib/server/modules/auth.ts +++ b/src/lib/server/modules/auth.ts @@ -1,11 +1,9 @@ import { error } from "@sveltejs/kit"; -import { getUserClient } from "$lib/server/db/client"; -import { IntegrityError } from "$lib/server/db/error"; -import { createSession, refreshSession } from "$lib/server/db/session"; +import { ClientRepo, SessionRepo, IntegrityError } from "$lib/server/db"; import env from "$lib/server/loadenv"; -import { issueSessionId, verifySessionId } from "$lib/server/modules/crypto"; +import { verifySessionId } from "$lib/server/modules/crypto"; -interface Session { +export interface Session { sessionId: string; userId: number; clientId?: number; @@ -42,11 +40,12 @@ export class AuthorizationError extends Error { } } -export const startSession = async (userId: number, ip: string, userAgent: string) => { - const { sessionId, sessionIdSigned } = await issueSessionId(32, env.session.secret); - await createSession(userId, sessionId, ip, userAgent); - return sessionIdSigned; -}; +export const cookieOptions = { + path: "/", + maxAge: env.session.exp / 1000, + secure: true, + sameSite: "strict", +} as const; export const authenticate = async (sessionIdSigned: string, ip: string, userAgent: string) => { const sessionId = verifySessionId(sessionIdSigned, env.session.secret); @@ -55,7 +54,7 @@ export const authenticate = async (sessionIdSigned: string, ip: string, userAgen } try { - const { userId, clientId } = await refreshSession(sessionId, ip, userAgent); + const { userId, clientId } = await SessionRepo.refreshSession(sessionId, ip, userAgent); return { id: sessionId, userId, @@ -96,7 +95,7 @@ export const authorizeInternal = async ( if (!clientId) { throw new AuthorizationError(403, "Forbidden"); } - const userClient = await getUserClient(userId, clientId); + const userClient = await ClientRepo.getUserClient(userId, clientId); if (!userClient) { throw new AuthorizationError(500, "Invalid session id"); } else if (userClient.state !== "pending") { @@ -108,7 +107,7 @@ export const authorizeInternal = async ( if (!clientId) { throw new AuthorizationError(403, "Forbidden"); } - const userClient = await getUserClient(userId, clientId); + const userClient = await ClientRepo.getUserClient(userId, clientId); if (!userClient) { throw new AuthorizationError(500, "Invalid session id"); } else if (userClient.state !== "active") { diff --git a/src/lib/services/auth.ts b/src/lib/services/auth.ts index 5d2d01a..2131943 100644 --- a/src/lib/services/auth.ts +++ b/src/lib/services/auth.ts @@ -12,7 +12,7 @@ export const requestSessionUpgrade = async ( const trpc = useTRPC(); let id, challenge; try { - ({ id, challenge } = await trpc.auth.upgradeSession.mutate({ + ({ id, challenge } = await trpc.auth.upgrade.mutate({ encPubKey: encryptKeyBase64, sigPubKey: verifyKeyBase64, })); @@ -26,7 +26,7 @@ export const requestSessionUpgrade = async ( const answerSig = await signMessageRSA(answer, signKey); try { - await trpc.auth.verifySessionUpgrade.mutate({ + await trpc.auth.verifyUpgrade.mutate({ id, answerSig: encodeToBase64(answerSig), force, diff --git a/src/trpc/routers/auth.ts b/src/trpc/routers/auth.ts index e0f3a7f..e0d3d50 100644 --- a/src/trpc/routers/auth.ts +++ b/src/trpc/routers/auth.ts @@ -3,51 +3,39 @@ import argon2 from "argon2"; import { z } from "zod"; import { ClientRepo, SessionRepo, UserRepo, IntegrityError } from "$lib/server/db"; import env from "$lib/server/loadenv"; -import { startSession } from "$lib/server/modules/auth"; -import { generateChallenge, verifySignature } from "$lib/server/modules/crypto"; -import { publicProcedure, roleProcedure, router } from "../init.server"; - -const hashPassword = async (password: string) => { - return await argon2.hash(password); -}; - -const verifyPassword = async (hash: string, password: string) => { - return await argon2.verify(hash, password); -}; +import { cookieOptions } from "$lib/server/modules/auth"; +import { generateChallenge, verifySignature, issueSessionId } from "$lib/server/modules/crypto"; +import { router, publicProcedure, roleProcedure } from "../init.server"; const authRouter = router({ login: publicProcedure .input( z.object({ email: z.email(), - password: z.string().trim().nonempty(), + password: z.string().nonempty(), }), ) .mutation(async ({ ctx, input }) => { const user = await UserRepo.getUserByEmail(input.email); - if (!user || !(await verifyPassword(user.password, input.password))) { + if (!user || !(await argon2.verify(user.password, input.password))) { throw new TRPCError({ code: "UNAUTHORIZED", message: "Invalid email or password" }); } - const sessionIdSigned = await startSession(user.id, ctx.locals.ip, ctx.locals.userAgent); - ctx.cookies.set("sessionId", sessionIdSigned, { - path: "/", - maxAge: env.session.exp / 1000, - secure: true, - sameSite: "strict", - }); + const { sessionId, sessionIdSigned } = await issueSessionId(32, env.session.secret); + await SessionRepo.createSession(user.id, sessionId, ctx.locals.ip, ctx.locals.userAgent); + ctx.cookies.set("sessionId", sessionIdSigned, cookieOptions); }), logout: roleProcedure["any"].mutation(async ({ ctx }) => { await SessionRepo.deleteSession(ctx.session.sessionId); - ctx.cookies.delete("sessionId", { path: "/" }); + ctx.cookies.delete("sessionId", cookieOptions); }), changePassword: roleProcedure["any"] .input( z.object({ - oldPassword: z.string().trim().nonempty(), - newPassword: z.string().trim().nonempty(), + oldPassword: z.string().nonempty(), + newPassword: z.string().nonempty(), }), ) .mutation(async ({ ctx, input }) => { @@ -60,15 +48,15 @@ const authRouter = router({ const user = await UserRepo.getUser(ctx.session.userId); if (!user) { throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", message: "Invalid session id" }); - } else if (!(await verifyPassword(user.password, input.oldPassword))) { + } else if (!(await argon2.verify(user.password, input.oldPassword))) { throw new TRPCError({ code: "FORBIDDEN", message: "Invalid password" }); } - await UserRepo.setUserPassword(ctx.session.userId, await hashPassword(input.newPassword)); + await UserRepo.setUserPassword(ctx.session.userId, await argon2.hash(input.newPassword)); await SessionRepo.deleteAllOtherSessions(ctx.session.userId, ctx.session.sessionId); }), - upgradeSession: roleProcedure["notClient"] + upgrade: roleProcedure["notClient"] .input( z.object({ encPubKey: z.base64().nonempty(), @@ -94,10 +82,11 @@ const authRouter = router({ ctx.locals.ip, new Date(Date.now() + env.challenge.sessionUpgradeExp), ); + return { id, challenge: challenge.toString("base64") }; }), - verifySessionUpgrade: roleProcedure["notClient"] + verifyUpgrade: roleProcedure["notClient"] .input( z.object({ id: z.int().positive(), diff --git a/src/trpc/routers/client.ts b/src/trpc/routers/client.ts index 37c5bc6..db7229a 100644 --- a/src/trpc/routers/client.ts +++ b/src/trpc/routers/client.ts @@ -19,6 +19,7 @@ const createUserClientChallenge = async ( ip, new Date(Date.now() + env.challenge.userClientExp), ); + return { id, challenge: challenge.toString("base64") }; }; From c9d4b1035602cc3f837f9b6272636e43e00ac656 Mon Sep 17 00:00:00 2001 From: static Date: Fri, 26 Dec 2025 15:45:03 +0900 Subject: [PATCH 13/23] =?UTF-8?q?=EC=82=AC=EC=86=8C=ED=95=9C=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81=202?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/modules/file/upload.ts | 5 ++--- src/lib/modules/filesystem.ts | 13 +++++------- src/lib/services/auth.ts | 10 ++++------ src/lib/services/category.ts | 8 +++----- src/lib/services/file.ts | 8 +++----- src/lib/services/key.ts | 18 ++++++----------- .../auth/changePassword/service.ts | 6 ++---- src/routes/(fullscreen)/auth/login/service.ts | 6 ++---- src/routes/(fullscreen)/file/[id]/service.ts | 6 ++---- .../(fullscreen)/settings/thumbnail/+page.ts | 7 +++---- .../(main)/category/[[id]]/service.svelte.ts | 9 +++------ .../(main)/directory/[[id]]/service.svelte.ts | 20 +++++++------------ src/routes/(main)/menu/+page.ts | 7 +++---- src/trpc/client.ts | 2 +- src/trpc/routers/directory.ts | 12 ++++++----- src/trpc/routers/file.ts | 1 + 16 files changed, 54 insertions(+), 84 deletions(-) diff --git a/src/lib/modules/file/upload.ts b/src/lib/modules/file/upload.ts index 7c48503..31aabd8 100644 --- a/src/lib/modules/file/upload.ts +++ b/src/lib/modules/file/upload.ts @@ -23,15 +23,14 @@ import { type HmacSecret, type FileUploadStatus, } from "$lib/stores"; -import { useTRPC } from "$trpc/client"; +import { trpc } from "$trpc/client"; const requestDuplicateFileScan = limitFunction( async (file: File, hmacSecret: HmacSecret, onDuplicate: () => Promise) => { - const trpc = useTRPC(); const fileBuffer = await file.arrayBuffer(); const fileSigned = encodeToBase64(await signMessageHmac(fileBuffer, hmacSecret.secret)); - const files = await trpc.file.listByHash.query({ + const files = await trpc().file.listByHash.query({ hskVersion: hmacSecret.version, contentHmac: fileSigned, }); diff --git a/src/lib/modules/filesystem.ts b/src/lib/modules/filesystem.ts index 8b9b203..40e8cd4 100644 --- a/src/lib/modules/filesystem.ts +++ b/src/lib/modules/filesystem.ts @@ -18,7 +18,7 @@ import { type CategoryId, } from "$lib/indexedDB"; import { unwrapDataKey, decryptString } from "$lib/modules/crypto"; -import { useTRPC } from "$trpc/client"; +import { trpc } from "$trpc/client"; export type DirectoryInfo = | { @@ -101,10 +101,9 @@ const fetchDirectoryInfoFromServer = async ( info: Writable, masterKey: CryptoKey, ) => { - const trpc = useTRPC(); let data; try { - data = await trpc.directory.get.query({ id }); + data = await trpc().directory.get.query({ id }); } catch (e) { if (e instanceof TRPCClientError && e.data?.code === "NOT_FOUND") { info.set(null); @@ -174,10 +173,9 @@ const fetchFileInfoFromServer = async ( info: Writable, masterKey: CryptoKey, ) => { - const trpc = useTRPC(); let metadata; try { - metadata = await trpc.file.get.query({ id }); + metadata = await trpc().file.get.query({ id }); } catch (e) { if (e instanceof TRPCClientError && e.data?.code === "NOT_FOUND") { info.set(null); @@ -270,10 +268,9 @@ const fetchCategoryInfoFromServer = async ( info: Writable, masterKey: CryptoKey, ) => { - const trpc = useTRPC(); let data; try { - data = await trpc.category.get.query({ id }); + data = await trpc().category.get.query({ id }); } catch (e) { if (e instanceof TRPCClientError && e.data?.code === "NOT_FOUND") { info.set(null); @@ -293,7 +290,7 @@ const fetchCategoryInfoFromServer = async ( let files; try { - files = await trpc.category.files.query({ id, recurse: true }); + files = await trpc().category.files.query({ id, recurse: true }); } catch { throw new Error("Failed to fetch category files"); } diff --git a/src/lib/services/auth.ts b/src/lib/services/auth.ts index 2131943..56d67eb 100644 --- a/src/lib/services/auth.ts +++ b/src/lib/services/auth.ts @@ -1,6 +1,6 @@ import { TRPCClientError } from "@trpc/client"; import { encodeToBase64, decryptChallenge, signMessageRSA } from "$lib/modules/crypto"; -import { useTRPC } from "$trpc/client"; +import { trpc } from "$trpc/client"; export const requestSessionUpgrade = async ( encryptKeyBase64: string, @@ -9,10 +9,9 @@ export const requestSessionUpgrade = async ( signKey: CryptoKey, force = false, ) => { - const trpc = useTRPC(); let id, challenge; try { - ({ id, challenge } = await trpc.auth.upgrade.mutate({ + ({ id, challenge } = await trpc().auth.upgrade.mutate({ encPubKey: encryptKeyBase64, sigPubKey: verifyKeyBase64, })); @@ -26,7 +25,7 @@ export const requestSessionUpgrade = async ( const answerSig = await signMessageRSA(answer, signKey); try { - await trpc.auth.verifyUpgrade.mutate({ + await trpc().auth.verifyUpgrade.mutate({ id, answerSig: encodeToBase64(answerSig), force, @@ -42,9 +41,8 @@ export const requestSessionUpgrade = async ( }; export const requestLogout = async () => { - const trpc = useTRPC(); try { - await trpc.auth.logout.mutate(); + await trpc().auth.logout.mutate(); return true; } catch { // TODO: Error Handling diff --git a/src/lib/services/category.ts b/src/lib/services/category.ts index c86c93b..7e0443f 100644 --- a/src/lib/services/category.ts +++ b/src/lib/services/category.ts @@ -1,18 +1,17 @@ import { generateDataKey, wrapDataKey, encryptString } from "$lib/modules/crypto"; import type { MasterKey } from "$lib/stores"; -import { useTRPC } from "$trpc/client"; +import { trpc } from "$trpc/client"; export const requestCategoryCreation = async ( name: string, parentId: "root" | number, masterKey: MasterKey, ) => { - const trpc = useTRPC(); const { dataKey, dataKeyVersion } = await generateDataKey(); const nameEncrypted = await encryptString(name, dataKey); try { - await trpc.category.create.mutate({ + await trpc().category.create.mutate({ parent: parentId, mekVersion: masterKey.version, dek: await wrapDataKey(dataKey, masterKey.key), @@ -28,10 +27,9 @@ export const requestCategoryCreation = async ( }; export const requestFileRemovalFromCategory = async (fileId: number, categoryId: number) => { - const trpc = useTRPC(); try { - await trpc.category.removeFile.mutate({ id: categoryId, file: fileId }); + await trpc().category.removeFile.mutate({ id: categoryId, file: fileId }); return true; } catch { // TODO: Error Handling diff --git a/src/lib/services/file.ts b/src/lib/services/file.ts index f428e97..7e93c7a 100644 --- a/src/lib/services/file.ts +++ b/src/lib/services/file.ts @@ -12,7 +12,7 @@ import { } from "$lib/modules/file"; import { getThumbnailUrl } from "$lib/modules/thumbnail"; import type { FileThumbnailUploadRequest } from "$lib/server/schemas"; -import { useTRPC } from "$trpc/client"; +import { trpc } from "$trpc/client"; export const requestFileDownload = async ( fileId: number, @@ -49,10 +49,9 @@ export const requestFileThumbnailDownload = async (fileId: number, dataKey?: Cry const cache = await getFileThumbnailCache(fileId); if (cache || !dataKey) return cache; - const trpc = useTRPC(); let thumbnailInfo; try { - thumbnailInfo = await trpc.file.thumbnail.query({ id: fileId }); + thumbnailInfo = await trpc().file.thumbnail.query({ id: fileId }); } catch { // TODO: Error Handling return null; @@ -70,10 +69,9 @@ export const requestFileThumbnailDownload = async (fileId: number, dataKey?: Cry }; export const requestDeletedFilesCleanup = async () => { - const trpc = useTRPC(); let liveFiles; try { - liveFiles = await trpc.file.list.query(); + liveFiles = await trpc().file.list.query(); } catch { // TODO: Error Handling return; diff --git a/src/lib/services/key.ts b/src/lib/services/key.ts index 6149829..fd89f74 100644 --- a/src/lib/services/key.ts +++ b/src/lib/services/key.ts @@ -11,7 +11,7 @@ import { } from "$lib/modules/crypto"; import { requestSessionUpgrade } from "$lib/services/auth"; import { masterKeyStore, type ClientKeys } from "$lib/stores"; -import { useTRPC } from "$trpc/client"; +import { trpc } from "$trpc/client"; export const requestClientRegistration = async ( encryptKeyBase64: string, @@ -19,16 +19,14 @@ export const requestClientRegistration = async ( verifyKeyBase64: string, signKey: CryptoKey, ) => { - const trpc = useTRPC(); - try { - const { id, challenge } = await trpc.client.register.mutate({ + const { id, challenge } = await trpc().client.register.mutate({ encPubKey: encryptKeyBase64, sigPubKey: verifyKeyBase64, }); const answer = await decryptChallenge(challenge, decryptKey); const answerSig = await signMessageRSA(answer, signKey); - await trpc.client.verify.mutate({ + await trpc().client.verify.mutate({ id, answerSig: encodeToBase64(answerSig), }); @@ -69,11 +67,9 @@ export const requestClientRegistrationAndSessionUpgrade = async ( }; export const requestMasterKeyDownload = async (decryptKey: CryptoKey, verifyKey: CryptoKey) => { - const trpc = useTRPC(); - let masterKeysWrapped; try { - masterKeysWrapped = await trpc.mek.list.query(); + masterKeysWrapped = await trpc().mek.list.query(); } catch { // TODO: Error Handling return false; @@ -110,10 +106,8 @@ export const requestInitialMasterKeyAndHmacSecretRegistration = async ( hmacSecretWrapped: string, signKey: CryptoKey, ) => { - const trpc = useTRPC(); - try { - await trpc.mek.registerInitial.mutate({ + await trpc().mek.registerInitial.mutate({ mek: masterKeyWrapped, mekSig: await signMasterKeyWrapped(masterKeyWrapped, 1, signKey), }); @@ -129,7 +123,7 @@ export const requestInitialMasterKeyAndHmacSecretRegistration = async ( } try { - await trpc.hsk.registerInitial.mutate({ + await trpc().hsk.registerInitial.mutate({ mekVersion: 1, hsk: hmacSecretWrapped, }); diff --git a/src/routes/(fullscreen)/auth/changePassword/service.ts b/src/routes/(fullscreen)/auth/changePassword/service.ts index 699ec7f..4673611 100644 --- a/src/routes/(fullscreen)/auth/changePassword/service.ts +++ b/src/routes/(fullscreen)/auth/changePassword/service.ts @@ -1,10 +1,8 @@ -import { useTRPC } from "$trpc/client"; +import { trpc } from "$trpc/client"; export const requestPasswordChange = async (oldPassword: string, newPassword: string) => { - const trpc = useTRPC(); - try { - await trpc.auth.changePassword.mutate({ oldPassword, newPassword }); + await trpc().auth.changePassword.mutate({ oldPassword, newPassword }); return true; } catch { // TODO: Error Handling diff --git a/src/routes/(fullscreen)/auth/login/service.ts b/src/routes/(fullscreen)/auth/login/service.ts index 0d57545..5c8219a 100644 --- a/src/routes/(fullscreen)/auth/login/service.ts +++ b/src/routes/(fullscreen)/auth/login/service.ts @@ -1,4 +1,4 @@ -import { useTRPC } from "$trpc/client"; +import { trpc } from "$trpc/client"; export { requestLogout } from "$lib/services/auth"; export { requestDeletedFilesCleanup } from "$lib/services/file"; @@ -8,10 +8,8 @@ export { } from "$lib/services/key"; export const requestLogin = async (email: string, password: string) => { - const trpc = useTRPC(); - try { - await trpc.auth.login.mutate({ email, password }); + await trpc().auth.login.mutate({ email, password }); return true; } catch { // TODO: Error Handling diff --git a/src/routes/(fullscreen)/file/[id]/service.ts b/src/routes/(fullscreen)/file/[id]/service.ts index 73ca7f1..09ec86f 100644 --- a/src/routes/(fullscreen)/file/[id]/service.ts +++ b/src/routes/(fullscreen)/file/[id]/service.ts @@ -1,7 +1,7 @@ import { encryptData } from "$lib/modules/crypto"; import { storeFileThumbnailCache } from "$lib/modules/file"; import { requestFileThumbnailUpload } from "$lib/services/file"; -import { useTRPC } from "$trpc/client"; +import { trpc } from "$trpc/client"; export { requestCategoryCreation, requestFileRemovalFromCategory } from "$lib/services/category"; export { requestFileDownload } from "$lib/services/file"; @@ -22,10 +22,8 @@ export const requestThumbnailUpload = async ( }; export const requestFileAdditionToCategory = async (fileId: number, categoryId: number) => { - const trpc = useTRPC(); - try { - await trpc.category.addFile.mutate({ id: categoryId, file: fileId }); + await trpc().category.addFile.mutate({ id: categoryId, file: fileId }); return true; } catch { // TODO: Error Handling diff --git a/src/routes/(fullscreen)/settings/thumbnail/+page.ts b/src/routes/(fullscreen)/settings/thumbnail/+page.ts index 3bfa322..f435b72 100644 --- a/src/routes/(fullscreen)/settings/thumbnail/+page.ts +++ b/src/routes/(fullscreen)/settings/thumbnail/+page.ts @@ -1,14 +1,13 @@ import { error } from "@sveltejs/kit"; -import { useTRPC } from "$trpc/client"; +import { trpc } from "$trpc/client"; import type { PageLoad } from "./$types"; export const load: PageLoad = async ({ fetch }) => { - const trpc = useTRPC(fetch); - try { - const files = await trpc.file.listWithoutThumbnail.query(); + const files = await trpc(fetch).file.listWithoutThumbnail.query(); return { files }; } catch { + // TODO: Error Handling error(500, "Internal server error"); } }; diff --git a/src/routes/(main)/category/[[id]]/service.svelte.ts b/src/routes/(main)/category/[[id]]/service.svelte.ts index 824de8a..18f68fd 100644 --- a/src/routes/(main)/category/[[id]]/service.svelte.ts +++ b/src/routes/(main)/category/[[id]]/service.svelte.ts @@ -1,7 +1,7 @@ import { getContext, setContext } from "svelte"; import { encryptString } from "$lib/modules/crypto"; import type { SelectedCategory } from "$lib/components/molecules"; -import { useTRPC } from "$trpc/client"; +import { trpc } from "$trpc/client"; export { requestCategoryCreation, requestFileRemovalFromCategory } from "$lib/services/category"; @@ -17,11 +17,10 @@ export const useContext = () => { }; export const requestCategoryRename = async (category: SelectedCategory, newName: string) => { - const trpc = useTRPC(); const newNameEncrypted = await encryptString(newName, category.dataKey); try { - await trpc.category.rename.mutate({ + await trpc().category.rename.mutate({ id: category.id, dekVersion: category.dataKeyVersion, name: newNameEncrypted.ciphertext, @@ -35,10 +34,8 @@ export const requestCategoryRename = async (category: SelectedCategory, newName: }; export const requestCategoryDeletion = async (category: SelectedCategory) => { - const trpc = useTRPC(); - try { - await trpc.category.delete.mutate({ id: category.id }); + await trpc().category.delete.mutate({ id: category.id }); return true; } catch { // TODO: Error Handling diff --git a/src/routes/(main)/directory/[[id]]/service.svelte.ts b/src/routes/(main)/directory/[[id]]/service.svelte.ts index 72a8fdb..c94cc1e 100644 --- a/src/routes/(main)/directory/[[id]]/service.svelte.ts +++ b/src/routes/(main)/directory/[[id]]/service.svelte.ts @@ -9,7 +9,7 @@ import { uploadFile, } from "$lib/modules/file"; import { hmacSecretStore, type MasterKey, type HmacSecret } from "$lib/stores"; -import { useTRPC } from "$trpc/client"; +import { trpc } from "$trpc/client"; export interface SelectedEntry { type: "directory" | "file"; @@ -33,11 +33,9 @@ export const useContext = () => { export const requestHmacSecretDownload = async (masterKey: CryptoKey) => { // TODO: MEK rotation - const trpc = useTRPC(); - let hmacSecretsWrapped; try { - hmacSecretsWrapped = await trpc.hsk.list.query(); + hmacSecretsWrapped = await trpc().hsk.list.query(); } catch { // TODO: Error Handling return false; @@ -61,12 +59,11 @@ export const requestDirectoryCreation = async ( parentId: "root" | number, masterKey: MasterKey, ) => { - const trpc = useTRPC(); const { dataKey, dataKeyVersion } = await generateDataKey(); const nameEncrypted = await encryptString(name, dataKey); try { - await trpc.directory.create.mutate({ + await trpc().directory.create.mutate({ parent: parentId, mekVersion: masterKey.version, dek: await wrapDataKey(dataKey, masterKey.key), @@ -100,19 +97,18 @@ export const requestFileUpload = async ( }; export const requestEntryRename = async (entry: SelectedEntry, newName: string) => { - const trpc = useTRPC(); const newNameEncrypted = await encryptString(newName, entry.dataKey); try { if (entry.type === "directory") { - await trpc.directory.rename.mutate({ + await trpc().directory.rename.mutate({ id: entry.id, dekVersion: entry.dataKeyVersion, name: newNameEncrypted.ciphertext, nameIv: newNameEncrypted.iv, }); } else { - await trpc.file.rename.mutate({ + await trpc().file.rename.mutate({ id: entry.id, dekVersion: entry.dataKeyVersion, name: newNameEncrypted.ciphertext, @@ -127,11 +123,9 @@ export const requestEntryRename = async (entry: SelectedEntry, newName: string) }; export const requestEntryDeletion = async (entry: SelectedEntry) => { - const trpc = useTRPC(); - try { if (entry.type === "directory") { - const { deletedFiles } = await trpc.directory.delete.mutate({ id: entry.id }); + const { deletedFiles } = await trpc().directory.delete.mutate({ id: entry.id }); await Promise.all( deletedFiles.flatMap((fileId) => [ deleteFileCache(fileId), @@ -139,7 +133,7 @@ export const requestEntryDeletion = async (entry: SelectedEntry) => { ]), ); } else { - await trpc.file.delete.mutate({ id: entry.id }); + await trpc().file.delete.mutate({ id: entry.id }); await Promise.all([deleteFileCache(entry.id), deleteFileThumbnailCache(entry.id)]); } return true; diff --git a/src/routes/(main)/menu/+page.ts b/src/routes/(main)/menu/+page.ts index ecd8f0b..e97e395 100644 --- a/src/routes/(main)/menu/+page.ts +++ b/src/routes/(main)/menu/+page.ts @@ -1,14 +1,13 @@ import { error } from "@sveltejs/kit"; -import { useTRPC } from "$trpc/client"; +import { trpc } from "$trpc/client"; import type { PageLoad } from "./$types"; export const load: PageLoad = async ({ fetch }) => { - const trpc = useTRPC(fetch); - try { - const { nickname } = await trpc.user.get.query(); + const { nickname } = await trpc(fetch).user.get.query(); return { nickname }; } catch { + // TODO: Error Handling error(500, "Internal server error"); } }; diff --git a/src/trpc/client.ts b/src/trpc/client.ts index cb1e8c5..a24cd5d 100644 --- a/src/trpc/client.ts +++ b/src/trpc/client.ts @@ -16,7 +16,7 @@ const createClient = (fetch: typeof globalThis.fetch) => let browserClient: ReturnType; -export const useTRPC = (fetch = globalThis.fetch) => { +export const trpc = (fetch = globalThis.fetch) => { const client = browserClient ?? createClient(fetch); if (browser) { browserClient ??= client; diff --git a/src/trpc/routers/directory.ts b/src/trpc/routers/directory.ts index bcd31c7..e060c23 100644 --- a/src/trpc/routers/directory.ts +++ b/src/trpc/routers/directory.ts @@ -108,11 +108,13 @@ const directoryRouter = router({ .mutation(async ({ ctx, input }) => { try { const files = await FileRepo.unregisterDirectory(ctx.session.userId, input.id); - files.forEach(({ path, thumbnailPath }) => { - safeUnlink(path); // Intended - safeUnlink(thumbnailPath); // Intended - }); - return { deletedFiles: files.map(({ id }) => id) }; + return { + deletedFiles: files.map((file) => { + safeUnlink(file.path); // Intended + safeUnlink(file.thumbnailPath); // Intended + return file.id; + }), + }; } catch (e) { if (e instanceof IntegrityError && e.message === "Directory not found") { throw new TRPCError({ code: "NOT_FOUND", message: "Invalid directory id" }); diff --git a/src/trpc/routers/file.ts b/src/trpc/routers/file.ts index 7c4e425..decbc76 100644 --- a/src/trpc/routers/file.ts +++ b/src/trpc/routers/file.ts @@ -115,6 +115,7 @@ const fileRouter = router({ if (!thumbnail) { throw new TRPCError({ code: "NOT_FOUND", message: "File or its thumbnail not found" }); } + return { updatedAt: thumbnail.updatedAt, contentIv: thumbnail.encContentIv }; }), }); From 3eb7411438e873bdda42c6a76bfbf35bd37e192f Mon Sep 17 00:00:00 2001 From: static Date: Fri, 26 Dec 2025 15:57:05 +0900 Subject: [PATCH 14/23] =?UTF-8?q?=EC=82=AC=EC=86=8C=ED=95=9C=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81=203?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/hooks/callApi.ts | 11 ----------- src/lib/services/category.ts | 1 - src/lib/services/file.ts | 3 +-- src/lib/{hooks => utils}/gotoStateful.ts | 0 src/lib/{hooks => utils}/index.ts | 1 - src/routes/(fullscreen)/key/export/+page.ts | 2 +- src/routes/(fullscreen)/key/generate/+page.svelte | 2 +- src/trpc/init.server.ts | 3 +-- 8 files changed, 4 insertions(+), 19 deletions(-) delete mode 100644 src/lib/hooks/callApi.ts rename src/lib/{hooks => utils}/gotoStateful.ts (100%) rename src/lib/{hooks => utils}/index.ts (54%) diff --git a/src/lib/hooks/callApi.ts b/src/lib/hooks/callApi.ts deleted file mode 100644 index 1699ec2..0000000 --- a/src/lib/hooks/callApi.ts +++ /dev/null @@ -1,11 +0,0 @@ -export const callGetApi = async (input: RequestInfo, fetchInternal = fetch) => { - return await fetchInternal(input); -}; - -export const callPostApi = async (input: RequestInfo, payload?: T, fetchInternal = fetch) => { - return await fetchInternal(input, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: payload ? JSON.stringify(payload) : undefined, - }); -}; diff --git a/src/lib/services/category.ts b/src/lib/services/category.ts index 7e0443f..c53a6f2 100644 --- a/src/lib/services/category.ts +++ b/src/lib/services/category.ts @@ -27,7 +27,6 @@ export const requestCategoryCreation = async ( }; export const requestFileRemovalFromCategory = async (fileId: number, categoryId: number) => { - try { await trpc().category.removeFile.mutate({ id: categoryId, file: fileId }); return true; diff --git a/src/lib/services/file.ts b/src/lib/services/file.ts index 7e93c7a..da05824 100644 --- a/src/lib/services/file.ts +++ b/src/lib/services/file.ts @@ -1,4 +1,3 @@ -import { callGetApi } from "$lib/hooks"; import { getAllFileInfos } from "$lib/indexedDB/filesystem"; import { decryptData } from "$lib/modules/crypto"; import { @@ -58,7 +57,7 @@ export const requestFileThumbnailDownload = async (fileId: number, dataKey?: Cry } const { contentIv: thumbnailEncryptedIv } = thumbnailInfo; - const res = await callGetApi(`/api/file/${fileId}/thumbnail/download`); + const res = await fetch(`/api/file/${fileId}/thumbnail/download`); if (!res.ok) return null; const thumbnailEncrypted = await res.arrayBuffer(); diff --git a/src/lib/hooks/gotoStateful.ts b/src/lib/utils/gotoStateful.ts similarity index 100% rename from src/lib/hooks/gotoStateful.ts rename to src/lib/utils/gotoStateful.ts diff --git a/src/lib/hooks/index.ts b/src/lib/utils/index.ts similarity index 54% rename from src/lib/hooks/index.ts rename to src/lib/utils/index.ts index e3b8dde..4c24322 100644 --- a/src/lib/hooks/index.ts +++ b/src/lib/utils/index.ts @@ -1,2 +1 @@ -export * from "./callApi"; export * from "./gotoStateful"; diff --git a/src/routes/(fullscreen)/key/export/+page.ts b/src/routes/(fullscreen)/key/export/+page.ts index a64ea53..5785158 100644 --- a/src/routes/(fullscreen)/key/export/+page.ts +++ b/src/routes/(fullscreen)/key/export/+page.ts @@ -1,5 +1,5 @@ import { error } from "@sveltejs/kit"; -import { keyExportState } from "$lib/hooks/gotoStateful"; +import { keyExportState } from "$lib/utils/gotoStateful"; import type { PageLoad } from "./$types"; export const load: PageLoad = async () => { diff --git a/src/routes/(fullscreen)/key/generate/+page.svelte b/src/routes/(fullscreen)/key/generate/+page.svelte index 6f7609c..7292d78 100644 --- a/src/routes/(fullscreen)/key/generate/+page.svelte +++ b/src/routes/(fullscreen)/key/generate/+page.svelte @@ -4,9 +4,9 @@ import { BottomDiv, Button, FullscreenDiv, TextButton } from "$lib/components/atoms"; import { TitledDiv } from "$lib/components/molecules"; import { ForceLoginModal } from "$lib/components/organisms"; - import { gotoStateful } from "$lib/hooks"; import { storeClientKeys } from "$lib/modules/key"; import { clientKeyStore } from "$lib/stores"; + import { gotoStateful } from "$lib/utils"; import Order from "./Order.svelte"; import { generateClientKeys, diff --git a/src/trpc/init.server.ts b/src/trpc/init.server.ts index 8b88157..8d5d7c5 100644 --- a/src/trpc/init.server.ts +++ b/src/trpc/init.server.ts @@ -3,11 +3,10 @@ import { initTRPC, TRPCError } from "@trpc/server"; import superjson from "superjson"; import { authorizeMiddleware, authorizeClientMiddleware } from "./middlewares/authorize"; +export const createContext = (event: RequestEvent) => event; export type Context = Awaited>; -export const createContext = (event: RequestEvent) => event; export const t = initTRPC.context().create({ transformer: superjson }); - export const router = t.router; export const publicProcedure = t.procedure; From ed21a9cd31c1f851774e714103939b989d389664 Mon Sep 17 00:00:00 2001 From: static Date: Fri, 26 Dec 2025 22:29:44 +0900 Subject: [PATCH 15/23] =?UTF-8?q?=EA=B0=A4=EB=9F=AC=EB=A6=AC=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + pnpm-lock.yaml | 18 +++ .../molecules/Categories/Categories.svelte | 2 +- .../molecules/Gallery/Gallery.svelte | 128 ++++++++++++++++++ .../molecules/Gallery/Thumbnail.svelte | 42 ++++++ src/lib/components/molecules/Gallery/index.ts | 1 + src/lib/components/molecules/index.ts | 1 + .../organisms/Category/Category.svelte | 2 +- src/lib/{modules/util.ts => utils/format.ts} | 36 +---- src/lib/utils/index.ts | 2 + src/lib/utils/sort.ts | 57 ++++++++ .../file/[id]/DownloadStatus.svelte | 2 +- .../(fullscreen)/file/downloads/File.svelte | 2 +- .../(fullscreen)/file/uploads/File.svelte | 2 +- src/routes/(fullscreen)/gallery/+page.svelte | 25 ++++ src/routes/(fullscreen)/gallery/+page.ts | 7 + .../(fullscreen)/settings/cache/+page.svelte | 2 +- .../(fullscreen)/settings/cache/File.svelte | 2 +- .../(fullscreen)/settings/thumbnail/+page.ts | 10 +- .../settings/thumbnail/File.svelte | 2 +- .../[[id]]/CategoryDeleteModal.svelte | 2 +- .../DirectoryEntries/DirectoryEntries.svelte | 2 +- .../[[id]]/DirectoryEntries/File.svelte | 2 +- .../DirectoryEntries/UploadingFile.svelte | 2 +- .../[[id]]/DuplicateFileModal.svelte | 2 +- .../directory/[[id]]/EntryDeleteModal.svelte | 2 +- src/routes/(main)/menu/+page.ts | 10 +- 27 files changed, 307 insertions(+), 59 deletions(-) create mode 100644 src/lib/components/molecules/Gallery/Gallery.svelte create mode 100644 src/lib/components/molecules/Gallery/Thumbnail.svelte create mode 100644 src/lib/components/molecules/Gallery/index.ts rename src/lib/{modules/util.ts => utils/format.ts} (64%) create mode 100644 src/lib/utils/sort.ts create mode 100644 src/routes/(fullscreen)/gallery/+page.svelte create mode 100644 src/routes/(fullscreen)/gallery/+page.ts diff --git a/package.json b/package.json index ce46336..bd44d2c 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@sveltejs/adapter-node": "^5.4.0", "@sveltejs/kit": "^2.49.2", "@sveltejs/vite-plugin-svelte": "^6.2.1", + "@tanstack/svelte-virtual": "^3.13.13", "@trpc/client": "^11.8.1", "@types/file-saver": "^2.0.7", "@types/ms": "^0.7.34", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e3de21a..9503e44 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -54,6 +54,9 @@ importers: '@sveltejs/vite-plugin-svelte': specifier: ^6.2.1 version: 6.2.1(svelte@5.46.1)(vite@7.3.0(@types/node@25.0.3)(jiti@1.21.7)(yaml@2.8.0)) + '@tanstack/svelte-virtual': + specifier: ^3.13.13 + version: 3.13.13(svelte@5.46.1) '@trpc/client': specifier: ^11.8.1 version: 11.8.1(@trpc/server@11.8.1(typescript@5.9.3))(typescript@5.9.3) @@ -610,6 +613,14 @@ packages: svelte: ^5.0.0 vite: ^6.3.0 || ^7.0.0 + '@tanstack/svelte-virtual@3.13.13': + resolution: {integrity: sha512-VDOvbRw3R+XBQdFodEJ4E7AOmEyo3Bmr4zL4DLVnJ0fxICdbvY5F5t8zSwJ4f7lqjckXi0yKFzY8WBtjaNbsGQ==} + peerDependencies: + svelte: ^3.48.0 || ^4.0.0 || ^5.0.0 + + '@tanstack/virtual-core@3.13.13': + resolution: {integrity: sha512-uQFoSdKKf5S8k51W5t7b2qpfkyIbdHMzAn+AMQvHPxKUPeo1SsGaA4JRISQT87jm28b7z8OEqPcg1IOZagQHcA==} + '@trpc/client@11.8.1': resolution: {integrity: sha512-L/SJFGanr9xGABmuDoeXR4xAdHJmsXsiF9OuH+apecJ+8sUITzVT1EPeqp0ebqA6lBhEl5pPfg3rngVhi/h60Q==} peerDependencies: @@ -2367,6 +2378,13 @@ snapshots: transitivePeerDependencies: - supports-color + '@tanstack/svelte-virtual@3.13.13(svelte@5.46.1)': + dependencies: + '@tanstack/virtual-core': 3.13.13 + svelte: 5.46.1 + + '@tanstack/virtual-core@3.13.13': {} + '@trpc/client@11.8.1(@trpc/server@11.8.1(typescript@5.9.3))(typescript@5.9.3)': dependencies: '@trpc/server': 11.8.1(typescript@5.9.3) diff --git a/src/lib/components/molecules/Categories/Categories.svelte b/src/lib/components/molecules/Categories/Categories.svelte index a11313e..54368c6 100644 --- a/src/lib/components/molecules/Categories/Categories.svelte +++ b/src/lib/components/molecules/Categories/Categories.svelte @@ -3,7 +3,7 @@ import type { SvelteHTMLElements } from "svelte/elements"; import { get, type Writable } from "svelte/store"; import type { CategoryInfo } from "$lib/modules/filesystem"; - import { SortBy, sortEntries } from "$lib/modules/util"; + import { SortBy, sortEntries } from "$lib/utils"; import Category from "./Category.svelte"; import type { SelectedCategory } from "./service"; diff --git a/src/lib/components/molecules/Gallery/Gallery.svelte b/src/lib/components/molecules/Gallery/Gallery.svelte new file mode 100644 index 0000000..1bcb335 --- /dev/null +++ b/src/lib/components/molecules/Gallery/Gallery.svelte @@ -0,0 +1,128 @@ + + +
+
+ {#each $virtualizer.getVirtualItems() as virtualRow (virtualRow.key)} + {@const row = rows[virtualRow.index]!} +
+ {#if row.type === "header"} +

{row.label}

+ {:else} +
+ {#each row.items as { info }} + + {/each} +
+ {/if} +
+ {/each} +
+ {#if $virtualizer.getVirtualItems().length === 0} +
+

+ {#if files.length === 0} + 업로드된 파일이 없어요. + {:else if filesWithDate.length === 0} + 파일 목록을 불러오고 있어요. + {:else} + 사진 또는 동영상이 없어요. + {/if} +

+
+ {/if} +
diff --git a/src/lib/components/molecules/Gallery/Thumbnail.svelte b/src/lib/components/molecules/Gallery/Thumbnail.svelte new file mode 100644 index 0000000..463419a --- /dev/null +++ b/src/lib/components/molecules/Gallery/Thumbnail.svelte @@ -0,0 +1,42 @@ + + +{#if $info} + +{/if} diff --git a/src/lib/components/molecules/Gallery/index.ts b/src/lib/components/molecules/Gallery/index.ts new file mode 100644 index 0000000..9a269ab --- /dev/null +++ b/src/lib/components/molecules/Gallery/index.ts @@ -0,0 +1 @@ +export { default as Gallery } from "./Gallery.svelte"; diff --git a/src/lib/components/molecules/index.ts b/src/lib/components/molecules/index.ts index 8edc84a..b11ed5f 100644 --- a/src/lib/components/molecules/index.ts +++ b/src/lib/components/molecules/index.ts @@ -2,6 +2,7 @@ export * from "./ActionModal.svelte"; export { default as ActionModal } from "./ActionModal.svelte"; export * from "./Categories"; export { default as Categories } from "./Categories"; +export * from "./Gallery"; export { default as IconEntryButton } from "./IconEntryButton.svelte"; export * from "./labels"; export { default as SubCategories } from "./SubCategories.svelte"; diff --git a/src/lib/components/organisms/Category/Category.svelte b/src/lib/components/organisms/Category/Category.svelte index 295bf99..ce3abcd 100644 --- a/src/lib/components/organisms/Category/Category.svelte +++ b/src/lib/components/organisms/Category/Category.svelte @@ -4,8 +4,8 @@ import { CheckBox } from "$lib/components/atoms"; import { SubCategories, type SelectedCategory } from "$lib/components/molecules"; import { getFileInfo, type FileInfo, type CategoryInfo } from "$lib/modules/filesystem"; - import { SortBy, sortEntries } from "$lib/modules/util"; import { masterKeyStore } from "$lib/stores"; + import { SortBy, sortEntries } from "$lib/utils"; import File from "./File.svelte"; import type { SelectedFile } from "./service"; diff --git a/src/lib/modules/util.ts b/src/lib/utils/format.ts similarity index 64% rename from src/lib/modules/util.ts rename to src/lib/utils/format.ts index 3fff89d..ce3b35c 100644 --- a/src/lib/modules/util.ts +++ b/src/lib/utils/format.ts @@ -7,6 +7,13 @@ export const formatDate = (date: Date) => { return `${year}. ${month}. ${day}.`; }; +export const formatDateSortable = (date: Date) => { + const year = date.getFullYear(); + const month = pad2(date.getMonth() + 1); + const day = pad2(date.getDate()); + return `${year}${month}${day}`; +}; + export const formatDateTime = (date: Date) => { const dateFormatted = formatDate(date); const hours = date.getHours(); @@ -32,32 +39,3 @@ export const truncateString = (str: string, maxLength = 20) => { if (str.length <= maxLength) return str; return `${str.slice(0, maxLength)}...`; }; - -export enum SortBy { - NAME_ASC, - NAME_DESC, -} - -type SortFunc = (a?: string, b?: string) => number; - -const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" }); - -const sortByNameAsc: SortFunc = (a, b) => { - if (a && b) return collator.compare(a, b); - if (a) return -1; - if (b) return 1; - return 0; -}; - -const sortByNameDesc: SortFunc = (a, b) => -sortByNameAsc(a, b); - -export const sortEntries = (entries: T[], sortBy: SortBy) => { - let sortFunc: SortFunc; - if (sortBy === SortBy.NAME_ASC) { - sortFunc = sortByNameAsc; - } else { - sortFunc = sortByNameDesc; - } - - entries.sort((a, b) => sortFunc(a.name, b.name)); -}; diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index 4c24322..1db9577 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -1 +1,3 @@ +export * from "./format"; export * from "./gotoStateful"; +export * from "./sort"; diff --git a/src/lib/utils/sort.ts b/src/lib/utils/sort.ts new file mode 100644 index 0000000..2385e55 --- /dev/null +++ b/src/lib/utils/sort.ts @@ -0,0 +1,57 @@ +interface SortEntry { + name?: string; + date?: Date; +} + +export enum SortBy { + NAME_ASC, + NAME_DESC, + DATE_ASC, + DATE_DESC, +} + +type SortFunc = (a: SortEntry, b: SortEntry) => number; + +const collator = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" }); + +const sortByNameAsc: SortFunc = ({ name: a }, { name: b }) => { + if (a && b) return collator.compare(a, b); + if (a) return -1; + if (b) return 1; + return 0; +}; + +const sortByNameDesc: SortFunc = (a, b) => -sortByNameAsc(a, b); + +const sortByDateAsc: SortFunc = ({ date: a }, { date: b }) => { + if (a && b) return a.getTime() - b.getTime(); + if (a) return -1; + if (b) return 1; + return 0; +}; + +const sortByDateDesc: SortFunc = (a, b) => -sortByDateAsc(a, b); + +export const sortEntries = (entries: T[], sortBy: SortBy) => { + let sortFunc: SortFunc; + + switch (sortBy) { + case SortBy.NAME_ASC: + sortFunc = sortByNameAsc; + break; + case SortBy.NAME_DESC: + sortFunc = sortByNameDesc; + break; + case SortBy.DATE_ASC: + sortFunc = sortByDateAsc; + break; + case SortBy.DATE_DESC: + sortFunc = sortByDateDesc; + break; + default: + const exhaustive: never = sortBy; + sortFunc = exhaustive; + } + + entries.sort(sortFunc); +}; diff --git a/src/routes/(fullscreen)/file/[id]/DownloadStatus.svelte b/src/routes/(fullscreen)/file/[id]/DownloadStatus.svelte index 21774cd..150669e 100644 --- a/src/routes/(fullscreen)/file/[id]/DownloadStatus.svelte +++ b/src/routes/(fullscreen)/file/[id]/DownloadStatus.svelte @@ -1,7 +1,7 @@ + + + 사진 및 동영상 + + + + + goto(`/file/${id}`)} /> + diff --git a/src/routes/(fullscreen)/gallery/+page.ts b/src/routes/(fullscreen)/gallery/+page.ts new file mode 100644 index 0000000..1a241c5 --- /dev/null +++ b/src/routes/(fullscreen)/gallery/+page.ts @@ -0,0 +1,7 @@ +import { trpc } from "$trpc/client"; +import type { PageLoad } from "./$types"; + +export const load: PageLoad = async ({ fetch }) => { + const files = await trpc(fetch).file.list.query(); + return { files }; +}; diff --git a/src/routes/(fullscreen)/settings/cache/+page.svelte b/src/routes/(fullscreen)/settings/cache/+page.svelte index af375c2..cf8192d 100644 --- a/src/routes/(fullscreen)/settings/cache/+page.svelte +++ b/src/routes/(fullscreen)/settings/cache/+page.svelte @@ -6,8 +6,8 @@ import type { FileCacheIndex } from "$lib/indexedDB"; import { getFileCacheIndex, deleteFileCache as doDeleteFileCache } from "$lib/modules/file"; import { getFileInfo, type FileInfo } from "$lib/modules/filesystem"; - import { formatFileSize } from "$lib/modules/util"; import { masterKeyStore } from "$lib/stores"; + import { formatFileSize } from "$lib/utils"; import File from "./File.svelte"; interface FileCache { diff --git a/src/routes/(fullscreen)/settings/cache/File.svelte b/src/routes/(fullscreen)/settings/cache/File.svelte index f21445b..581d144 100644 --- a/src/routes/(fullscreen)/settings/cache/File.svelte +++ b/src/routes/(fullscreen)/settings/cache/File.svelte @@ -2,7 +2,7 @@ import type { Writable } from "svelte/store"; import type { FileCacheIndex } from "$lib/indexedDB"; import type { FileInfo } from "$lib/modules/filesystem"; - import { formatDate, formatFileSize } from "$lib/modules/util"; + import { formatDate, formatFileSize } from "$lib/utils"; import IconDraft from "~icons/material-symbols/draft"; import IconScanDelete from "~icons/material-symbols/scan-delete"; diff --git a/src/routes/(fullscreen)/settings/thumbnail/+page.ts b/src/routes/(fullscreen)/settings/thumbnail/+page.ts index f435b72..4d5520c 100644 --- a/src/routes/(fullscreen)/settings/thumbnail/+page.ts +++ b/src/routes/(fullscreen)/settings/thumbnail/+page.ts @@ -1,13 +1,7 @@ -import { error } from "@sveltejs/kit"; import { trpc } from "$trpc/client"; import type { PageLoad } from "./$types"; export const load: PageLoad = async ({ fetch }) => { - try { - const files = await trpc(fetch).file.listWithoutThumbnail.query(); - return { files }; - } catch { - // TODO: Error Handling - error(500, "Internal server error"); - } + const files = await trpc(fetch).file.listWithoutThumbnail.query(); + return { files }; }; diff --git a/src/routes/(fullscreen)/settings/thumbnail/File.svelte b/src/routes/(fullscreen)/settings/thumbnail/File.svelte index 8977fc7..93c23ad 100644 --- a/src/routes/(fullscreen)/settings/thumbnail/File.svelte +++ b/src/routes/(fullscreen)/settings/thumbnail/File.svelte @@ -14,7 +14,7 @@ import { ActionEntryButton } from "$lib/components/atoms"; import { DirectoryEntryLabel } from "$lib/components/molecules"; import type { FileInfo } from "$lib/modules/filesystem"; - import { formatDateTime } from "$lib/modules/util"; + import { formatDateTime } from "$lib/utils"; import type { GenerationStatus } from "./service.svelte"; import IconCamera from "~icons/material-symbols/camera"; diff --git a/src/routes/(main)/category/[[id]]/CategoryDeleteModal.svelte b/src/routes/(main)/category/[[id]]/CategoryDeleteModal.svelte index 5e792d1..289b971 100644 --- a/src/routes/(main)/category/[[id]]/CategoryDeleteModal.svelte +++ b/src/routes/(main)/category/[[id]]/CategoryDeleteModal.svelte @@ -1,6 +1,6 @@ + + + + + +
+

ArkVault

+
+ goto("/gallery")} class="w-full"> +

사진 및 동영상

+
+
+ {#each files as file} + goto(`/file/${id}`)} /> + {/each} +
+
diff --git a/src/routes/(main)/home/service.ts b/src/routes/(main)/home/service.ts new file mode 100644 index 0000000..50dc652 --- /dev/null +++ b/src/routes/(main)/home/service.ts @@ -0,0 +1,10 @@ +import { getAllFileInfos } from "$lib/indexedDB"; + +export const requestFreshFilesRetrieval = async (limit = 8) => { + const files = await getAllFileInfos(); + files.sort( + (a, b) => + (b.createdAt ?? b.lastModifiedAt).getTime() - (a.createdAt ?? a.lastModifiedAt).getTime(), + ); + return files.slice(0, limit); +}; From a1f30ee1542bd6190b61950953befa9efd13ea06 Mon Sep 17 00:00:00 2001 From: static Date: Fri, 26 Dec 2025 22:58:09 +0900 Subject: [PATCH 17/23] =?UTF-8?q?=ED=99=88=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EC=99=80=20=EA=B0=A4=EB=9F=AC=EB=A6=AC=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=EC=97=90=EC=84=9C=20=EC=82=AC=EC=A7=84=20=EB=B0=8F=20?= =?UTF-8?q?=EB=8F=99=EC=98=81=EC=83=81=EB=A7=8C=20=ED=91=9C=EC=8B=9C?= =?UTF-8?q?=EB=90=98=EB=8F=84=EB=A1=9D=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/components/organisms/Gallery.svelte | 23 +++++++++++++++++---- src/routes/(main)/home/+page.svelte | 10 ++++----- src/routes/(main)/home/service.ts | 8 +++++-- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/lib/components/organisms/Gallery.svelte b/src/lib/components/organisms/Gallery.svelte index 9f80846..521c287 100644 --- a/src/lib/components/organisms/Gallery.svelte +++ b/src/lib/components/organisms/Gallery.svelte @@ -13,7 +13,9 @@ let { files, onFileClick }: Props = $props(); - type FileEntry = { date?: Date; info: Writable }; + type FileEntry = + | { date?: undefined; contentType?: undefined; info: Writable } + | { date: Date; contentType: string; info: Writable }; type Row = | { type: "header"; key: string; label: string } | { type: "items"; key: string; items: FileEntry[] }; @@ -37,15 +39,28 @@ $effect(() => { filesWithDate = files.map((file) => { - const { createdAt, lastModifiedAt } = get(file) ?? {}; - return { date: createdAt ?? lastModifiedAt, info: file }; + const info = get(file); + if (info) { + return { + date: info.createdAt ?? info.lastModifiedAt, + contentType: info.contentType, + info: file, + }; + } else { + return { info: file }; + } }); const buildRows = () => { const map = new Map(); for (const file of filesWithDate) { - if (!file.date) continue; + if ( + !file.date || + !(file.contentType.startsWith("image/") || file.contentType.startsWith("video/")) + ) { + continue; + } const date = formatDateSortable(file.date); const entries = map.get(date) ?? []; diff --git a/src/routes/(main)/home/+page.svelte b/src/routes/(main)/home/+page.svelte index 8393641..e8997c4 100644 --- a/src/routes/(main)/home/+page.svelte +++ b/src/routes/(main)/home/+page.svelte @@ -5,13 +5,13 @@ import { FileThumbnailButton } from "$lib/components/molecules"; import { getFileInfo, type FileInfo } from "$lib/modules/filesystem"; import { masterKeyStore } from "$lib/stores"; - import { requestFreshFilesRetrieval } from "./service"; + import { requestFreshMediaFilesRetrieval } from "./service"; - let files: Writable[] = $state([]); + let mediaFiles: Writable[] = $state([]); $effect(() => { - requestFreshFilesRetrieval().then((retrievedFiles) => { - files = retrievedFiles.map(({ id }) => getFileInfo(id, $masterKeyStore?.get(1)?.key!)); + requestFreshMediaFilesRetrieval().then((files) => { + mediaFiles = files.map(({ id }) => getFileInfo(id, $masterKeyStore?.get(1)?.key!)); }); }); @@ -27,7 +27,7 @@

사진 및 동영상

- {#each files as file} + {#each mediaFiles as file} goto(`/file/${id}`)} /> {/each}
diff --git a/src/routes/(main)/home/service.ts b/src/routes/(main)/home/service.ts index 50dc652..6d02199 100644 --- a/src/routes/(main)/home/service.ts +++ b/src/routes/(main)/home/service.ts @@ -1,10 +1,14 @@ import { getAllFileInfos } from "$lib/indexedDB"; -export const requestFreshFilesRetrieval = async (limit = 8) => { +export const requestFreshMediaFilesRetrieval = async (limit = 8) => { const files = await getAllFileInfos(); files.sort( (a, b) => (b.createdAt ?? b.lastModifiedAt).getTime() - (a.createdAt ?? a.lastModifiedAt).getTime(), ); - return files.slice(0, limit); + return files + .filter( + ({ contentType }) => contentType.startsWith("image/") || contentType.startsWith("video/"), + ) + .slice(0, limit); }; From 27a46bcc2e0911b915ae6443b370ce4666e2e55b Mon Sep 17 00:00:00 2001 From: static Date: Fri, 26 Dec 2025 23:12:37 +0900 Subject: [PATCH 18/23] =?UTF-8?q?eslint.config.js=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- eslint.config.js | 24 ++++++++++++++++-------- package.json | 4 ++-- pnpm-lock.yaml | 22 +++++++++++++++------- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 4027dc6..f12d6e7 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,21 +1,24 @@ -import prettier from "eslint-config-prettier"; -import js from "@eslint/js"; import { includeIgnoreFile } from "@eslint/compat"; +import js from "@eslint/js"; +import { defineConfig } from "eslint/config"; +import prettier from "eslint-config-prettier"; import svelte from "eslint-plugin-svelte"; import tailwind from "eslint-plugin-tailwindcss"; import globals from "globals"; -import { fileURLToPath } from "node:url"; import ts from "typescript-eslint"; +import { fileURLToPath } from "url"; +import svelteConfig from "./svelte.config.js"; + const gitignorePath = fileURLToPath(new URL("./.gitignore", import.meta.url)); -export default ts.config( +export default defineConfig( includeIgnoreFile(gitignorePath), js.configs.recommended, ...ts.configs.recommended, - ...svelte.configs["flat/recommended"], + ...svelte.configs.recommended, ...tailwind.configs["flat/recommended"], prettier, - ...svelte.configs["flat/prettier"], + ...svelte.configs.prettier, { languageOptions: { globals: { @@ -23,13 +26,18 @@ export default ts.config( ...globals.node, }, }, + rules: { + "no-undef": "off", + }, }, { - files: ["**/*.svelte"], - + files: ["**/*.svelte", "**/*.svelte.ts", "**/*.svelte.js"], languageOptions: { parserOptions: { + projectService: true, + extraFileExtensions: [".svelte"], parser: ts.parser, + svelteConfig, }, }, }, diff --git a/package.json b/package.json index bd44d2c..3479c2c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "arkvault", "private": true, - "version": "0.5.1", + "version": "0.6.0", "type": "module", "scripts": { "dev": "vite dev", @@ -16,7 +16,7 @@ "db:migrate": "kysely migrate" }, "devDependencies": { - "@eslint/compat": "^1.4.1", + "@eslint/compat": "^2.0.0", "@iconify-json/material-symbols": "^1.2.50", "@sveltejs/adapter-node": "^5.4.0", "@sveltejs/kit": "^2.49.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9503e44..9dcf04f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,8 +40,8 @@ importers: version: 4.2.1 devDependencies: '@eslint/compat': - specifier: ^1.4.1 - version: 1.4.1(eslint@9.39.2(jiti@1.21.7)) + specifier: ^2.0.0 + version: 2.0.0(eslint@9.39.2(jiti@1.21.7)) '@iconify-json/material-symbols': specifier: ^1.2.50 version: 1.2.50 @@ -326,9 +326,9 @@ packages: resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/compat@1.4.1': - resolution: {integrity: sha512-cfO82V9zxxGBxcQDr1lfaYB7wykTa0b00mGa36FrJl7iTFd0Z2cHfEYuxcBRP/iNijCsWsEkA+jzT8hGYmv33w==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/compat@2.0.0': + resolution: {integrity: sha512-T9AfE1G1uv4wwq94ozgTGio5EUQBqAVe1X9qsQtSNVEYW6j3hvtZVm8Smr4qL1qDPFg+lOB2cL5RxTRMzq4CTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} peerDependencies: eslint: ^8.40 || 9 peerDependenciesMeta: @@ -347,6 +347,10 @@ packages: resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/core@1.0.0': + resolution: {integrity: sha512-PRfWP+8FOldvbApr6xL7mNCw4cJcSTq4GA7tYbgq15mRb0kWKO/wEB2jr+uwjFH3sZvEZneZyCUGTxsv4Sahyw==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + '@eslint/eslintrc@3.3.3': resolution: {integrity: sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2117,9 +2121,9 @@ snapshots: '@eslint-community/regexpp@4.12.2': {} - '@eslint/compat@1.4.1(eslint@9.39.2(jiti@1.21.7))': + '@eslint/compat@2.0.0(eslint@9.39.2(jiti@1.21.7))': dependencies: - '@eslint/core': 0.17.0 + '@eslint/core': 1.0.0 optionalDependencies: eslint: 9.39.2(jiti@1.21.7) @@ -2139,6 +2143,10 @@ snapshots: dependencies: '@types/json-schema': 7.0.15 + '@eslint/core@1.0.0': + dependencies: + '@types/json-schema': 7.0.15 + '@eslint/eslintrc@3.3.3': dependencies: ajv: 6.12.6 From 3e98e3d59134ca421a6c2f4f4072fe6e2e699ea7 Mon Sep 17 00:00:00 2001 From: static Date: Fri, 26 Dec 2025 23:29:29 +0900 Subject: [PATCH 19/23] =?UTF-8?q?=EA=B0=A4=EB=9F=AC=EB=A6=AC=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=EC=97=90=EC=84=9C=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EC=9D=B4=20=ED=91=9C=EC=8B=9C=EB=90=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8D=98=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/components/organisms/Gallery.svelte | 7 ++++++- src/routes/(main)/BottomBar.svelte | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/lib/components/organisms/Gallery.svelte b/src/lib/components/organisms/Gallery.svelte index 521c287..4ee8a37 100644 --- a/src/lib/components/organisms/Gallery.svelte +++ b/src/lib/components/organisms/Gallery.svelte @@ -95,8 +95,13 @@ const unsubscribes = filesWithDate.map((file) => file.info.subscribe((value) => { const newDate = value?.createdAt ?? value?.lastModifiedAt; - if (file.date?.getTime() === newDate?.getTime()) return; + const newContentType = value?.contentType; + if (file.date?.getTime() === newDate?.getTime() && file.contentType === newContentType) { + return; + } + file.date = newDate; + file.contentType = newContentType; buildRows(); }), ); diff --git a/src/routes/(main)/BottomBar.svelte b/src/routes/(main)/BottomBar.svelte index 60c2b24..e1008df 100644 --- a/src/routes/(main)/BottomBar.svelte +++ b/src/routes/(main)/BottomBar.svelte @@ -22,7 +22,7 @@ {#each pages as { path, label, icon: Icon }} + + +
diff --git a/src/routes/(fullscreen)/file/[id]/TopBarMenu.svelte b/src/routes/(fullscreen)/file/[id]/TopBarMenu.svelte new file mode 100644 index 0000000..8c92986 --- /dev/null +++ b/src/routes/(fullscreen)/file/[id]/TopBarMenu.svelte @@ -0,0 +1,58 @@ + + + (isOpen = false)} /> + +{#if isOpen && (directoryId || fileBlob)} +
+

더보기

+
+ {#snippet menuButton( + Icon: Component, + text: string, + onclick: () => void, + )} + + {/snippet} + + {#if directoryId} + {@render menuButton(IconFolderOpen, "폴더에서 보기", () => + goto(directoryId === "root" ? "/directory" : `/directory/${directoryId}`), + )} + {/if} + {#if fileBlob} + {@render menuButton(IconCloudDownload, "다운로드", () => { + console.log(filename); + FileSaver.saveAs(fileBlob, filename); + })} + {/if} +
+
+{/if} diff --git a/src/routes/(main)/category/[[id]]/+page.svelte b/src/routes/(main)/category/[[id]]/+page.svelte index 9cbc41c..9b3e195 100644 --- a/src/routes/(main)/category/[[id]]/+page.svelte +++ b/src/routes/(main)/category/[[id]]/+page.svelte @@ -58,7 +58,7 @@ goto(`/file/${id}`)} + onFileClick={({ id }) => goto(`/file/${id}?from=category`)} onFileRemoveClick={async ({ id }) => { await requestFileRemovalFromCategory(id, data.id as number); info = getCategoryInfo(data.id, $masterKeyStore?.get(1)?.key!); // TODO: FIXME From 576d41da7f8d21fdbc30dcf30b4683ace16a4c66 Mon Sep 17 00:00:00 2001 From: static Date: Sat, 27 Dec 2025 03:04:09 +0900 Subject: [PATCH 22/23] =?UTF-8?q?=EB=94=94=EB=A0=89=ED=84=B0=EB=A6=AC=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=EC=97=90=20=EC=83=81=EC=9C=84=20?= =?UTF-8?q?=EB=94=94=EB=A0=89=ED=84=B0=EB=A6=AC=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=20=EB=B2=84=ED=8A=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../labels/DirectoryEntryLabel.svelte | 5 ++++- src/lib/modules/filesystem.ts | 11 +++++++++- .../(fullscreen)/file/[id]/TopBarMenu.svelte | 5 +++-- .../(main)/directory/[[id]]/+page.svelte | 15 ++++++++++++-- .../DirectoryEntries/DirectoryEntries.svelte | 20 +++++++++++++++++-- 5 files changed, 48 insertions(+), 8 deletions(-) diff --git a/src/lib/components/molecules/labels/DirectoryEntryLabel.svelte b/src/lib/components/molecules/labels/DirectoryEntryLabel.svelte index 319e0df..e85665d 100644 --- a/src/lib/components/molecules/labels/DirectoryEntryLabel.svelte +++ b/src/lib/components/molecules/labels/DirectoryEntryLabel.svelte @@ -3,6 +3,7 @@ import { IconLabel } from "$lib/components/molecules"; import IconFolder from "~icons/material-symbols/folder"; + import IconDriveFolderUpload from "~icons/material-symbols/drive-folder-upload"; import IconDraft from "~icons/material-symbols/draft"; interface Props { @@ -11,7 +12,7 @@ subtext?: string; textClass?: ClassValue; thumbnail?: string; - type: "directory" | "file"; + type: "directory" | "parent-directory" | "file"; } let { @@ -30,6 +31,8 @@ {name} {:else if type === "directory"} + {:else if type === "parent-directory"} + {:else} {/if} diff --git a/src/lib/modules/filesystem.ts b/src/lib/modules/filesystem.ts index 6009b60..9f447bf 100644 --- a/src/lib/modules/filesystem.ts +++ b/src/lib/modules/filesystem.ts @@ -23,6 +23,7 @@ import { trpc } from "$trpc/client"; export type DirectoryInfo = | { id: "root"; + parentId?: undefined; dataKey?: undefined; dataKeyVersion?: undefined; name?: undefined; @@ -31,6 +32,7 @@ export type DirectoryInfo = } | { id: number; + parentId: DirectoryId; dataKey?: CryptoKey; dataKeyVersion?: Date; name: string; @@ -93,7 +95,13 @@ const fetchDirectoryInfoFromIndexedDB = async ( info.set({ id, subDirectoryIds, fileIds }); } else { if (!directory) return; - info.set({ id, name: directory.name, subDirectoryIds, fileIds }); + info.set({ + id, + parentId: directory.parentId, + name: directory.name, + subDirectoryIds, + fileIds, + }); } }; @@ -124,6 +132,7 @@ const fetchDirectoryInfoFromServer = async ( info.set({ id, + parentId: metadata!.parent, dataKey, dataKeyVersion: new Date(metadata!.dekVersion), name, diff --git a/src/routes/(fullscreen)/file/[id]/TopBarMenu.svelte b/src/routes/(fullscreen)/file/[id]/TopBarMenu.svelte index 8c92986..a037b61 100644 --- a/src/routes/(fullscreen)/file/[id]/TopBarMenu.svelte +++ b/src/routes/(fullscreen)/file/[id]/TopBarMenu.svelte @@ -44,12 +44,13 @@ {#if directoryId} {@render menuButton(IconFolderOpen, "폴더에서 보기", () => - goto(directoryId === "root" ? "/directory" : `/directory/${directoryId}`), + goto( + directoryId === "root" ? "/directory?from=file" : `/directory/${directoryId}?from=file`, + ), )} {/if} {#if fileBlob} {@render menuButton(IconCloudDownload, "다운로드", () => { - console.log(filename); FileSaver.saveAs(fileBlob, filename); })} {/if} diff --git a/src/routes/(main)/directory/[[id]]/+page.svelte b/src/routes/(main)/directory/[[id]]/+page.svelte index 98572b3..a4edf30 100644 --- a/src/routes/(main)/directory/[[id]]/+page.svelte +++ b/src/routes/(main)/directory/[[id]]/+page.svelte @@ -2,6 +2,7 @@ import { onMount } from "svelte"; import type { Writable } from "svelte/store"; import { goto } from "$app/navigation"; + import { page } from "$app/state"; import { FloatingButton } from "$lib/components/atoms"; import { TopBar } from "$lib/components/molecules"; import { getDirectoryInfo, type DirectoryInfo } from "$lib/modules/filesystem"; @@ -42,6 +43,9 @@ let isEntryRenameModalOpen = $state(false); let isEntryDeleteModalOpen = $state(false); + let isFromFilePage = $derived(page.url.searchParams.get("from") === "file"); + let showTopBar = $derived(data.id !== "root" || isFromFilePage); + const uploadFile = () => { const files = fileInput?.files; if (!files || files.length === 0) return; @@ -86,11 +90,11 @@
- {#if data.id !== "root"} + {#if showTopBar} {/if} {#if $info} -
+
goto("/file/uploads")} /> goto("/file/downloads")} /> @@ -103,6 +107,13 @@ context.selectedEntry = entry; isEntryMenuBottomSheetOpen = true; }} + showParentEntry={isFromFilePage && $info.parentId !== undefined} + onParentClick={() => + goto( + $info.parentId === "root" + ? "/directory?from=file" + : `/directory/${$info.parentId}?from=file`, + )} /> {/key}
diff --git a/src/routes/(main)/directory/[[id]]/DirectoryEntries/DirectoryEntries.svelte b/src/routes/(main)/directory/[[id]]/DirectoryEntries/DirectoryEntries.svelte index 537a7ce..a3e975e 100644 --- a/src/routes/(main)/directory/[[id]]/DirectoryEntries/DirectoryEntries.svelte +++ b/src/routes/(main)/directory/[[id]]/DirectoryEntries/DirectoryEntries.svelte @@ -1,6 +1,8 @@ -{#if subDirectories.length + files.length > 0} +{#if subDirectories.length + files.length > 0 || showParentEntry}
+ {#if showParentEntry} + + + + {/if} {#each subDirectories as { info }} {/each} From 0d13d3baef8fdbf449f3bb5daf997af733246e99 Mon Sep 17 00:00:00 2001 From: static Date: Sat, 27 Dec 2025 14:10:33 +0900 Subject: [PATCH 23/23] =?UTF-8?q?=EC=82=AC=EC=86=8C=ED=95=9C=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{molecules => atoms/buttons}/FileThumbnailButton.svelte | 2 +- src/lib/components/atoms/buttons/index.ts | 1 + src/lib/components/molecules/index.ts | 1 - src/lib/components/organisms/Gallery.svelte | 2 +- src/routes/(fullscreen)/key/export/+page.ts | 2 +- src/routes/(main)/home/+page.svelte | 3 +-- src/trpc/routers/client.ts | 2 +- 7 files changed, 6 insertions(+), 7 deletions(-) rename src/lib/components/{molecules => atoms/buttons}/FileThumbnailButton.svelte (89%) diff --git a/src/lib/components/molecules/FileThumbnailButton.svelte b/src/lib/components/atoms/buttons/FileThumbnailButton.svelte similarity index 89% rename from src/lib/components/molecules/FileThumbnailButton.svelte rename to src/lib/components/atoms/buttons/FileThumbnailButton.svelte index 463419a..c18101c 100644 --- a/src/lib/components/molecules/FileThumbnailButton.svelte +++ b/src/lib/components/atoms/buttons/FileThumbnailButton.svelte @@ -31,7 +31,7 @@ {#if $info}