diff --git a/client/package.json b/client/package.json index 9f07b894..6948ca8d 100644 --- a/client/package.json +++ b/client/package.json @@ -12,25 +12,25 @@ "@fsf/shared": "*", "d3": "^7.9.0", "leaflet": "^1.9.4", - "lucide-react": "^1.22.0", + "lucide-react": "^1.48.0", "portos-ai-toolkit": "^0.8.4", "react": "^19.2.5", "react-dom": "^19.3.0", - "react-hot-toast": "^2.6.0", + "react-hot-toast": "^2.6.1", "react-leaflet": "^5.0.0", - "react-router-dom": "^7.18.2", + "react-router-dom": "^7.18.4", "socket.io-client": "^4.8.3" }, "devDependencies": { - "@tailwindcss/vite": "^4.2.2", + "@tailwindcss/vite": "^4.3.3", "@testing-library/react": "^16.3.2", "@types/d3": "^7.4.3", "@types/leaflet": "^1.9.21", "@types/react": "^19.2.14", "@types/react-dom": "^19.3.0", - "@vitejs/plugin-react": "^6.0.3", + "@vitejs/plugin-react": "^6.1.1", "tailwindcss": "^4.3.1", "typescript": "^7.0.2", - "vite": "^8.0.10" + "vite": "^8.3.5" } } diff --git a/client/vite.config.ts b/client/vite.config.ts index 11eca1f6..1c2c1454 100644 --- a/client/vite.config.ts +++ b/client/vite.config.ts @@ -1,7 +1,7 @@ import type { IncomingMessage, ServerResponse } from 'node:http'; import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; -import { createBearerCheck, resolveAccessConfig } from '../server/src/middleware/accessBoundary'; +import { createBearerCheck, resolveAccessConfig } from '../server/src/middleware/accessBoundary.ts'; import tailwindcss from '@tailwindcss/vite'; // A remotely reachable dev proxy must never expose a token-free local API. diff --git a/package-lock.json b/package-lock.json index e1326052..b2ed2e7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,13 +21,14 @@ "@playwright/test": "^1.61.1", "@types/sql.js": "^1.4.11", "@types/supertest": "^7.2.0", - "@vitest/coverage-v8": "^4.1.9", + "@vitest/coverage-v8": "^5.0.1", "happy-dom": "^20.10.6", "msw": "^2.15.0", "pm2": "^7.0.4", "sql.js": "^1.14.2", "supertest": "^7.2.2", - "vitest": "^4.1.9" + "tsx": "^4.23.15", + "vitest": "^5.0.1" } }, "client": { @@ -37,26 +38,26 @@ "@fsf/shared": "*", "d3": "^7.9.0", "leaflet": "^1.9.4", - "lucide-react": "^1.22.0", + "lucide-react": "^1.48.0", "portos-ai-toolkit": "^0.8.4", "react": "^19.2.5", "react-dom": "^19.3.0", - "react-hot-toast": "^2.6.0", + "react-hot-toast": "^2.6.1", "react-leaflet": "^5.0.0", - "react-router-dom": "^7.18.2", + "react-router-dom": "^7.18.4", "socket.io-client": "^4.8.3" }, "devDependencies": { - "@tailwindcss/vite": "^4.2.2", + "@tailwindcss/vite": "^4.3.3", "@testing-library/react": "^16.3.2", "@types/d3": "^7.4.3", "@types/leaflet": "^1.9.21", "@types/react": "^19.2.14", "@types/react-dom": "^19.3.0", - "@vitejs/plugin-react": "^6.0.3", + "@vitejs/plugin-react": "^6.1.1", "tailwindcss": "^4.3.1", "typescript": "^7.0.2", - "vite": "^8.0.10" + "vite": "^8.3.5" } }, "client/node_modules/@react-leaflet/core": { @@ -129,13 +130,13 @@ } }, "node_modules/@babel/parser": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", - "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "version": "7.29.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", + "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.29.7" + "@babel/types": "^7.29.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -155,9 +156,9 @@ } }, "node_modules/@babel/types": { - "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", - "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", "dev": true, "license": "MIT", "dependencies": { @@ -760,9 +761,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "dev": true, "license": "MIT" }, @@ -860,13 +861,13 @@ "license": "MIT" }, "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.155.0.tgz", + "integrity": "sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==", "dev": true, "license": "MIT", "funding": { - "url": "https://github.com/sponsors/Boshen" + "url": "https://github.com/sponsors/oxc-project" } }, "node_modules/@paralleldrive/cuid2": { @@ -963,10 +964,27 @@ "debug": "^4.3.1" } }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.15.tgz", + "integrity": "sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", - "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.15.tgz", + "integrity": "sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg==", "cpu": [ "arm64" ], @@ -981,9 +999,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", - "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.15.tgz", + "integrity": "sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA==", "cpu": [ "arm64" ], @@ -998,9 +1016,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", - "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.15.tgz", + "integrity": "sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A==", "cpu": [ "x64" ], @@ -1015,9 +1033,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", - "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.15.tgz", + "integrity": "sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ==", "cpu": [ "x64" ], @@ -1032,9 +1050,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", - "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.15.tgz", + "integrity": "sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ==", "cpu": [ "arm" ], @@ -1049,13 +1067,16 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", - "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.15.tgz", + "integrity": "sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1066,13 +1087,16 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", - "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.15.tgz", + "integrity": "sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1083,13 +1107,16 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", - "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.15.tgz", + "integrity": "sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1100,13 +1127,16 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", - "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.15.tgz", + "integrity": "sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1117,13 +1147,16 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", - "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.15.tgz", + "integrity": "sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1134,13 +1167,16 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", - "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.15.tgz", + "integrity": "sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1151,9 +1187,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", - "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.15.tgz", + "integrity": "sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w==", "cpu": [ "arm64" ], @@ -1167,29 +1203,10 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", - "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", - "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.15.tgz", + "integrity": "sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg==", "cpu": [ "arm64" ], @@ -1204,9 +1221,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", - "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.15.tgz", + "integrity": "sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ==", "cpu": [ "x64" ], @@ -1233,57 +1250,50 @@ "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", "license": "MIT" }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "dev": true, - "license": "MIT" - }, "node_modules/@tailwindcss/node": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.1.tgz", - "integrity": "sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", + "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "5.21.6", + "enhanced-resolve": "^5.24.1", "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.3.1" + "tailwindcss": "4.3.3" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.1.tgz", - "integrity": "sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", + "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", "dev": true, "license": "MIT", "engines": { "node": ">= 20" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.3.1", - "@tailwindcss/oxide-darwin-arm64": "4.3.1", - "@tailwindcss/oxide-darwin-x64": "4.3.1", - "@tailwindcss/oxide-freebsd-x64": "4.3.1", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.1", - "@tailwindcss/oxide-linux-arm64-gnu": "4.3.1", - "@tailwindcss/oxide-linux-arm64-musl": "4.3.1", - "@tailwindcss/oxide-linux-x64-gnu": "4.3.1", - "@tailwindcss/oxide-linux-x64-musl": "4.3.1", - "@tailwindcss/oxide-wasm32-wasi": "4.3.1", - "@tailwindcss/oxide-win32-arm64-msvc": "4.3.1", - "@tailwindcss/oxide-win32-x64-msvc": "4.3.1" + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.1.tgz", - "integrity": "sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", + "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", "cpu": [ "arm64" ], @@ -1298,9 +1308,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.1.tgz", - "integrity": "sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", + "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", "cpu": [ "arm64" ], @@ -1315,9 +1325,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.1.tgz", - "integrity": "sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", + "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", "cpu": [ "x64" ], @@ -1332,9 +1342,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.1.tgz", - "integrity": "sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", + "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", "cpu": [ "x64" ], @@ -1349,9 +1359,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.1.tgz", - "integrity": "sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", + "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", "cpu": [ "arm" ], @@ -1366,13 +1376,16 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.1.tgz", - "integrity": "sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", + "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1383,13 +1396,16 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.1.tgz", - "integrity": "sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", + "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1400,13 +1416,16 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.1.tgz", - "integrity": "sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", + "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1417,13 +1436,16 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.1.tgz", - "integrity": "sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", + "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1434,9 +1456,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.1.tgz", - "integrity": "sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", + "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -1452,9 +1474,9 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.10.0", - "@emnapi/runtime": "^1.10.0", - "@emnapi/wasi-threads": "^1.2.1", + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.2", "tslib": "^2.8.1" @@ -1464,9 +1486,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.1.tgz", - "integrity": "sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", + "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", "cpu": [ "arm64" ], @@ -1481,9 +1503,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.1.tgz", - "integrity": "sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", + "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", "cpu": [ "x64" ], @@ -1498,15 +1520,15 @@ } }, "node_modules/@tailwindcss/vite": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.1.tgz", - "integrity": "sha512-hItDHuIIlEV61R+faXu66s1K36aTurO/Qw0e45Vskz57gXl9pWOT6eg3zmcEui6CZXddbN7zd41bwmvag4JGwQ==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz", + "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", "dev": true, "license": "MIT", "dependencies": { - "@tailwindcss/node": "4.3.1", - "@tailwindcss/oxide": "4.3.1", - "tailwindcss": "4.3.1" + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "tailwindcss": "4.3.3" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" @@ -1954,9 +1976,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.7.tgz", - "integrity": "sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.3.tgz", + "integrity": "sha512-dPfW8NFiOF4wOHc7+N/QSxlY9cfSsenewGbAz8C8U/MULPd/YZ27LvJUIlzaXie7e6Ove9YunJGgC9tbHD2cKw==", "dev": true, "license": "MIT", "dependencies": { @@ -2037,7 +2059,7 @@ "version": "19.3.0", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz", "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -2047,7 +2069,7 @@ "version": "19.3.0", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.3.0.tgz", "integrity": "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==", - "dev": true, + "devOptional": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.3.0" @@ -2484,9 +2506,9 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.3.tgz", - "integrity": "sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.1.2.tgz", + "integrity": "sha512-fhJNGUdscQa4HkI0xFxwonWyhnVReCPDbXI7FH7jmzpnVCrFQI7PRrhWecO263610vOrs9tr2Y4xcf8uAdFxbw==", "dev": true, "license": "MIT", "dependencies": { @@ -2498,6 +2520,7 @@ "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", + "oxc-transform-react": "^0.152.0", "vite": "^8.0.0" }, "peerDependenciesMeta": { @@ -2506,33 +2529,34 @@ }, "babel-plugin-react-compiler": { "optional": true + }, + "oxc-transform-react": { + "optional": true } } }, "node_modules/@vitest/coverage-v8": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.9.tgz", - "integrity": "sha512-G9/lgqibheLVBDRuya45EbsEXTYcWoSG+TLg7i2axuzx0Eq62eXn+aWXyaVdV5vKvFSWd6ywcX8hA7la9Pvu8g==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-5.0.5.tgz", + "integrity": "sha512-4KpkGdIrSqBihx/JyP/e+S+IKB98etg1XDQiLAxksLfcShnwi1VReGeOT9NI43hLHbknosKp/1CCLn9Zgcs8mg==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^1.0.2", - "@vitest/utils": "4.1.9", - "ast-v8-to-istanbul": "^1.0.0", - "istanbul-lib-coverage": "^3.2.2", - "istanbul-lib-report": "^3.0.1", - "istanbul-reports": "^3.2.0", - "magicast": "^0.5.2", - "obug": "^2.1.1", - "std-env": "^4.0.0-rc.1", - "tinyrainbow": "^3.1.0" + "@vitest/istanbul-lib-coverage": "^1.0.2", + "@vitest/istanbul-lib-report": "^1.0.2", + "ast-v8-to-istanbul": "^1.0.7", + "magicast": "^0.5.5", + "obug": "^3.0.0", + "std-env": "^4.3.0", + "tinyrainbow": "^3.2.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "@vitest/browser": "4.1.9", - "vitest": "4.1.9" + "@vitest/browser": "5.0.5", + "vitest": "5.0.5" }, "peerDependenciesMeta": { "@vitest/browser": { @@ -2540,40 +2564,46 @@ } } }, - "node_modules/@vitest/expect": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", - "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", + "node_modules/@vitest/istanbul-lib-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@vitest/istanbul-lib-coverage/-/istanbul-lib-coverage-1.0.2.tgz", + "integrity": "sha512-9J/JMwOf9AoJhAywhrn7ScKTL38hsWQP/qPG60OtaAFcQ5OXPwKsxZFlbnuCKmZ61m8/lGgHYnFpdyQZUvG/iA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + } + }, + "node_modules/@vitest/istanbul-lib-report": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@vitest/istanbul-lib-report/-/istanbul-lib-report-1.0.2.tgz", + "integrity": "sha512-gUsfXZJbzPamoIY5TvHFiMMoXESBrUMo+xqaj+rYrWI69+EnvRlYBlP96ZnHPY4vX8kyUpgAnFUCg5wZG/HkDQ==", "dev": true, "license": "MIT", "dependencies": { - "@standard-schema/spec": "^1.1.0", - "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", - "chai": "^6.2.2", - "tinyrainbow": "^3.1.0" + "@vitest/istanbul-lib-coverage": "1.0.2" }, - "funding": { - "url": "https://opencollective.com/vitest" + "engines": { + "node": ">=22" } }, "node_modules/@vitest/mocker": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", - "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.5.tgz", + "integrity": "sha512-xLL3lkDegJZw+MGoGaCCsFayDZthGgpAtr3Vziyfc4p7xH89K7KRPcRBBh/kEYm0Aqkt5DtTmkYpBtRV/FrHZg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.9", + "@jridgewell/trace-mapping": "0.3.31", + "@vitest/spy": "5.0.5", "estree-walker": "^3.0.3", - "magic-string": "^0.30.21" + "magic-string": "^1.4.3" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "msw": "^2.4.9", + "msw": "^2.4.9 || ^3.0.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { @@ -2585,74 +2615,26 @@ } } }, - "node_modules/@vitest/pretty-format": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", - "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/runner": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", - "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/utils": "4.1.9", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/snapshot": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", - "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", + "node_modules/@vitest/mocker/node_modules/magic-string": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz", + "integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.9", - "@vitest/utils": "4.1.9", - "magic-string": "^0.30.21", - "pathe": "^2.0.3" - }, - "funding": { - "url": "https://opencollective.com/vitest" + "@jridgewell/sourcemap-codec": "^1.6.0" } }, "node_modules/@vitest/spy": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", - "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.5.tgz", + "integrity": "sha512-lwJRExuH417257Q5wv9QYugN9IMJkoxcrKrYxGPQkBzl7wlNpTUxD/5yNX4ESKVUIShKPgkGchYPFMWOOk0wzw==", "dev": true, "license": "MIT", "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/utils": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", - "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "4.1.9", - "convert-source-map": "^2.0.0", - "tinyrainbow": "^3.1.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", @@ -2815,9 +2797,9 @@ } }, "node_modules/ast-v8-to-istanbul": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.4.tgz", - "integrity": "sha512-0bC0/4bTSrnwdhU3IsZDwEdojvuPrSg59OYZfKsLRtJZ0u8VBx9DebfqqG8bRdCC0I7vjgxmPi41P0lpkhJHtA==", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.7.tgz", + "integrity": "sha512-kFL68AG6ajd8fg248zwM9GQrUWEp79gsmjum34OEXjs4yHuUMZfYKwOLW9GMmB4oNvVrj+EAGxsP7ye2UR9UlA==", "dev": true, "license": "MIT", "dependencies": { @@ -2981,9 +2963,9 @@ } }, "node_modules/chai": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", - "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.3.0.tgz", + "integrity": "sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==", "dev": true, "license": "MIT", "engines": { @@ -3178,13 +3160,6 @@ "node": ">= 0.6" } }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, "node_modules/cookie": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", @@ -3841,9 +3816,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.21.6", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz", - "integrity": "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==", + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.26.0.tgz", + "integrity": "sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==", "dev": true, "license": "MIT", "dependencies": { @@ -3886,9 +3861,9 @@ } }, "node_modules/es-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", - "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.2.tgz", + "integrity": "sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==", "dev": true, "license": "MIT" }, @@ -4059,9 +4034,9 @@ "license": "MIT" }, "node_modules/expect-type": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -4531,13 +4506,6 @@ "set-cookie-parser": "^3.0.1" } }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -4602,9 +4570,9 @@ } }, "node_modules/ip-address": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.4.0.tgz", - "integrity": "sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==", + "version": "10.7.4", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.4.tgz", + "integrity": "sha512-DWBMKyeM25D70nrIiTkgQth59/G+naKOTep6huF9j+GmeK3DMD60Zvxg5d5qQnz+38AHgou6ean2LECX+lrKDg==", "dev": true, "license": "MIT", "engines": { @@ -4689,58 +4657,6 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-reports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", - "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jiti": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", @@ -5073,12 +4989,18 @@ } }, "node_modules/lucide-react": { - "version": "1.22.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.22.0.tgz", - "integrity": "sha512-c9o3l0PiNcgOQDW4F31BEYHudE7kgxVt3o30qMl36ZPwTxXlGB4QnLilhERvVM4uh/pl5MDyY1/gzZSYcHDtBg==", + "version": "1.61.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.61.0.tgz", + "integrity": "sha512-v2SQPPPNjnr+BlhL9Spuzmo5yR+hLkftfuE4E3jaxdNPgSH0hJKPcLJBP7yAta87jQNPsSERS59PJjCJ/CorBA==", "license": "ISC", "peerDependencies": { + "@types/react": "*", "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, "node_modules/lz-string": { @@ -5103,33 +5025,17 @@ } }, "node_modules/magicast": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.3.tgz", - "integrity": "sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.5.tgz", + "integrity": "sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.3", - "@babel/types": "^7.29.0", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", "source-map-js": "^1.2.1" } }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -5391,9 +5297,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.16", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", - "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", "dev": true, "funding": [ { @@ -5460,9 +5366,9 @@ } }, "node_modules/obug": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", - "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/obug/-/obug-3.0.0.tgz", + "integrity": "sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==", "dev": true, "funding": [ "https://github.com/sponsors/sxzz", @@ -5551,13 +5457,6 @@ "dev": true, "license": "MIT" }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "dev": true, - "license": "MIT" - }, "node_modules/pg": { "version": "8.23.0", "resolved": "https://registry.npmjs.org/pg/-/pg-8.23.0.tgz", @@ -5825,9 +5724,9 @@ } }, "node_modules/postcss": { - "version": "8.5.25", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.25.tgz", - "integrity": "sha512-DTPx3RWSSnWyzLxQnlH0rJP+EW5ekl16ZU4/psbIhA0e53kJfdgaN5vKM+xP7yJtXVu+nfdVFmlgFDEKAe4Pyw==", + "version": "8.5.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz", + "integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==", "dev": true, "funding": [ { @@ -5845,9 +5744,9 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.16", + "nanoid": "^3.3.20", "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "source-map-js": "^1.2.2" }, "engines": { "node": "^10 || ^12 || >=14" @@ -6055,9 +5954,9 @@ } }, "node_modules/react-hot-toast": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.6.0.tgz", - "integrity": "sha512-bH+2EBMZ4sdyou/DPrfgIouFpcRLCJ+HoCA32UoAYHn6T3Ur5yfcDCeSr5mwldl6pFOsiocmrXMuoCJ1vV8bWg==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.6.1.tgz", + "integrity": "sha512-gjFsf05dxcku0FkyMtFH+XmYFdA2odXfBVknEr/IZ+XpUjwsgvtY2616sOpUynK+l9ykQDRNpaJ1hYfuximUiA==", "license": "MIT", "dependencies": { "csstype": "^3.1.3", @@ -6067,8 +5966,18 @@ "node": ">=10" }, "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", "react": ">=16", "react-dom": ">=16" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } } }, "node_modules/react-is": { @@ -6080,9 +5989,9 @@ "peer": true }, "node_modules/react-router": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.2.tgz", - "integrity": "sha512-aUVMjFm3GAPTTZL7oYr5E7ETiqfQCHRLH+B+5afnICvf0r7kkK4eR6SMuwbSTJw/7t+12khT/Kahij49fqOCIg==", + "version": "7.18.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.4.tgz", + "integrity": "sha512-PUPQcMhMGRAslLcvtlPz/kmzBEWPhLdgLFrL7pLNepBL6dX0lWj4WD2cUYVgYCuT3jxvghYFg81cDTj44DhetQ==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -6102,12 +6011,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.18.2", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.2.tgz", - "integrity": "sha512-AIKJ/jgGlFb3EbfCXk5Gzshiwt+l3mqbCrNjmEWMMjqQxNJ3svBa6bgzFyCC2Sw3RA0VWF1kg3uQf2OFhxb8hw==", + "version": "7.18.4", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.4.tgz", + "integrity": "sha512-yrfmJHIpDG7taCpqKjT1G5B6q3O2K+RN8/fgNf0lTjCwiPbQ0ei6vXX9ZjQR+7ld8Tr7Z5xmyMnZ8YJrphWQUw==", "license": "MIT", "dependencies": { - "react-router": "7.18.2" + "react-router": "7.18.4" }, "engines": { "node": ">=20.0.0" @@ -6160,13 +6069,13 @@ "license": "Unlicense" }, "node_modules/rolldown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", - "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.15.tgz", + "integrity": "sha512-6odND+fel5zVw3I4Yc2e69gvguDnX0CAAGiNDZSq4ik5WUcaLrkD2GLuog0XcODbor31hZ5ayEMgVDno+KPV5g==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.133.0", + "@oxc-project/types": "=0.155.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -6176,21 +6085,21 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.3", - "@rolldown/binding-darwin-arm64": "1.0.3", - "@rolldown/binding-darwin-x64": "1.0.3", - "@rolldown/binding-freebsd-x64": "1.0.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", - "@rolldown/binding-linux-arm64-gnu": "1.0.3", - "@rolldown/binding-linux-arm64-musl": "1.0.3", - "@rolldown/binding-linux-ppc64-gnu": "1.0.3", - "@rolldown/binding-linux-s390x-gnu": "1.0.3", - "@rolldown/binding-linux-x64-gnu": "1.0.3", - "@rolldown/binding-linux-x64-musl": "1.0.3", - "@rolldown/binding-openharmony-arm64": "1.0.3", - "@rolldown/binding-wasm32-wasi": "1.0.3", - "@rolldown/binding-win32-arm64-msvc": "1.0.3", - "@rolldown/binding-win32-x64-msvc": "1.0.3" + "@rolldown/binding-android-arm-eabi": "1.2.15", + "@rolldown/binding-android-arm64": "1.2.15", + "@rolldown/binding-darwin-arm64": "1.2.15", + "@rolldown/binding-darwin-x64": "1.2.15", + "@rolldown/binding-freebsd-x64": "1.2.15", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.15", + "@rolldown/binding-linux-arm64-gnu": "1.2.15", + "@rolldown/binding-linux-arm64-musl": "1.2.15", + "@rolldown/binding-linux-ppc64-gnu": "1.2.15", + "@rolldown/binding-linux-s390x-gnu": "1.2.15", + "@rolldown/binding-linux-x64-gnu": "1.2.15", + "@rolldown/binding-linux-x64-musl": "1.2.15", + "@rolldown/binding-openharmony-arm64": "1.2.15", + "@rolldown/binding-win32-arm64-msvc": "1.2.15", + "@rolldown/binding-win32-x64-msvc": "1.2.15" } }, "node_modules/router": { @@ -6279,19 +6188,6 @@ "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", "license": "MIT" }, - "node_modules/semver": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", - "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/send": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", @@ -6447,13 +6343,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -6548,9 +6437,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -6573,13 +6462,6 @@ "dev": true, "license": "MIT" }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -6590,9 +6472,9 @@ } }, "node_modules/std-env": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", - "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.3.0.tgz", + "integrity": "sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==", "dev": true, "license": "MIT" }, @@ -6685,9 +6567,9 @@ } }, "node_modules/tailwindcss": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.1.tgz", - "integrity": "sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", + "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", "dev": true, "license": "MIT" }, @@ -6706,16 +6588,19 @@ } }, "node_modules/tinybench": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.2.tgz", + "integrity": "sha512-nIZ2N2dFpjBsg2hgitKu7mA+FvsIJuGJOwysRngmVRRmWgR/UyPjwJj8c/fT/dhm8EkHnF5EOogRf+0Z2qrimA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } }, "node_modules/tinyexec": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", - "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", "dev": true, "license": "MIT", "engines": { @@ -6771,9 +6656,9 @@ } }, "node_modules/tinyrainbow": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", - "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.2.0.tgz", + "integrity": "sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==", "dev": true, "license": "MIT", "engines": { @@ -6843,9 +6728,9 @@ "license": "0BSD" }, "node_modules/tsx": { - "version": "4.22.4", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz", - "integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==", + "version": "4.23.15", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz", + "integrity": "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==", "license": "MIT", "dependencies": { "esbuild": "~0.28.0" @@ -7055,16 +6940,16 @@ } }, "node_modules/vite": { - "version": "8.0.16", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", - "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.5.tgz", + "integrity": "sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ==", "dev": true, "license": "MIT", "dependencies": { - "lightningcss": "^1.32.0", - "picomatch": "^4.0.4", - "postcss": "^8.5.15", - "rolldown": "1.0.3", + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.30", + "rolldown": "~1.2.14", "tinyglobby": "^0.2.17" }, "bin": { @@ -7081,7 +6966,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.18", + "@vitejs/devtools": "^0.7.1 || ^0.8.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -7147,10 +7032,283 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/vite/node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/vite/node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { @@ -7161,38 +7319,31 @@ } }, "node_modules/vitest": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", - "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.5.tgz", + "integrity": "sha512-cPODOMF8YnkiiYtDdHo6n4wWmBrOBBbOIYH2d33M4l3CdNn72h+qMmO6uXffUN6A5AZFXNND/d+krS9mgkXRfg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.9", - "@vitest/mocker": "4.1.9", - "@vitest/pretty-format": "4.1.9", - "@vitest/runner": "4.1.9", - "@vitest/snapshot": "4.1.9", - "@vitest/spy": "4.1.9", - "@vitest/utils": "4.1.9", - "es-module-lexer": "^2.0.0", - "expect-type": "^1.3.0", - "magic-string": "^0.30.21", - "obug": "^2.1.1", - "pathe": "^2.0.3", - "picomatch": "^4.0.3", - "std-env": "^4.0.0-rc.1", - "tinybench": "^2.9.0", - "tinyexec": "^1.0.2", - "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.1.0", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", - "why-is-node-running": "^2.3.0" + "@types/chai": "^5.2.2", + "@vitest/mocker": "5.0.5", + "chai": "^6.3.0", + "es-module-lexer": "^2.3.2", + "expect-type": "^1.4.0", + "magic-string": "^1.4.3", + "obug": "^3.0.0", + "picomatch": "^4.0.7", + "std-env": "^4.3.0", + "tinybench": "^6.2.2", + "tinyexec": "^1.3.1", + "tinyglobby": "^0.2.17", + "why-is-node-running": "3.2.1" }, "bin": { "vitest": "vitest.mjs" }, "engines": { - "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + "node": "^22.12.0 || ^24.0.0 || >=26.0.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -7200,16 +7351,16 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", - "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.9", - "@vitest/browser-preview": "4.1.9", - "@vitest/browser-webdriverio": "4.1.9", - "@vitest/coverage-istanbul": "4.1.9", - "@vitest/coverage-v8": "4.1.9", - "@vitest/ui": "4.1.9", + "@types/node": "^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "5.0.5", + "@vitest/browser-preview": "5.0.5", + "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", + "@vitest/coverage-istanbul": "5.0.5", + "@vitest/coverage-v8": "5.0.5", + "@vitest/ui": "5.0.5", "happy-dom": "*", "jsdom": "*", - "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { @@ -7250,10 +7401,20 @@ } } }, + "node_modules/vitest/node_modules/magic-string": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz", + "integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.6.0" + } + }, "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { @@ -7274,20 +7435,16 @@ } }, "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-3.2.1.tgz", + "integrity": "sha512-Tb2FUhB4vUsGQlfSquQLYkApkuPAFQXGFzxWKHHumVz2dK+X1RUm/HnID4+TfIGYJ1kTcwOaCk/buYCEJr6YjQ==", "dev": true, "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, "bin": { "why-is-node-running": "cli.js" }, "engines": { - "node": ">=8" + "node": ">=20.11" } }, "node_modules/wrap-ansi": { @@ -7421,14 +7578,14 @@ "pg": "^8.23.0", "playwright": "^1.61.1", "portos-ai-toolkit": "^0.8.4", - "tsx": "^4.19.2", + "tsx": "^4.23.15", "ulid": "^3.0.2", "zod": "^4.6.5" }, "devDependencies": { "@types/cors": "^2.8.17", "@types/express": "^5.0.0", - "@types/express-serve-static-core": "~5.0.7", + "@types/express-serve-static-core": "~5.1.3", "@types/node": "^26.0.1", "@types/pg": "^8.23.1", "typescript": "^7.0.2" diff --git a/package.json b/package.json index f35eef3e..9658e262 100644 --- a/package.json +++ b/package.json @@ -55,12 +55,13 @@ "@playwright/test": "^1.61.1", "@types/sql.js": "^1.4.11", "@types/supertest": "^7.2.0", - "@vitest/coverage-v8": "^4.1.9", + "@vitest/coverage-v8": "^5.0.1", "happy-dom": "^20.10.6", "msw": "^2.15.0", "pm2": "^7.0.4", "sql.js": "^1.14.2", "supertest": "^7.2.2", - "vitest": "^4.1.9" + "tsx": "^4.23.15", + "vitest": "^5.0.1" } } diff --git a/server/package.json b/server/package.json index 6f16acec..5bda934a 100644 --- a/server/package.json +++ b/server/package.json @@ -15,14 +15,14 @@ "pg": "^8.23.0", "playwright": "^1.61.1", "portos-ai-toolkit": "^0.8.4", - "tsx": "^4.19.2", + "tsx": "^4.23.15", "ulid": "^3.0.2", "zod": "^4.6.5" }, "devDependencies": { "@types/cors": "^2.8.17", "@types/express": "^5.0.0", - "@types/express-serve-static-core": "~5.0.7", + "@types/express-serve-static-core": "~5.1.3", "@types/node": "^26.0.1", "@types/pg": "^8.23.1", "typescript": "^7.0.2" diff --git a/server/src/routes/ai-discovery.routes.ts b/server/src/routes/ai-discovery.routes.ts index 3066a67a..ee856cf2 100644 --- a/server/src/routes/ai-discovery.routes.ts +++ b/server/src/routes/ai-discovery.routes.ts @@ -34,7 +34,7 @@ export const createAiDiscoveryRouter = (services: AiDiscoveryRouterServices = {} * Start a quick AI discovery to find interesting ancestors * POST /api/ai-discovery/:dbId/quick */ - router.post('/:dbId/quick', asyncHandler(async (req: Request, res: Response) => { + router.post('/:dbId/quick', asyncHandler(async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const { sampleSize, excludeBiblical, minBirthYear, maxGenerations, customPrompt } = req.body; @@ -62,7 +62,7 @@ export const createAiDiscoveryRouter = (services: AiDiscoveryRouterServices = {} * Start a full AI discovery run (async) * POST /api/ai-discovery/:dbId/start */ - router.post('/:dbId/start', asyncHandler(async (req: Request, res: Response) => { + router.post('/:dbId/start', asyncHandler(async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; let options; try { @@ -93,7 +93,7 @@ export const createAiDiscoveryRouter = (services: AiDiscoveryRouterServices = {} * Cancel the active full AI discovery run for a database. * POST /api/ai-discovery/:dbId/cancel */ - router.post('/:dbId/cancel', (req: Request, res: Response) => { + router.post('/:dbId/cancel', (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const cancelled = aiDiscoveryService.cancelDiscovery(dbId); @@ -109,7 +109,7 @@ export const createAiDiscoveryRouter = (services: AiDiscoveryRouterServices = {} * Get progress of a discovery run * GET /api/ai-discovery/progress/:runId */ - router.get('/progress/:runId', (req: Request, res: Response) => { + router.get('/progress/:runId', (req: Request<{ runId: string }>, res: Response) => { const { runId } = req.params; const progress = aiDiscoveryService.getProgress(runId); @@ -125,7 +125,7 @@ export const createAiDiscoveryRouter = (services: AiDiscoveryRouterServices = {} * Apply a candidate as a favorite * POST /api/ai-discovery/:dbId/apply */ - router.post('/:dbId/apply', async (req: Request, res: Response) => { + router.post('/:dbId/apply', async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const { personId, whyInteresting, tags } = req.body; @@ -148,7 +148,7 @@ export const createAiDiscoveryRouter = (services: AiDiscoveryRouterServices = {} * Apply multiple candidates as favorites * POST /api/ai-discovery/:dbId/apply-batch */ - router.post('/:dbId/apply-batch', async (req: Request, res: Response) => { + router.post('/:dbId/apply-batch', async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const { candidates } = req.body; if (!validateCandidatesBatch(candidates, res)) return; @@ -176,7 +176,7 @@ export const createAiDiscoveryRouter = (services: AiDiscoveryRouterServices = {} * Dismiss a candidate (mark as not interesting) * POST /api/ai-discovery/:dbId/dismiss */ - router.post('/:dbId/dismiss', async (req: Request, res: Response) => { + router.post('/:dbId/dismiss', async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const { personId, whyInteresting, suggestedTags } = req.body; @@ -199,7 +199,7 @@ export const createAiDiscoveryRouter = (services: AiDiscoveryRouterServices = {} * Dismiss multiple candidates at once * POST /api/ai-discovery/:dbId/dismiss-batch */ - router.post('/:dbId/dismiss-batch', async (req: Request, res: Response) => { + router.post('/:dbId/dismiss-batch', async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const { candidates } = req.body; if (!validateCandidatesBatch(candidates, res)) return; @@ -212,7 +212,7 @@ export const createAiDiscoveryRouter = (services: AiDiscoveryRouterServices = {} * Get dismissed candidates for a database * GET /api/ai-discovery/:dbId/dismissed */ - router.get('/:dbId/dismissed', async (req: Request, res: Response) => { + router.get('/:dbId/dismissed', async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const dismissed = await aiDiscoveryService.getDismissedCandidates(dbId); const count = await aiDiscoveryService.getDismissedCount(dbId); @@ -223,7 +223,7 @@ export const createAiDiscoveryRouter = (services: AiDiscoveryRouterServices = {} * Undo dismiss (restore a candidate) * DELETE /api/ai-discovery/:dbId/dismissed/:personId */ - router.delete('/:dbId/dismissed/:personId', async (req: Request, res: Response) => { + router.delete('/:dbId/dismissed/:personId', async (req: Request<{ dbId: string; personId: string }>, res: Response) => { const { dbId, personId } = req.params; const result = await aiDiscoveryService.undoDismiss(dbId, personId); res.json({ success: true, data: result }); @@ -233,7 +233,7 @@ export const createAiDiscoveryRouter = (services: AiDiscoveryRouterServices = {} * Clear all dismissed candidates for a database * DELETE /api/ai-discovery/:dbId/dismissed */ - router.delete('/:dbId/dismissed', async (req: Request, res: Response) => { + router.delete('/:dbId/dismissed', async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const result = await aiDiscoveryService.clearDismissed(dbId); res.json({ success: true, data: result }); @@ -265,7 +265,7 @@ export const createAiDiscoveryRouter = (services: AiDiscoveryRouterServices = {} * Get specific run output for debugging * GET /api/ai-discovery/debug/runs/:runId */ - router.get('/debug/runs/:runId', async (req: Request, res: Response) => { + router.get('/debug/runs/:runId', async (req: Request<{ runId: string }>, res: Response) => { if (!debugEnabled) { res.status(403).json({ success: false, error: 'Debug endpoints disabled. Set ENABLE_AI_DEBUG=1 to enable.' }); return; diff --git a/server/src/routes/ancestry-hints.routes.ts b/server/src/routes/ancestry-hints.routes.ts index bac967a8..8809ed0a 100644 --- a/server/src/routes/ancestry-hints.routes.ts +++ b/server/src/routes/ancestry-hints.routes.ts @@ -16,7 +16,7 @@ const router = Router(); * POST /:dbId/:personId - Process free hints for a single person * Returns the result with counts of hints processed */ -router.post('/:dbId/:personId', asyncHandler(async (req: Request, res: Response) => { +router.post('/:dbId/:personId', asyncHandler(async (req: Request<{ dbId: string; personId: string }>, res: Response) => { const { personId } = req.params; logger.start('ancestry-hints', `Processing hints for person ${personId}`); @@ -45,7 +45,7 @@ router.post('/:dbId/:personId', asyncHandler(async (req: Request, res: Response) * GET /:dbId/:personId/events - SSE stream for hints processing progress * Streams real-time progress events during hint processing */ -router.get('/:dbId/:personId/events', asyncHandler(async (req: Request, res: Response) => { +router.get('/:dbId/:personId/events', asyncHandler(async (req: Request<{ dbId: string; personId: string }>, res: Response) => { const { personId } = req.params; const sendEvent = initSSEData(res); diff --git a/server/src/routes/ancestry-update.routes.ts b/server/src/routes/ancestry-update.routes.ts index c1dd5a7a..3c7fe75e 100644 --- a/server/src/routes/ancestry-update.routes.ts +++ b/server/src/routes/ancestry-update.routes.ts @@ -31,7 +31,7 @@ router.get('/status', (_req: Request, res: Response) => { * - maxGenerations: Optional. 1, 2, 3, 4, or 'full'. Default: 4 * - testMode: Optional. If 'true', runs in dry-run mode */ -router.get('/:dbId/events', async (req: Request, res: Response) => { +router.get('/:dbId/events', async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const { rootPersonId, maxGenerations, testMode } = req.query; @@ -109,7 +109,7 @@ router.post('/:dbId/cancel', (_req: Request, res: Response) => { /** * GET /:dbId/validate/:personId - Validate that a person can be used as root */ -router.get('/:dbId/validate/:personId', async (req: Request, res: Response) => { +router.get('/:dbId/validate/:personId', async (req: Request<{ dbId: string; personId: string }>, res: Response) => { const { dbId, personId } = req.params; // Resolve to canonical ID diff --git a/server/src/routes/auditor.routes.ts b/server/src/routes/auditor.routes.ts index 33050a13..dedc94ae 100644 --- a/server/src/routes/auditor.routes.ts +++ b/server/src/routes/auditor.routes.ts @@ -21,7 +21,7 @@ export function createAuditorRouter(auditorService = defaultAuditorService) { * POST /:dbId/start - Start a new audit run * Body: Partial */ - router.post('/:dbId/start', async (req: Request, res: Response, next) => { + router.post('/:dbId/start', async (req: Request<{ dbId: string }>, res: Response, next) => { const { dbId } = req.params; const inputConfig = req.body as Partial | undefined; @@ -48,7 +48,7 @@ export function createAuditorRouter(auditorService = defaultAuditorService) { /** * POST /:dbId/:runId/pause - Pause a running audit */ - router.post('/:dbId/:runId/pause', async (req: Request, res: Response) => { + router.post('/:dbId/:runId/pause', async (req: Request<{ dbId: string; runId: string }>, res: Response) => { const { runId } = req.params; const paused = await auditorService.pauseAudit(runId); @@ -63,7 +63,7 @@ export function createAuditorRouter(auditorService = defaultAuditorService) { /** * POST /:dbId/:runId/resume - Resume a paused audit */ - router.post('/:dbId/:runId/resume', async (req: Request, res: Response, next) => { + router.post('/:dbId/:runId/resume', async (req: Request<{ dbId: string; runId: string }>, res: Response, next) => { const { dbId, runId } = req.params; if (auditorService.isRunning()) { @@ -89,7 +89,7 @@ export function createAuditorRouter(auditorService = defaultAuditorService) { /** * POST /:dbId/:runId/cancel - Cancel a running audit */ - router.post('/:dbId/:runId/cancel', async (req: Request, res: Response) => { + router.post('/:dbId/:runId/cancel', async (req: Request<{ dbId: string; runId: string }>, res: Response) => { const { runId } = req.params; const cancelled = await auditorService.cancelAudit(runId); @@ -105,7 +105,7 @@ export function createAuditorRouter(auditorService = defaultAuditorService) { * GET /:dbId/events - SSE stream for audit progress (observe-only) * Subscribes to events from a running audit. Does NOT start audits. */ - router.get('/:dbId/events', (req: Request, res: Response) => { + router.get('/:dbId/events', (req: Request<{ dbId: string }>, res: Response) => { const sendEvent = initSSEData(res); if (!auditorService.isRunning()) { @@ -137,7 +137,7 @@ export function createAuditorRouter(auditorService = defaultAuditorService) { * POST /:dbId/path-audit - Audit a specific path of person IDs * Body: { personIds: string[] } */ - router.post('/:dbId/path-audit', async (req: Request, res: Response, next) => { + router.post('/:dbId/path-audit', async (req: Request<{ dbId: string }>, res: Response, next) => { const { dbId } = req.params; const personIds = req.body?.personIds as string[] | undefined; @@ -161,7 +161,7 @@ export function createAuditorRouter(auditorService = defaultAuditorService) { /** * GET /:dbId/issue-overlay - Get issue counts per person for tree overlay */ - router.get('/:dbId/issue-overlay', async (req: Request, res: Response) => { + router.get('/:dbId/issue-overlay', async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const overlay = await auditorService.getIssueOverlay(dbId); res.json({ success: true, data: overlay }); @@ -177,7 +177,7 @@ export function createAuditorRouter(auditorService = defaultAuditorService) { /** * GET /:dbId/runs - List audit runs for a database */ - router.get('/:dbId/runs', async (req: Request, res: Response) => { + router.get('/:dbId/runs', async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const runs = await auditorService.getRunsByDb(dbId); res.json({ success: true, data: runs }); @@ -191,7 +191,7 @@ export function createAuditorRouter(auditorService = defaultAuditorService) { * GET /:dbId/issues - List issues (filterable) * Query: ?type=impossible_date&severity=error&status=open&runId=xxx */ - router.get('/:dbId/issues', async (req: Request, res: Response) => { + router.get('/:dbId/issues', async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const filters = { type: req.query.type as AuditIssueType | undefined, @@ -207,7 +207,7 @@ export function createAuditorRouter(auditorService = defaultAuditorService) { /** * GET /:dbId/issues/:issueId - Get single issue detail */ - router.get('/:dbId/issues/:issueId', async (req: Request, res: Response) => { + router.get('/:dbId/issues/:issueId', async (req: Request<{ dbId: string; issueId: string }>, res: Response) => { const { issueId } = req.params; const issue = await auditorService.getIssue(issueId); @@ -227,7 +227,7 @@ export function createAuditorRouter(auditorService = defaultAuditorService) { * POST /:dbId/issues/accept - Bulk accept issues * Body: { issueIds: string[] } */ - router.post('/:dbId/issues/accept', async (req: Request, res: Response) => { + router.post('/:dbId/issues/accept', async (req: Request<{ dbId: string }>, res: Response) => { const issueIds = req.body?.issueIds as string[] | undefined; if (!Array.isArray(issueIds) || !issueIds.length) { @@ -243,7 +243,7 @@ export function createAuditorRouter(auditorService = defaultAuditorService) { * POST /:dbId/issues/reject - Bulk reject issues * Body: { issueIds: string[] } */ - router.post('/:dbId/issues/reject', async (req: Request, res: Response) => { + router.post('/:dbId/issues/reject', async (req: Request<{ dbId: string }>, res: Response) => { const issueIds = req.body?.issueIds as string[] | undefined; if (!Array.isArray(issueIds) || !issueIds.length) { @@ -258,7 +258,7 @@ export function createAuditorRouter(auditorService = defaultAuditorService) { /** * POST /:dbId/issues/:issueId/accept - Accept single issue */ - router.post('/:dbId/issues/:issueId/accept', async (req: Request, res: Response) => { + router.post('/:dbId/issues/:issueId/accept', async (req: Request<{ dbId: string; issueId: string }>, res: Response) => { const { issueId } = req.params; const result = await auditorService.acceptIssue(issueId); @@ -273,7 +273,7 @@ export function createAuditorRouter(auditorService = defaultAuditorService) { /** * POST /:dbId/issues/:issueId/reject - Reject single issue */ - router.post('/:dbId/issues/:issueId/reject', async (req: Request, res: Response) => { + router.post('/:dbId/issues/:issueId/reject', async (req: Request<{ dbId: string; issueId: string }>, res: Response) => { const { issueId } = req.params; const result = await auditorService.rejectIssue(issueId); @@ -292,7 +292,7 @@ export function createAuditorRouter(auditorService = defaultAuditorService) { /** * GET /:dbId/changes - Get audit change log */ - router.get('/:dbId/changes', async (req: Request, res: Response) => { + router.get('/:dbId/changes', async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const changes = await auditorService.getChanges(dbId); res.json({ success: true, data: changes }); @@ -301,7 +301,7 @@ export function createAuditorRouter(auditorService = defaultAuditorService) { /** * POST /:dbId/changes/:changeId/undo - Undo a specific change */ - router.post('/:dbId/changes/:changeId/undo', async (req: Request, res: Response) => { + router.post('/:dbId/changes/:changeId/undo', async (req: Request<{ dbId: string; changeId: string }>, res: Response) => { const { changeId } = req.params; const result = await auditorService.undoChange(changeId); @@ -316,7 +316,7 @@ export function createAuditorRouter(auditorService = defaultAuditorService) { /** * GET /:dbId/:runId - Get run details with summary (catch-all, must be last) */ - router.get('/:dbId/:runId', async (req: Request, res: Response) => { + router.get('/:dbId/:runId', async (req: Request<{ dbId: string; runId: string }>, res: Response) => { const { runId } = req.params; const summary = await auditorService.getRunSummary(runId); diff --git a/server/src/routes/augmentation.routes.ts b/server/src/routes/augmentation.routes.ts index 7e4b4c23..823f1b60 100644 --- a/server/src/routes/augmentation.routes.ts +++ b/server/src/routes/augmentation.routes.ts @@ -25,7 +25,7 @@ const router = Router(); * All provider photo endpoints follow the same pattern. */ function servePhoto(getPath: (id: string) => string | null, label: string) { - return async (req: Request, res: Response) => { + return async (req: Request<{ personId: string }>, res: Response) => { const personId = sanitizePersonId(req.params.personId); const photoPath = getPath(personId); @@ -55,7 +55,7 @@ function servePhoto(getPath: (id: string) => string | null, label: string) { * All providers use the same pattern: sanitize ID, check existence, return JSON. */ function checkPhotoExists(hasPhoto: (id: string) => boolean) { - return async (req: Request, res: Response) => { + return async (req: Request<{ personId: string }>, res: Response) => { const personId = sanitizePersonId(req.params.personId); const exists = hasPhoto(personId); res.setHeader('Cache-Control', 'no-cache, no-store, must-revalidate'); @@ -72,7 +72,7 @@ function linkPlatform( label: string, linkFn: (personId: string, url: string) => Promise ) { - return asyncHandler(async (req: Request, res: Response) => { + return asyncHandler(async (req: Request<{ personId: string }>, res: Response) => { const personId = sanitizePersonId(req.params.personId); const { url } = req.body; @@ -99,7 +99,7 @@ function linkPlatform( } // Get augmentation data for a person -router.get('/:personId', async (req: Request, res: Response) => { +router.get('/:personId', async (req: Request<{ personId: string }>, res: Response) => { const personId = sanitizePersonId(req.params.personId); const data = await augmentationService.getAugmentation(personId); @@ -115,7 +115,7 @@ router.get('/:personId', async (req: Request, res: Response) => { router.post('/:personId/wikipedia', linkPlatform('wikipedia.org', 'Wikipedia', (id, url) => linkWikipedia(id, url))); // Update custom augmentation data -router.put('/:personId', async (req: Request, res: Response) => { +router.put('/:personId', async (req: Request<{ personId: string }>, res: Response) => { const personId = sanitizePersonId(req.params.personId); const { customBio, customPhotoUrl, notes } = req.body; @@ -167,7 +167,7 @@ router.get('/:personId/linkedin-photo', servePhoto(id => getLinkedInPhotoPath(id router.get('/:personId/linkedin-photo/exists', checkPhotoExists(id => hasLinkedInPhoto(id))); // Fetch and download photo from a linked platform -router.post('/:personId/fetch-photo/:platform', async (req: Request, res: Response) => { +router.post('/:personId/fetch-photo/:platform', async (req: Request<{ personId: string; platform: string }>, res: Response) => { const personId = sanitizePersonId(req.params.personId); const { platform } = req.params; @@ -189,14 +189,14 @@ router.post('/:personId/fetch-photo/:platform', async (req: Request, res: Respon }); // Get all provider mappings for a person -router.get('/:personId/provider-links', async (req: Request, res: Response) => { +router.get('/:personId/provider-links', async (req: Request<{ personId: string }>, res: Response) => { const personId = sanitizePersonId(req.params.personId); const mappings = await getProviderMappings(personId); res.json({ success: true, data: mappings }); }); // Link a person to a provider -router.post('/:personId/provider-link', async (req: Request, res: Response) => { +router.post('/:personId/provider-link', async (req: Request<{ personId: string }>, res: Response) => { const personId = sanitizePersonId(req.params.personId); const { providerId, platform, url, externalId, confidence, matchedBy } = req.body; @@ -229,7 +229,7 @@ router.post('/:personId/provider-link', async (req: Request, res: Response) => { }); // Unlink a person from a provider -router.delete('/:personId/provider-link/:providerId', async (req: Request, res: Response) => { +router.delete('/:personId/provider-link/:providerId', async (req: Request<{ personId: string; providerId: string }>, res: Response) => { const personId = sanitizePersonId(req.params.personId); const { providerId } = req.params; diff --git a/server/src/routes/browser.routes.ts b/server/src/routes/browser.routes.ts index 4ffa33e9..19019625 100644 --- a/server/src/routes/browser.routes.ts +++ b/server/src/routes/browser.routes.ts @@ -139,7 +139,7 @@ router.post('/login', asyncHandler(async (_req: Request, res: Response) => { })); // Scrape person data (streaming) -router.get('/scrape/:personId', async (req: Request, res: Response) => { +router.get('/scrape/:personId', async (req: Request<{ personId: string }>, res: Response) => { const personId = sanitizePersonId(req.params.personId); const sendEvent = initSSE(res); @@ -162,7 +162,7 @@ router.get('/scrape/:personId', async (req: Request, res: Response) => { }); // Scrape person data (non-streaming) -router.post('/scrape/:personId', async (req: Request, res: Response) => { +router.post('/scrape/:personId', async (req: Request<{ personId: string }>, res: Response) => { const personId = sanitizePersonId(req.params.personId); const data = await scraperService.scrapePerson(personId).catch(err => { @@ -177,7 +177,7 @@ router.post('/scrape/:personId', async (req: Request, res: Response) => { }); // Get scraped data for a person -router.get('/scraped/:personId', async (req: Request, res: Response) => { +router.get('/scraped/:personId', async (req: Request<{ personId: string }>, res: Response) => { const personId = sanitizePersonId(req.params.personId); const data = scraperService.getScrapedData(personId); @@ -190,7 +190,7 @@ router.get('/scraped/:personId', async (req: Request, res: Response) => { }); // Serve photo files -router.get('/photos/:personId', async (req: Request, res: Response) => { +router.get('/photos/:personId', async (req: Request<{ personId: string }>, res: Response) => { const personId = sanitizePersonId(req.params.personId); const photoPath = scraperService.getPhotoPath(personId); @@ -208,7 +208,7 @@ router.get('/photos/:personId', async (req: Request, res: Response) => { }); // Check if photo exists -router.get('/photos/:personId/exists', async (req: Request, res: Response) => { +router.get('/photos/:personId/exists', async (req: Request<{ personId: string }>, res: Response) => { const personId = sanitizePersonId(req.params.personId); const exists = scraperService.hasPhoto(personId); const fsExists = scraperService.hasFsPhoto(personId); diff --git a/server/src/routes/deaths.routes.ts b/server/src/routes/deaths.routes.ts index 32900cbd..4e6a69bf 100644 --- a/server/src/routes/deaths.routes.ts +++ b/server/src/routes/deaths.routes.ts @@ -30,18 +30,18 @@ deathsRouter.post('/keywords', asyncHandler(async (req: Request, res: Response) res.json({ success: true, data: await deathsService.listKeywords() }); })); -deathsRouter.delete('/keywords/:keyword', asyncHandler(async (req: Request, res: Response) => { +deathsRouter.delete('/keywords/:keyword', asyncHandler(async (req: Request<{ keyword: string }>, res: Response) => { const removed = await deathsService.removeKeyword(req.params.keyword); res.json({ success: true, data: { removed, keywords: await deathsService.listKeywords() } }); })); // Per-person read / edit -deathsRouter.get('/:personId', asyncHandler(async (req: Request, res: Response) => { +deathsRouter.get('/:personId', asyncHandler(async (req: Request<{ personId: string }>, res: Response) => { const info = await deathsService.getDeathInfo(req.params.personId); res.json({ success: true, data: info }); })); -deathsRouter.patch('/:personId', asyncHandler(async (req: Request, res: Response) => { +deathsRouter.patch('/:personId', asyncHandler(async (req: Request<{ personId: string }>, res: Response) => { const { cause, circumstance, isUnusualManual, reason } = req.body ?? {}; const info = await deathsService.setDeathInfo(req.params.personId, { cause, diff --git a/server/src/routes/favorites.routes.ts b/server/src/routes/favorites.routes.ts index a2db2d18..e692f4fa 100644 --- a/server/src/routes/favorites.routes.ts +++ b/server/src/routes/favorites.routes.ts @@ -50,7 +50,7 @@ export const createFavoritesRouter = (services: FavoritesRouterServices = {}) => })); // Get favorites in a specific database (legacy endpoint, kept for backwards compatibility) - router.get('/in-database/:dbId', async (req: Request, res: Response) => { + router.get('/in-database/:dbId', async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const favorites = await favoritesService.getFavoritesInDatabase(dbId).catch(err => { @@ -64,7 +64,7 @@ export const createFavoritesRouter = (services: FavoritesRouterServices = {}) => }); // Get sparse tree for a database (legacy location) - router.get('/sparse-tree/:dbId', async (req: Request, res: Response) => { + router.get('/sparse-tree/:dbId', async (req: Request<{ dbId: string }>, res: Response) => { const result = await fetchSparseTree(req.params.dbId, resolveSparseSource(req), sparseTreeService).catch(err => { res.status(500).json({ success: false, error: err.message }); return null; @@ -77,7 +77,7 @@ export const createFavoritesRouter = (services: FavoritesRouterServices = {}) => // ============ DB-SCOPED ENDPOINTS ============ // List favorites in a specific database - router.get('/db/:dbId', asyncHandler(async (req: Request, res: Response) => { + router.get('/db/:dbId', asyncHandler(async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const page = parseInt(req.query.page as string) || 1; const limit = parseInt(req.query.limit as string) || 50; @@ -87,14 +87,14 @@ export const createFavoritesRouter = (services: FavoritesRouterServices = {}) => })); // Get tags for a specific database - router.get('/db/:dbId/tags', asyncHandler(async (req: Request, res: Response) => { + router.get('/db/:dbId/tags', asyncHandler(async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const allTags = await favoritesService.getDbTags(dbId); res.json({ success: true, data: { presetTags: PRESET_TAGS, allTags } }); })); // Get sparse tree for a database (new location under db-scoped) - router.get('/db/:dbId/sparse-tree', async (req: Request, res: Response) => { + router.get('/db/:dbId/sparse-tree', async (req: Request<{ dbId: string }>, res: Response) => { const result = await fetchSparseTree(req.params.dbId, resolveSparseSource(req), sparseTreeService).catch(err => { res.status(500).json({ success: false, error: err.message }); return null; @@ -106,7 +106,7 @@ export const createFavoritesRouter = (services: FavoritesRouterServices = {}) => // Get favorite status for a person in a specific database // Accepts both ULID and FamilySearch ID - router.get('/db/:dbId/:personId', asyncHandler(async (req: Request, res: Response) => { + router.get('/db/:dbId/:personId', asyncHandler(async (req: Request<{ dbId: string; personId: string }>, res: Response) => { const { dbId } = req.params; const { personId } = req.params; const favorite = await favoritesService.getDbFavorite(dbId, personId); @@ -115,7 +115,7 @@ export const createFavoritesRouter = (services: FavoritesRouterServices = {}) => // Mark a person as favorite in a specific database // Accepts both ULID and FamilySearch ID - router.post('/db/:dbId/:personId', asyncHandler(async (req: Request, res: Response) => { + router.post('/db/:dbId/:personId', asyncHandler(async (req: Request<{ dbId: string; personId: string }>, res: Response) => { const { dbId } = req.params; const { personId } = req.params; const whyInteresting = requireWhyInteresting(req, res); @@ -134,7 +134,7 @@ export const createFavoritesRouter = (services: FavoritesRouterServices = {}) => // Update favorite details in a specific database // Accepts both ULID and FamilySearch ID - router.put('/db/:dbId/:personId', asyncHandler(async (req: Request, res: Response) => { + router.put('/db/:dbId/:personId', asyncHandler(async (req: Request<{ dbId: string; personId: string }>, res: Response) => { const { dbId } = req.params; const { personId } = req.params; const whyInteresting = requireWhyInteresting(req, res); @@ -158,7 +158,7 @@ export const createFavoritesRouter = (services: FavoritesRouterServices = {}) => // Remove from favorites in a specific database // Accepts both ULID and FamilySearch ID - router.delete('/db/:dbId/:personId', asyncHandler(async (req: Request, res: Response) => { + router.delete('/db/:dbId/:personId', asyncHandler(async (req: Request<{ dbId: string; personId: string }>, res: Response) => { const { dbId } = req.params; const { personId } = req.params; const removed = await favoritesService.removeDbFavorite(dbId, personId); @@ -175,7 +175,7 @@ export const createFavoritesRouter = (services: FavoritesRouterServices = {}) => // Get favorite status for a person (legacy - global) // Accepts both ULID and FamilySearch ID - router.get('/:personId', async (req: Request, res: Response) => { + router.get('/:personId', async (req: Request<{ personId: string }>, res: Response) => { const { personId } = req.params; const favorite = await favoritesService.getFavorite(personId); @@ -184,7 +184,7 @@ export const createFavoritesRouter = (services: FavoritesRouterServices = {}) => // Mark a person as favorite (legacy - global) // Accepts both ULID and FamilySearch ID - router.post('/:personId', async (req: Request, res: Response) => { + router.post('/:personId', async (req: Request<{ personId: string }>, res: Response) => { const { personId } = req.params; const whyInteresting = requireWhyInteresting(req, res); if (!whyInteresting) return; @@ -201,7 +201,7 @@ export const createFavoritesRouter = (services: FavoritesRouterServices = {}) => // Update favorite details (legacy - global) // Accepts both ULID and FamilySearch ID - router.put('/:personId', async (req: Request, res: Response) => { + router.put('/:personId', async (req: Request<{ personId: string }>, res: Response) => { const { personId } = req.params; const whyInteresting = requireWhyInteresting(req, res); if (!whyInteresting) return; @@ -223,7 +223,7 @@ export const createFavoritesRouter = (services: FavoritesRouterServices = {}) => // Remove from favorites (legacy - global) // Accepts both ULID and FamilySearch ID - router.delete('/:personId', async (req: Request, res: Response) => { + router.delete('/:personId', async (req: Request<{ personId: string }>, res: Response) => { const { personId } = req.params; const data = await favoritesService.removeFavorite(personId); diff --git a/server/src/routes/gedcom.routes.ts b/server/src/routes/gedcom.routes.ts index 71eb2293..0d80d5c9 100644 --- a/server/src/routes/gedcom.routes.ts +++ b/server/src/routes/gedcom.routes.ts @@ -23,7 +23,7 @@ const validateGedcomContent = (content: string, res: Response): boolean => { /** * Export database to GEDCOM format */ -router.get('/export/:dbId', (req: Request, res: Response) => { +router.get('/export/:dbId', (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const gedcom = gedcomService.exportToGedcom(dbId); diff --git a/server/src/routes/genealogy-provider.routes.ts b/server/src/routes/genealogy-provider.routes.ts index b7e2d9c4..557849d1 100644 --- a/server/src/routes/genealogy-provider.routes.ts +++ b/server/src/routes/genealogy-provider.routes.ts @@ -20,7 +20,7 @@ router.get('/platforms', (_req: Request, res: Response) => { }); // Get defaults for a specific platform -router.get('/defaults/:platform', (req: Request, res: Response) => { +router.get('/defaults/:platform', (req: Request<{ platform: string }>, res: Response) => { const { platform } = req.params; const defaults = genealogyProviderService.getProviderDefaults(platform as PlatformType); @@ -33,7 +33,7 @@ router.get('/defaults/:platform', (req: Request, res: Response) => { }); // Get single provider -router.get('/:id', (req: Request, res: Response) => { +router.get('/:id', (req: Request<{ id: string }>, res: Response) => { const { id } = req.params; const provider = genealogyProviderService.getProvider(id); @@ -88,7 +88,7 @@ router.post('/', (req: Request, res: Response) => { }); // Update provider -router.put('/:id', (req: Request, res: Response) => { +router.put('/:id', (req: Request<{ id: string }>, res: Response) => { const { id } = req.params; const existing = genealogyProviderService.getProvider(id); @@ -109,7 +109,7 @@ router.put('/:id', (req: Request, res: Response) => { }); // Delete provider -router.delete('/:id', (req: Request, res: Response) => { +router.delete('/:id', (req: Request<{ id: string }>, res: Response) => { const { id } = req.params; const result = (() => { @@ -126,7 +126,7 @@ router.delete('/:id', (req: Request, res: Response) => { }); // Test connection -router.post('/:id/test', asyncHandler(async (req: Request, res: Response) => { +router.post('/:id/test', asyncHandler(async (req: Request<{ id: string }>, res: Response) => { const { id } = req.params; const result = await genealogyProviderService.testConnection(id); @@ -138,7 +138,7 @@ router.post('/:id/test', asyncHandler(async (req: Request, res: Response) => { })); // Set as active provider -router.post('/:id/activate', (req: Request, res: Response) => { +router.post('/:id/activate', (req: Request<{ id: string }>, res: Response) => { const { id } = req.params; const provider = genealogyProviderService.getProvider(id); diff --git a/server/src/routes/integrity.routes.ts b/server/src/routes/integrity.routes.ts index 0bffc0c4..43526dc3 100644 --- a/server/src/routes/integrity.routes.ts +++ b/server/src/routes/integrity.routes.ts @@ -16,7 +16,7 @@ const router = Router(); /** * GET /:dbId - Full integrity summary with counts */ -router.get('/:dbId', async (req: Request, res: Response) => { +router.get('/:dbId', async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const summary = await integrityService.getIntegritySummary(dbId); @@ -27,7 +27,7 @@ router.get('/:dbId', async (req: Request, res: Response) => { * GET /:dbId/coverage - Provider coverage gaps * Query: ?providers=familysearch,ancestry */ -router.get('/:dbId/coverage', async (req: Request, res: Response) => { +router.get('/:dbId/coverage', async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const providers = req.query.providers ? (req.query.providers as string).split(',') @@ -41,7 +41,7 @@ router.get('/:dbId/coverage', async (req: Request, res: Response) => { * GET /:dbId/parents - Parent linkage gaps * Query: ?provider=familysearch */ -router.get('/:dbId/parents', async (req: Request, res: Response) => { +router.get('/:dbId/parents', async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const provider = req.query.provider as string | undefined; @@ -52,7 +52,7 @@ router.get('/:dbId/parents', async (req: Request, res: Response) => { /** * GET /:dbId/orphans - Orphaned parent edges */ -router.get('/:dbId/orphans', async (req: Request, res: Response) => { +router.get('/:dbId/orphans', async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const orphans = await integrityService.getOrphanedEdges(dbId); @@ -63,7 +63,7 @@ router.get('/:dbId/orphans', async (req: Request, res: Response) => { * GET /:dbId/stale - Stale provider cache records * Query: ?days=30 */ -router.get('/:dbId/stale', async (req: Request, res: Response) => { +router.get('/:dbId/stale', async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const days = parseInt(req.query.days as string) || 30; @@ -75,7 +75,7 @@ router.get('/:dbId/stale', async (req: Request, res: Response) => { * POST /:dbId/discover-all - Start bulk parent discovery * Body: { provider: 'familysearch' } */ -router.post('/:dbId/discover-all', (req: Request, res: Response) => { +router.post('/:dbId/discover-all', (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const { provider } = req.body as { provider: BuiltInProvider }; @@ -121,7 +121,7 @@ router.post('/:dbId/discover-all', (req: Request, res: Response) => { * GET /:dbId/discover-all/events - SSE stream for bulk discovery progress * Query: ?provider=familysearch */ -router.get('/:dbId/discover-all/events', async (req: Request, res: Response) => { +router.get('/:dbId/discover-all/events', async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const provider = req.query.provider as BuiltInProvider; diff --git a/server/src/routes/map.routes.ts b/server/src/routes/map.routes.ts index 964a736f..e531a098 100644 --- a/server/src/routes/map.routes.ts +++ b/server/src/routes/map.routes.ts @@ -104,7 +104,7 @@ mapRouter.get('/geocode/stream', async (req: Request, res: Response) => { * GET /api/map/:dbId/sparse * Get sparse tree map data (favorites only) */ -mapRouter.get('/:dbId/sparse', asyncHandler(async (req: Request, res: Response) => { +mapRouter.get('/:dbId/sparse', asyncHandler(async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; logger.api('map', `Sparse tree map data for ${dbId}`); @@ -117,7 +117,7 @@ mapRouter.get('/:dbId/sparse', asyncHandler(async (req: Request, res: Response) * GET /api/map/:dbId/:personId * Get ancestry tree map data for a person */ -mapRouter.get('/:dbId/:personId', asyncHandler(async (req: Request, res: Response) => { +mapRouter.get('/:dbId/:personId', asyncHandler(async (req: Request<{ dbId: string; personId: string }>, res: Response) => { const { dbId, personId } = req.params; const MAX_DEPTH = 15; const parsedDepth = parseInt(req.query.depth as string); diff --git a/server/src/routes/sync.routes.ts b/server/src/routes/sync.routes.ts index 85a3b681..063f046d 100644 --- a/server/src/routes/sync.routes.ts +++ b/server/src/routes/sync.routes.ts @@ -16,7 +16,7 @@ const router = Router(); /** * Compare a person across all enabled providers (legacy endpoint) */ -router.get('/:dbId/:personId/compare', async (req: Request, res: Response) => { +router.get('/:dbId/:personId/compare', async (req: Request<{ dbId: string; personId: string }>, res: Response) => { const { dbId, personId } = req.params; const comparison = await syncService.compareAcrossProviders(dbId, personId) @@ -36,7 +36,7 @@ router.get('/:dbId/:personId/compare', async (req: Request, res: Response) => { * Returns a detailed comparison of person data across all linked providers, * showing which fields match, differ, or are missing. */ -router.get('/:dbId/:personId/multi-platform-compare', async (req: Request, res: Response) => { +router.get('/:dbId/:personId/multi-platform-compare', async (req: Request<{ dbId: string; personId: string }>, res: Response) => { const { dbId, personId } = req.params; const comparison = await multiPlatformComparisonService.compareAcrossPlatforms(dbId, personId) @@ -55,7 +55,7 @@ router.get('/:dbId/:personId/multi-platform-compare', async (req: Request, res: * * Scrapes fresh data from the specified provider and updates the cache. */ -router.post('/:dbId/:personId/refresh-provider/:provider', async (req: Request, res: Response) => { +router.post('/:dbId/:personId/refresh-provider/:provider', async (req: Request<{ dbId: string; personId: string; provider: string }>, res: Response) => { const { dbId, personId, provider } = req.params; // Validate provider @@ -81,7 +81,7 @@ router.post('/:dbId/:personId/refresh-provider/:provider', async (req: Request, /** * Compare local data with FamilySearch for upload */ -router.get('/:dbId/:personId/compare-for-upload', async (req: Request, res: Response) => { +router.get('/:dbId/:personId/compare-for-upload', async (req: Request<{ dbId: string; personId: string }>, res: Response) => { const { dbId, personId } = req.params; const comparison = await familySearchUploadService.compareForUpload(dbId, personId) @@ -101,7 +101,7 @@ router.get('/:dbId/:personId/compare-for-upload', async (req: Request, res: Resp * Uses the browser session to extract auth token and fetch fresh data * from the FamilySearch API. Updates the local JSON cache and PostgreSQL query store. */ -router.post('/:dbId/:personId/refresh-from-familysearch', async (req: Request, res: Response) => { +router.post('/:dbId/:personId/refresh-from-familysearch', async (req: Request<{ dbId: string; personId: string }>, res: Response) => { const { dbId, personId } = req.params; const result = await familySearchRefreshService.refreshPerson(dbId, personId) @@ -121,7 +121,7 @@ router.post('/:dbId/:personId/refresh-from-familysearch', async (req: Request, r /** * Upload selected fields to FamilySearch */ -router.post('/:dbId/:personId/upload-to-familysearch', async (req: Request, res: Response) => { +router.post('/:dbId/:personId/upload-to-familysearch', async (req: Request<{ dbId: string; personId: string }>, res: Response) => { const { dbId, personId } = req.params; const { fields } = req.body as { fields: string[] }; @@ -143,7 +143,7 @@ router.post('/:dbId/:personId/upload-to-familysearch', async (req: Request, res: /** * Compare local photo with Ancestry for upload */ -router.get('/:dbId/:personId/compare-for-ancestry-upload', async (req: Request, res: Response) => { +router.get('/:dbId/:personId/compare-for-ancestry-upload', async (req: Request<{ dbId: string; personId: string }>, res: Response) => { const { dbId, personId } = req.params; const comparison = await ancestryUploadService.compareForUpload(dbId, personId) @@ -160,7 +160,7 @@ router.get('/:dbId/:personId/compare-for-ancestry-upload', async (req: Request, /** * Upload photo to Ancestry */ -router.post('/:dbId/:personId/upload-to-ancestry', async (req: Request, res: Response) => { +router.post('/:dbId/:personId/upload-to-ancestry', async (req: Request<{ dbId: string; personId: string }>, res: Response) => { const { dbId, personId } = req.params; const { fields } = req.body as { fields: string[] }; @@ -182,7 +182,7 @@ router.post('/:dbId/:personId/upload-to-ancestry', async (req: Request, res: Res /** * Import a person from a provider */ -router.post('/:dbId/:personId/import', async (req: Request, res: Response) => { +router.post('/:dbId/:personId/import', async (req: Request<{ dbId: string; personId: string }>, res: Response) => { const { dbId, personId } = req.params; const { provider, externalId } = req.body as { provider: BuiltInProvider; externalId?: string }; @@ -208,7 +208,7 @@ router.post('/:dbId/:personId/import', async (req: Request, res: Response) => { /** * Open edit page on provider (push update) */ -router.post('/:dbId/:personId/push', async (req: Request, res: Response) => { +router.post('/:dbId/:personId/push', async (req: Request<{ dbId: string; personId: string }>, res: Response) => { const { dbId, personId } = req.params; const { provider } = req.body as { provider: BuiltInProvider }; @@ -231,7 +231,7 @@ router.post('/:dbId/:personId/push', async (req: Request, res: Response) => { /** * Find matching person on another provider */ -router.post('/:dbId/:personId/find-match', async (req: Request, res: Response) => { +router.post('/:dbId/:personId/find-match', async (req: Request<{ dbId: string; personId: string }>, res: Response) => { const { dbId, personId } = req.params; const { provider } = req.body as { provider: BuiltInProvider }; @@ -275,7 +275,7 @@ router.post('/:dbId/:personId/find-match', async (req: Request, res: Response) = /** * SSE endpoint for batch database sync */ -router.get('/database/:dbId/events', async (req: Request, res: Response) => { +router.get('/database/:dbId/events', async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const provider = req.query.provider as BuiltInProvider; const direction = (req.query.direction as 'import' | 'export' | 'both') || 'import'; @@ -301,7 +301,7 @@ router.get('/database/:dbId/events', async (req: Request, res: Response) => { /** * Start batch database sync (non-SSE, returns immediately) */ -router.post('/database/:dbId', async (req: Request, res: Response) => { +router.post('/database/:dbId', async (req: Request<{ dbId: string }>, res: Response) => { const { dbId } = req.params; const { provider, direction } = req.body as { provider: BuiltInProvider; diff --git a/server/src/utils/asyncHandler.ts b/server/src/utils/asyncHandler.ts index a5ea3865..2e2a0ba4 100644 --- a/server/src/utils/asyncHandler.ts +++ b/server/src/utils/asyncHandler.ts @@ -1,12 +1,14 @@ import type { Request, Response, NextFunction, RequestHandler } from 'express'; +import type { ParamsDictionary } from 'express-serve-static-core'; /** * Wraps an async Express route handler so that rejected promises * are forwarded to the Express error handler via next(err). + * Generic over route params so handlers can declare `Request<{ id: string }>`. */ -export function asyncHandler( - fn: (req: Request, res: Response, next: NextFunction) => Promise -): RequestHandler { +export function asyncHandler

( + fn: (req: Request

, res: Response, next: NextFunction) => Promise +): RequestHandler

{ return (req, res, next) => { Promise.resolve(fn(req, res, next)).catch(next); }; diff --git a/tests/scraper/familysearch.scraper.spec.ts b/tests/scraper/familysearch.scraper.spec.ts index 6ecbd534..7b0f2f51 100644 --- a/tests/scraper/familysearch.scraper.spec.ts +++ b/tests/scraper/familysearch.scraper.spec.ts @@ -5,6 +5,12 @@ import { test, expect } from '@playwright/test'; import { createMockFamilySearchServer, type MockPerson } from '../__mocks__/providers/familysearch/server'; +// Every test shares one mock server on a fixed port and some mutate its login +// state, so the file must run in a single worker. Under fullyParallel, each +// worker's beforeAll tries to bind the same port and a test can hit another +// worker's server, ignoring the state it just set. +test.describe.configure({ mode: 'default' }); + const MOCK_PORT = 3333; let mockServer: ReturnType;