diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 110fa60..3e36655 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,6 +3,10 @@ version: 2 updates: - package-ecosystem: 'npm' directory: '/' + # Keep eslint/@eslint/js on 9.x for now. + # Verified on 2026-04-20: an isolated npm install of eslint@10.2.1 and @eslint/js@10.0.1 + # still fails dependency resolution, and eslint-plugin-react@7.37.5 currently advertises + # eslint support only through ^9.7. schedule: interval: 'weekly' day: 'monday' @@ -12,6 +16,17 @@ updates: open-pull-requests-limit: 5 rebase-strategy: 'auto' versioning-strategy: 'increase-if-necessary' + ignore: + - dependency-name: 'eslint' + update-types: + - 'version-update:semver-major' + versions: + - '>=10' + - dependency-name: '@eslint/js' + update-types: + - 'version-update:semver-major' + versions: + - '>=10' labels: - 'dependencies' commit-message: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 731d815..6148b04 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Node.js - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 24 cache: npm @@ -78,7 +78,7 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Set up Node.js - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 24 cache: npm diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7a82622..9f34e00 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -99,7 +99,7 @@ jobs: fi - name: Set up Node.js - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 24 cache: npm diff --git a/.gitignore b/.gitignore index 74a26da..1546cb3 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ test-json/ .idea coverage/ requirements/ +docs/security/ /activity-*.png /cache-hit-rate-*.png /request-*.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 23eff68..2ac2f6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## [6.2.5] - 2026-04-20 + +### Added + +- **Gezielte Security-Regressionstests für lokale Serverhärtung** — neue Unit-, Integrations- und E2E-Tests decken jetzt Null-Byte-Requests, Host-/Origin-Vertrauen, Auto-Import-Serialisierung und Custom-API-Prefix-Pfade gezielt gegen Regressionen ab + +### Improved + +- **Toktrack-Stand und Nutzerkommunikation im Auto-Import** — `toktrack` ist jetzt repo-weit exakt auf `2.5.0` angehoben, inklusive aktualisierter Runner-/UI-/Test-Erwartungen und robusterer Fehler- sowie Abbruchkommunikation bei Version-Check, Runner-Fehlern, ungültigem JSON und fehlerhaften Nutzdaten +- **Robustere Paket-Runner-Warmups für `bunx` und `npx`** — paketbasierte `toktrack`-Aufrufe verwenden jetzt großzügigere, runner-spezifische Probe-/Versions-Timeouts, liefern klarere First-Run- und Timeout-Hinweise für Paket-Downloads und bleiben im Auto-Import über Unit-, Integration- und E2E-Tests vollständig abgesichert +- **Direktabhängigkeiten und Workflow-Actions auf aktuelle kompatible Stände gebracht** — kompatible npm-Dependencies sowie GitHub Actions wurden aktualisiert, darunter `actions/setup-node` auf `6.4.0`, bei unverändert stabilem lokalen Verify-, Coverage- und E2E-Gate +- **Background-Registry und Runtime-Metadaten für lokale Serverinstanzen** — Background-Einträge speichern jetzt den zugehörigen `apiPrefix`, und `/api/runtime` gibt nur noch die für App und CLI tatsächlich benötigten Felder zurück + +### Fixed + +- **Hängende oder unklare Toktrack-Auto-Import-Läufe** — der Auto-Import hat jetzt explizite Timeouts, einen sauberen Cancel-/Abort-Pfad bis zum Child-Process und differenzierte, lokalisierbare Fehlermeldungen statt generischer oder irreführender Runner-Fehler +- **Unscharfe Vertrauensgrenzen bei lokalen Mutationen** — mutierende Requests ohne vertrauenswürdigen `Origin` sowie untrusted `Host`-Header werden jetzt konsequent mit `403` blockiert, ohne das Local-First-Modell um einen Login-/Token-Layer zu erweitern +- **Mehrere robuste Server-Härtungslücken aus dem Pentest-Review** — Null-Byte-Pfade und rohe fehlerhafte Requests führen nicht mehr zu instabilem Static-Serving, und parallele `POST /api/auto-import/stream`-Starts werden frühzeitig abgewehrt, bevor ein zweiter `toktrack`-Runner gestartet werden kann + ## [6.2.4] - 2026-04-16 ### Added diff --git a/README.md b/README.md index ce21813..34c8f5e 100644 --- a/README.md +++ b/README.md @@ -98,8 +98,8 @@ Then either: The auto-import path prefers: 1. local `toktrack` -2. `bunx toktrack@2.4.0` -3. `npx --yes toktrack@2.4.0` +2. `bunx toktrack@2.5.0` +3. `npx --yes toktrack@2.5.0` ## Common Commands diff --git a/bun.lock b/bun.lock index 0def288..9ef913d 100644 --- a/bun.lock +++ b/bun.lock @@ -6,10 +6,10 @@ "name": "@roastcodes/ttdash", "dependencies": { "cross-spawn": "^7.0.6", - "i18next": "^26.0.3", - "react-i18next": "^17.0.3", - "react-is": "^19.2.4", - "toktrack": "2.4.0", + "i18next": "^26.0.6", + "react-i18next": "^17.0.4", + "react-is": "^19.2.5", + "toktrack": "2.5.0", }, "devDependencies": { "@eslint/js": "^9.39.4", @@ -20,13 +20,13 @@ "@radix-ui/react-toast": "^1.2.14", "@radix-ui/react-tooltip": "^1.2.3", "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-query": "^5.99.0", + "@tanstack/react-query": "^5.99.2", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", - "@vitest/coverage-v8": "^4.1.3", + "@vitest/coverage-v8": "^4.1.4", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", @@ -37,25 +37,25 @@ "eslint-plugin-jest-dom": "^5.5.0", "eslint-plugin-jsdoc": "^62.9.0", "eslint-plugin-jsx-a11y": "^6.10.2", - "eslint-plugin-playwright": "^2.10.1", + "eslint-plugin-playwright": "^2.10.2", "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-testing-library": "^7.16.2", "framer-motion": "^12.6.5", "globals": "^17.5.0", "jsdom": "^29.0.2", - "lucide-react": "^1.7.0", + "lucide-react": "^1.8.0", "prettier": "^3.8.2", "prettier-plugin-tailwindcss": "^0.7.2", - "react": "^19.2.4", - "react-dom": "^19.2.4", + "react": "^19.2.5", + "react-dom": "^19.2.5", "recharts": "^3.8.1", "tailwind-merge": "^3.0.2", "tailwindcss": "^4.1.3", - "typescript": "^6.0.2", + "typescript": "^6.0.3", "typescript-eslint": "^8.58.2", - "vite": "^8.0.8", - "vitest": "^4.1.3", + "vite": "^8.0.9", + "vitest": "^4.1.4", }, }, }, @@ -176,7 +176,7 @@ "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], - "@oxc-project/types": ["@oxc-project/types@0.124.0", "", {}, "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg=="], + "@oxc-project/types": ["@oxc-project/types@0.126.0", "", {}, "sha512-oGfVtjAgwQVVpfBrbtk4e1XDyWHRFta6BS3GWVzrF8xYBT2VGQAk39yJS/wFSMrZqoiCU4oghT3Ch0HaHGIHcQ=="], "@package-json/types": ["@package-json/types@0.0.12", "", {}, "sha512-uu43FGU34B5VM9mCNjXCwLaGHYjXdNincqKLaraaCW+7S2+SmiBg1Nv8bPnmschrIfZmfKNY9f3fC376MRrObw=="], @@ -244,35 +244,35 @@ "@reduxjs/toolkit": ["@reduxjs/toolkit@2.11.2", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@standard-schema/utils": "^0.3.0", "immer": "^11.0.0", "redux": "^5.0.1", "redux-thunk": "^3.1.0", "reselect": "^5.1.0" }, "peerDependencies": { "react": "^16.9.0 || ^17.0.0 || ^18 || ^19", "react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0" } }, "sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ=="], - "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.15", "", { "os": "android", "cpu": "arm64" }, "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA=="], + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.0.0-rc.16", "", { "os": "android", "cpu": "arm64" }, "sha512-rhY3k7Bsae9qQfOtph2Pm2jZEA+s8Gmjoz4hhmx70K9iMQ/ddeae+xhRQcM5IuVx5ry1+bGfkvMn7D6MJggVSA=="], - "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg=="], + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-rc.16", "", { "os": "darwin", "cpu": "arm64" }, "sha512-rNz0yK078yrNn3DrdgN+PKiMOW8HfQ92jQiXxwX8yW899ayV00MLVdaCNeVBhG/TbH3ouYVObo8/yrkiectkcQ=="], - "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw=="], + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-rc.16", "", { "os": "darwin", "cpu": "x64" }, "sha512-r/OmdR00HmD4i79Z//xO06uEPOq5hRXdhw7nzkxQxwSavs3PSHa1ijntdpOiZ2mzOQ3fVVu8C1M19FoNM+dMUQ=="], - "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.15", "", { "os": "freebsd", "cpu": "x64" }, "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw=="], + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-rc.16", "", { "os": "freebsd", "cpu": "x64" }, "sha512-KcRE5w8h0OnjUatG8pldyD14/CQ5Phs1oxfR+3pKDjboHRo9+MkqQaiIZlZRpsxC15paeXme/I127tUa9TXJ6g=="], - "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm" }, "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA=="], + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.16", "", { "os": "linux", "cpu": "arm" }, "sha512-bT0guA1bpxEJ/ZhTRniQf7rNF8ybvXOuWbNIeLABaV5NGjx4EtOWBTSRGWFU9ZWVkPOZ+HNFP8RMcBokBiZ0Kg=="], - "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w=="], + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-rc.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-+tHktCHWV8BDQSjemUqm/Jl/TPk3QObCTIjmdDy/nlupcujZghmKK2962LYrqFpWu+ai01AN/REOH3NEpqvYQg=="], - "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ=="], + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-rc.16", "", { "os": "linux", "cpu": "arm64" }, "sha512-3fPzdREH806oRLxpTWW1Gt4tQHs0TitZFOECB2xzCFLPKnSOy90gwA7P29cksYilFO6XVRY1kzga0cL2nRjKPg=="], - "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "ppc64" }, "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ=="], + "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.16", "", { "os": "linux", "cpu": "ppc64" }, "sha512-EKwI1tSrLs7YVw+JPJT/G2dJQ1jl9qlTTTEG0V2Ok/RdOenRfBw2PQdLPyjhIu58ocdBfP7vIRN/pvMsPxs/AQ=="], - "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "s390x" }, "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ=="], + "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.0.0-rc.16", "", { "os": "linux", "cpu": "s390x" }, "sha512-Uknladnb3Sxqu6SEcqBldQyJUpk8NleooZEc0MbRBJ4inEhRYWZX0NJu12vNf2mqAq7gsofAxHrGghiUYjhaLQ=="], - "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.15", "", { "os": "linux", "cpu": "x64" }, "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA=="], + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-rc.16", "", { "os": "linux", "cpu": "x64" }, "sha512-FIb8+uG49sZBtLTn+zt1AJ20TqVcqWeSIyoVt0or7uAWesgKaHbiBh6OpA/k9v0LTt+PTrb1Lao133kP4uVxkg=="], - "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.15", "", { "os": "linux", "cpu": "x64" }, "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw=="], + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-rc.16", "", { "os": "linux", "cpu": "x64" }, "sha512-RuERhF9/EgWxZEXYWCOaViUWHIboceK4/ivdtQ3R0T44NjLkIIlGIAVAuCddFxsZ7vnRHtNQUrt2vR2n2slB2w=="], - "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.15", "", { "os": "none", "cpu": "arm64" }, "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg=="], + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.0.0-rc.16", "", { "os": "none", "cpu": "arm64" }, "sha512-mXcXnvd9GpazCxeUCCnZ2+YF7nut+ZOEbE4GtaiPtyY6AkhZWbK70y1KK3j+RDhjVq5+U8FySkKRb/+w0EeUwA=="], - "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.15", "", { "dependencies": { "@emnapi/core": "1.9.2", "@emnapi/runtime": "1.9.2", "@napi-rs/wasm-runtime": "^1.1.3" }, "cpu": "none" }, "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q=="], + "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-rc.16", "", { "dependencies": { "@emnapi/core": "1.9.2", "@emnapi/runtime": "1.9.2", "@napi-rs/wasm-runtime": "^1.1.4" }, "cpu": "none" }, "sha512-3Q2KQxnC8IJOLqXmUMoYwyIPZU9hzRbnHaoV3Euz+VVnjZKcY8ktnNP8T9R4/GGQtb27C/UYKABxesKWb8lsvQ=="], - "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA=="], + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-rc.16", "", { "os": "win32", "cpu": "arm64" }, "sha512-tj7XRemQcOcFwv7qhpUxMTBbI5mWMlE4c1Omhg5+h8GuLXzyj8HviYgR+bB2DMDgRqUE+jiDleqSCRjx4aYk/Q=="], - "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.15", "", { "os": "win32", "cpu": "x64" }, "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g=="], + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-rc.16", "", { "os": "win32", "cpu": "x64" }, "sha512-PH5DRZT+F4f2PTXRXR8uJxnBq2po/xFtddyabTJVJs/ZYVHqXPEgNIr35IHTEa6bpa0Q8Awg+ymkTaGnKITw4g=="], "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.7", "", {}, "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA=="], @@ -312,9 +312,9 @@ "@tailwindcss/vite": ["@tailwindcss/vite@4.2.2", "", { "dependencies": { "@tailwindcss/node": "4.2.2", "@tailwindcss/oxide": "4.2.2", "tailwindcss": "4.2.2" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w=="], - "@tanstack/query-core": ["@tanstack/query-core@5.99.0", "", {}, "sha512-3Jv3WQG0BCcH7G+7lf/bP8QyBfJOXeY+T08Rin3GZ1bshvwlbPt7NrDHMEzGdKIOmOzvIQmxjk28YEQX60k7pQ=="], + "@tanstack/query-core": ["@tanstack/query-core@5.99.2", "", {}, "sha512-1HunU0bXVsR1ZJMZbcOPE6VtaBJxsW809RE9xPe4Gz7MlB0GWwQvuTPhMoEmQ/hIzFKJ/DWAuttIe7BOaWx0tA=="], - "@tanstack/react-query": ["@tanstack/react-query@5.99.0", "", { "dependencies": { "@tanstack/query-core": "5.99.0" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-OY2bCqPemT1LlqJ8Y2CUau4KELnIhhG9Ol3ZndPbdnB095pRbPo1cHuXTndg8iIwtoHTgwZjyaDnQ0xD0mYwAw=="], + "@tanstack/react-query": ["@tanstack/react-query@5.99.2", "", { "dependencies": { "@tanstack/query-core": "5.99.2" }, "peerDependencies": { "react": "^18 || ^19" } }, "sha512-vM91UEe45QUS9ED6OklsVL15i8qKcRqNwpWzPTVWvRPRSEgDudDgHpvyTjcdlwHcrKNa80T+xXYcchT2noPnZA=="], "@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="], @@ -418,21 +418,21 @@ "@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.1", "", { "dependencies": { "@rolldown/pluginutils": "1.0.0-rc.7" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ=="], - "@vitest/coverage-v8": ["@vitest/coverage-v8@4.1.3", "", { "dependencies": { "@bcoe/v8-coverage": "^1.0.2", "@vitest/utils": "4.1.3", "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" }, "peerDependencies": { "@vitest/browser": "4.1.3", "vitest": "4.1.3" }, "optionalPeers": ["@vitest/browser"] }, "sha512-/MBdrkA8t6hbdCWFKs09dPik774xvs4Z6L4bycdCxYNLHM8oZuRyosumQMG19LUlBsB6GeVpL1q4kFFazvyKGA=="], + "@vitest/coverage-v8": ["@vitest/coverage-v8@4.1.4", "", { "dependencies": { "@bcoe/v8-coverage": "^1.0.2", "@vitest/utils": "4.1.4", "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" }, "peerDependencies": { "@vitest/browser": "4.1.4", "vitest": "4.1.4" }, "optionalPeers": ["@vitest/browser"] }, "sha512-x7FptB5oDruxNPDNY2+S8tCh0pcq7ymCe1gTHcsp733jYjrJl8V1gMUlVysuCD9Kz46Xz9t1akkv08dPcYDs1w=="], - "@vitest/expect": ["@vitest/expect@4.1.3", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.3", "@vitest/utils": "4.1.3", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-CW8Q9KMtXDGHj0vCsqui0M5KqRsu0zm0GNDW7Gd3U7nZ2RFpPKSCpeCXoT+/+5zr1TNlsoQRDEz+LzZUyq6gnQ=="], + "@vitest/expect": ["@vitest/expect@4.1.4", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.4", "@vitest/utils": "4.1.4", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-iPBpra+VDuXmBFI3FMKHSFXp3Gx5HfmSCE8X67Dn+bwephCnQCaB7qWK2ldHa+8ncN8hJU8VTMcxjPpyMkUjww=="], - "@vitest/mocker": ["@vitest/mocker@4.1.3", "", { "dependencies": { "@vitest/spy": "4.1.3", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-XN3TrycitDQSzGRnec/YWgoofkYRhouyVQj4YNsJ5r/STCUFqMrP4+oxEv3e7ZbLi4og5kIHrZwekDJgw6hcjw=="], + "@vitest/mocker": ["@vitest/mocker@4.1.4", "", { "dependencies": { "@vitest/spy": "4.1.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-R9HTZBhW6yCSGbGQnDnH3QHfJxokKN4KB+Yvk9Q1le7eQNYwiCyKxmLmurSpFy6BzJanSLuEUDrD+j97Q+ZLPg=="], - "@vitest/pretty-format": ["@vitest/pretty-format@4.1.3", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-hYqqwuMbpkkBodpRh4k4cQSOELxXky1NfMmQvOfKvV8zQHz8x8Dla+2wzElkMkBvSAJX5TRGHJAQvK0TcOafwg=="], + "@vitest/pretty-format": ["@vitest/pretty-format@4.1.4", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-ddmDHU0gjEUyEVLxtZa7xamrpIefdEETu3nZjWtHeZX4QxqJ7tRxSteHVXJOcr8jhiLoGAhkK4WJ3WqBpjx42A=="], - "@vitest/runner": ["@vitest/runner@4.1.3", "", { "dependencies": { "@vitest/utils": "4.1.3", "pathe": "^2.0.3" } }, "sha512-VwgOz5MmT0KhlUj40h02LWDpUBVpflZ/b7xZFA25F29AJzIrE+SMuwzFf0b7t4EXdwRNX61C3B6auIXQTR3ttA=="], + "@vitest/runner": ["@vitest/runner@4.1.4", "", { "dependencies": { "@vitest/utils": "4.1.4", "pathe": "^2.0.3" } }, "sha512-xTp7VZ5aXP5ZJrn15UtJUWlx6qXLnGtF6jNxHepdPHpMfz/aVPx+htHtgcAL2mDXJgKhpoo2e9/hVJsIeFbytQ=="], - "@vitest/snapshot": ["@vitest/snapshot@4.1.3", "", { "dependencies": { "@vitest/pretty-format": "4.1.3", "@vitest/utils": "4.1.3", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-9l+k/J9KG5wPJDX9BcFFzhhwNjwkRb8RsnYhaT1vPY7OufxmQFc9sZzScRCPTiETzl37mrIWVY9zxzmdVeJwDQ=="], + "@vitest/snapshot": ["@vitest/snapshot@4.1.4", "", { "dependencies": { "@vitest/pretty-format": "4.1.4", "@vitest/utils": "4.1.4", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-MCjCFgaS8aZz+m5nTcEcgk/xhWv0rEH4Yl53PPlMXOZ1/Ka2VcZU6CJ+MgYCZbcJvzGhQRjVrGQNZqkGPttIKw=="], - "@vitest/spy": ["@vitest/spy@4.1.3", "", {}, "sha512-ujj5Uwxagg4XUIfAUyRQxAg631BP6e9joRiN99mr48Bg9fRs+5mdUElhOoZ6rP5mBr8Bs3lmrREnkrQWkrsTCw=="], + "@vitest/spy": ["@vitest/spy@4.1.4", "", {}, "sha512-XxNdAsKW7C+FLydqFJLb5KhJtl3PGCMmYwFRfhvIgxJvLSXhhVI1zM8f1qD3Zg7RCjTSzDVyct6sghs9UEgBEQ=="], - "@vitest/utils": ["@vitest/utils@4.1.3", "", { "dependencies": { "@vitest/pretty-format": "4.1.3", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-Pc/Oexse/khOWsGB+w3q4yzA4te7W4gpZZAvk+fr8qXfTURZUMj5i7kuxsNK5mP/dEB6ao3jfr0rs17fHhbHdw=="], + "@vitest/utils": ["@vitest/utils@4.1.4", "", { "dependencies": { "@vitest/pretty-format": "4.1.4", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-13QMT+eysM5uVGa1rG4kegGYNp6cnQcsTc67ELFbhNLQO+vgsygtYJx2khvdt4gVQqSSpC/KT5FZZxUpP3Oatw=="], "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], @@ -632,11 +632,11 @@ "eslint-plugin-jsx-a11y": ["eslint-plugin-jsx-a11y@6.10.2", "", { "dependencies": { "aria-query": "^5.3.2", "array-includes": "^3.1.8", "array.prototype.flatmap": "^1.3.2", "ast-types-flow": "^0.0.8", "axe-core": "^4.10.0", "axobject-query": "^4.1.0", "damerau-levenshtein": "^1.0.8", "emoji-regex": "^9.2.2", "hasown": "^2.0.2", "jsx-ast-utils": "^3.3.5", "language-tags": "^1.0.9", "minimatch": "^3.1.2", "object.fromentries": "^2.0.8", "safe-regex-test": "^1.0.3", "string.prototype.includes": "^2.0.1" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" } }, "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q=="], - "eslint-plugin-playwright": ["eslint-plugin-playwright@2.10.1", "", { "dependencies": { "globals": "^17.3.0" }, "peerDependencies": { "eslint": ">=8.40.0" } }, "sha512-qea3UxBOb8fTwJ77FMApZKvRye5DOluDHcev0LDJwID3RELeun0JlqzrNIXAB/SXCyB/AesCW/6sZfcT9q3Edg=="], + "eslint-plugin-playwright": ["eslint-plugin-playwright@2.10.2", "", { "dependencies": { "globals": "^17.3.0" }, "peerDependencies": { "eslint": ">=8.40.0" } }, "sha512-0N+2OWc3NZbOZ0gK8mp2TK6Qu3UWcJTQ9rqU0UM2yRJXgT758pvpY0lsOLIySfbyFrLqn3TcXjixbmcK90VnuQ=="], "eslint-plugin-react": ["eslint-plugin-react@7.37.5", "", { "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", "array.prototype.flatmap": "^1.3.3", "array.prototype.tosorted": "^1.1.4", "doctrine": "^2.1.0", "es-iterator-helpers": "^1.2.1", "estraverse": "^5.3.0", "hasown": "^2.0.2", "jsx-ast-utils": "^2.4.1 || ^3.0.0", "minimatch": "^3.1.2", "object.entries": "^1.1.9", "object.fromentries": "^2.0.8", "object.values": "^1.2.1", "prop-types": "^15.8.1", "resolve": "^2.0.0-next.5", "semver": "^6.3.1", "string.prototype.matchall": "^4.0.12", "string.prototype.repeat": "^1.0.0" }, "peerDependencies": { "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" } }, "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA=="], - "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@7.0.1", "", { "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", "hermes-parser": "^0.25.1", "zod": "^3.25.0 || ^4.0.0", "zod-validation-error": "^3.5.0 || ^4.0.0" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" } }, "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA=="], + "eslint-plugin-react-hooks": ["eslint-plugin-react-hooks@7.1.1", "", { "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", "hermes-parser": "^0.25.1", "zod": "^3.25.0 || ^4.0.0", "zod-validation-error": "^3.5.0 || ^4.0.0" }, "peerDependencies": { "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" } }, "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g=="], "eslint-plugin-testing-library": ["eslint-plugin-testing-library@7.16.2", "", { "dependencies": { "@typescript-eslint/scope-manager": "^8.56.0", "@typescript-eslint/utils": "^8.56.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0" } }, "sha512-8gleGnQXK2ZA3hHwjCwpYTZvM+9VsrJ+/9kDI8CjqAQGAdMQOdn/rJNu7ZySENuiWlGKQWyZJ4ZjEg2zamaRHw=="], @@ -738,7 +738,7 @@ "html-parse-stringify": ["html-parse-stringify@3.0.1", "", { "dependencies": { "void-elements": "3.1.0" } }, "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg=="], - "i18next": ["i18next@26.0.3", "", { "dependencies": { "@babel/runtime": "^7.29.2" }, "peerDependencies": { "typescript": "^5 || ^6" }, "optionalPeers": ["typescript"] }, "sha512-1571kXINxHKY7LksWp8wP+zP0YqHSSpl/OW0Y0owFEf2H3s8gCAffWaZivcz14rMkOvn3R/psiQxVsR9t2Nafg=="], + "i18next": ["i18next@26.0.6", "", { "dependencies": { "@babel/runtime": "^7.29.2" }, "peerDependencies": { "typescript": "^5 || ^6" }, "optionalPeers": ["typescript"] }, "sha512-A4U6eCXodIbrhf8EarRurB9/4ebyaurH4+fu4gig9bqxmpSt+fCAFm/GpRQDcN1Xzu/LdFCx4nYHsnM1edIIbg=="], "ignore": ["ignore@5.3.2", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="], @@ -880,7 +880,7 @@ "lru-cache": ["lru-cache@11.3.3", "", {}, "sha512-JvNw9Y81y33E+BEYPr0U7omo+U9AySnsMsEiXgwT6yqd31VQWTLNQqmT4ou5eqPFUrTfIDFta2wKhB1hyohtAQ=="], - "lucide-react": ["lucide-react@1.7.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-yI7BeItCLZJTXikmK4KNUGCKoGzSvbKlfCvw44bU4fXAL6v3gYS4uHD1jzsLkfwODYwI6Drw5Tu9Z5ulDe0TSg=="], + "lucide-react": ["lucide-react@1.8.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-WuvlsjngSk7TnTBJ1hsCy3ql9V9VOdcPkd3PKcSmM34vJD8KG6molxz7m7zbYFgICwsanQWmJ13JlYs4Zp7Arw=="], "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], @@ -966,7 +966,7 @@ "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], - "postcss": ["postcss@8.5.8", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg=="], + "postcss": ["postcss@8.5.10", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ=="], "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], @@ -980,13 +980,13 @@ "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], - "react": ["react@19.2.4", "", {}, "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ=="], + "react": ["react@19.2.5", "", {}, "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA=="], - "react-dom": ["react-dom@19.2.4", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.4" } }, "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ=="], + "react-dom": ["react-dom@19.2.5", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.5" } }, "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag=="], - "react-i18next": ["react-i18next@17.0.3", "", { "dependencies": { "@babel/runtime": "^7.29.2", "html-parse-stringify": "^3.0.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "i18next": ">= 26.0.1", "react": ">= 16.8.0", "typescript": "^5 || ^6" }, "optionalPeers": ["typescript"] }, "sha512-x4xjvUNZ56T+zfXWNedNnCET9Xq1IBYWX7IsWo5cCQ/RT+Rm7GWqt0h9PShFi4IhyMnsdiu1C6Jc4DE+/S3PFQ=="], + "react-i18next": ["react-i18next@17.0.4", "", { "dependencies": { "@babel/runtime": "^7.29.2", "html-parse-stringify": "^3.0.1", "use-sync-external-store": "^1.6.0" }, "peerDependencies": { "i18next": ">= 26.0.1", "react": ">= 16.8.0", "typescript": "^5 || ^6" }, "optionalPeers": ["typescript"] }, "sha512-hQipmK4EF0y6RO6tt6WuqnmWpWYEXmQUUzecmMBuNsIgYd3smXcG4GtYPWhvgxn0pqMOItKlEO8H24HCs5hc3g=="], - "react-is": ["react-is@19.2.4", "", {}, "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA=="], + "react-is": ["react-is@19.2.5", "", {}, "sha512-Dn0t8IQhCmeIT3wu+Apm1/YVsJXsGWi6k4sPdnBIdqMVtHtv0IGi6dcpNpNkNac0zB2uUAqNX3MHzN8c+z2rwQ=="], "react-redux": ["react-redux@9.2.0", "", { "dependencies": { "@types/use-sync-external-store": "^0.0.6", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "@types/react": "^18.2.25 || ^19", "react": "^18.0 || ^19", "redux": "^5.0.0" } }, "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g=="], @@ -1022,7 +1022,7 @@ "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], - "rolldown": ["rolldown@1.0.0-rc.15", "", { "dependencies": { "@oxc-project/types": "=0.124.0", "@rolldown/pluginutils": "1.0.0-rc.15" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.15", "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", "@rolldown/binding-darwin-x64": "1.0.0-rc.15", "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g=="], + "rolldown": ["rolldown@1.0.0-rc.16", "", { "dependencies": { "@oxc-project/types": "=0.126.0", "@rolldown/pluginutils": "1.0.0-rc.16" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.16", "@rolldown/binding-darwin-arm64": "1.0.0-rc.16", "@rolldown/binding-darwin-x64": "1.0.0-rc.16", "@rolldown/binding-freebsd-x64": "1.0.0-rc.16", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.16", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.16", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.16", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.16", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.16", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.16", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.16", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.16", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.16", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.16", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.16" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-rzi5WqKzEZw3SooTt7cgm4eqIoujPIyGcJNGFL7iPEuajQw7vxMHUkXylu4/vhCkJGXsgRmxqMKXUpT6FEgl0g=="], "safe-array-concat": ["safe-array-concat@1.1.3", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "get-intrinsic": "^1.2.6", "has-symbols": "^1.1.0", "isarray": "^2.0.5" } }, "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q=="], @@ -1116,7 +1116,7 @@ "to-valid-identifier": ["to-valid-identifier@1.0.0", "", { "dependencies": { "@sindresorhus/base62": "^1.0.0", "reserved-identifiers": "^1.0.0" } }, "sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw=="], - "toktrack": ["toktrack@2.4.0", "", { "os": [ "linux", "win32", "darwin", ], "cpu": [ "x64", "arm64", ], "bin": { "toktrack": "bin/run.js" } }, "sha512-rKqMm/9vGgxnXRs/+VfSgxYRi7wdiFMCA4PubnPyloV06SpbrndIwTk90jYC64RmaOtCqfRp7yV/hRY3a9jciQ=="], + "toktrack": ["toktrack@2.5.0", "", { "os": [ "linux", "win32", "darwin", ], "cpu": [ "x64", "arm64", ], "bin": { "toktrack": "bin/run.js" } }, "sha512-WJYj86vF6uQ2NA3eBQZ7bzN9Idrn12i3u2vEqT7FkjuTAz6zaRXX1/Z1JjIyurc7OR8xfzE78PiONhCTl0T3jw=="], "tough-cookie": ["tough-cookie@6.0.1", "", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw=="], @@ -1136,7 +1136,7 @@ "typed-array-length": ["typed-array-length@1.0.7", "", { "dependencies": { "call-bind": "^1.0.7", "for-each": "^0.3.3", "gopd": "^1.0.1", "is-typed-array": "^1.1.13", "possible-typed-array-names": "^1.0.0", "reflect.getprototypeof": "^1.0.6" } }, "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg=="], - "typescript": ["typescript@6.0.2", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ=="], + "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], "typescript-eslint": ["typescript-eslint@8.58.2", "", { "dependencies": { "@typescript-eslint/eslint-plugin": "8.58.2", "@typescript-eslint/parser": "8.58.2", "@typescript-eslint/typescript-estree": "8.58.2", "@typescript-eslint/utils": "8.58.2" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } }, "sha512-V8iSng9mRbdZjl54VJ9NKr6ZB+dW0J3TzRXRGcSbLIej9jV86ZRtlYeTKDR/QLxXykocJ5icNzbsl2+5TzIvcQ=="], @@ -1158,9 +1158,9 @@ "victory-vendor": ["victory-vendor@37.3.6", "", { "dependencies": { "@types/d3-array": "^3.0.3", "@types/d3-ease": "^3.0.0", "@types/d3-interpolate": "^3.0.1", "@types/d3-scale": "^4.0.2", "@types/d3-shape": "^3.1.0", "@types/d3-time": "^3.0.0", "@types/d3-timer": "^3.0.0", "d3-array": "^3.1.6", "d3-ease": "^3.0.1", "d3-interpolate": "^3.0.1", "d3-scale": "^4.0.2", "d3-shape": "^3.1.0", "d3-time": "^3.0.0", "d3-timer": "^3.0.1" } }, "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ=="], - "vite": ["vite@8.0.8", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.15", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw=="], + "vite": ["vite@8.0.9", "", { "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.10", "rolldown": "1.0.0-rc.16", "tinyglobby": "^0.2.16" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-t7g7GVRpMXjNpa67HaVWI/8BWtdVIQPCL2WoozXXA7LBGEFK4AkkKkHx2hAQf5x1GZSlcmEDPkVLSGahxnEEZw=="], - "vitest": ["vitest@4.1.3", "", { "dependencies": { "@vitest/expect": "4.1.3", "@vitest/mocker": "4.1.3", "@vitest/pretty-format": "4.1.3", "@vitest/runner": "4.1.3", "@vitest/snapshot": "4.1.3", "@vitest/spy": "4.1.3", "@vitest/utils": "4.1.3", "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" }, "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.3", "@vitest/browser-preview": "4.1.3", "@vitest/browser-webdriverio": "4.1.3", "@vitest/coverage-istanbul": "4.1.3", "@vitest/coverage-v8": "4.1.3", "@vitest/ui": "4.1.3", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-DBc4Tx0MPNsqb9isoyOq00lHftVx/KIU44QOm2q59npZyLUkENn8TMFsuzuO+4U2FUa9rgbbPt3udrP25GcjXw=="], + "vitest": ["vitest@4.1.4", "", { "dependencies": { "@vitest/expect": "4.1.4", "@vitest/mocker": "4.1.4", "@vitest/pretty-format": "4.1.4", "@vitest/runner": "4.1.4", "@vitest/snapshot": "4.1.4", "@vitest/spy": "4.1.4", "@vitest/utils": "4.1.4", "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" }, "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.4", "@vitest/browser-preview": "4.1.4", "@vitest/browser-webdriverio": "4.1.4", "@vitest/coverage-istanbul": "4.1.4", "@vitest/coverage-v8": "4.1.4", "@vitest/ui": "4.1.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-tFuJqTxKb8AvfyqMfnavXdzfy3h3sWZRWwfluGbkeR7n0HUev+FmNgZ8SDrRBTVrVCjgH5cA21qGbCffMNtWvg=="], "void-elements": ["void-elements@3.1.0", "", {}, "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w=="], @@ -1222,7 +1222,7 @@ "@reduxjs/toolkit/immer": ["immer@11.1.4", "", {}, "sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw=="], - "@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.3", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ=="], + "@rolldown/binding-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], "@testing-library/dom/aria-query": ["aria-query@5.3.0", "", { "dependencies": { "dequal": "^2.0.3" } }, "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A=="], @@ -1252,7 +1252,9 @@ "prop-types/react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], - "rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.15", "", {}, "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g=="], + "rolldown/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.16", "", {}, "sha512-45+YtqxLYKDWQouLKCrpIZhke+nXxhsw+qAHVzHDVwttyBlHNBVs2K25rDXrZzhpTp9w1FlAlvweV1H++fdZoA=="], + + "vite/tinyglobby": ["tinyglobby@0.2.16", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg=="], "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.5", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ=="], diff --git a/package-lock.json b/package-lock.json index d99f7e6..91b1c2a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,10 +10,10 @@ "license": "MIT", "dependencies": { "cross-spawn": "^7.0.6", - "i18next": "^26.0.3", - "react-i18next": "^17.0.3", - "react-is": "^19.2.4", - "toktrack": "2.4.0" + "i18next": "^26.0.6", + "react-i18next": "^17.0.4", + "react-is": "^19.2.5", + "toktrack": "2.5.0" }, "bin": { "ttdash": "server.js" @@ -27,13 +27,13 @@ "@radix-ui/react-toast": "^1.2.14", "@radix-ui/react-tooltip": "^1.2.3", "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-query": "^5.99.0", + "@tanstack/react-query": "^5.99.2", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", - "@vitest/coverage-v8": "^4.1.3", + "@vitest/coverage-v8": "^4.1.4", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", @@ -44,25 +44,25 @@ "eslint-plugin-jest-dom": "^5.5.0", "eslint-plugin-jsdoc": "^62.9.0", "eslint-plugin-jsx-a11y": "^6.10.2", - "eslint-plugin-playwright": "^2.10.1", + "eslint-plugin-playwright": "^2.10.2", "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-testing-library": "^7.16.2", "framer-motion": "^12.6.5", "globals": "^17.5.0", "jsdom": "^29.0.2", - "lucide-react": "^1.7.0", + "lucide-react": "^1.8.0", "prettier": "^3.8.2", "prettier-plugin-tailwindcss": "^0.7.2", - "react": "^19.2.4", - "react-dom": "^19.2.4", + "react": "^19.2.5", + "react-dom": "^19.2.5", "recharts": "^3.8.1", "tailwind-merge": "^3.0.2", "tailwindcss": "^4.1.3", - "typescript": "^6.0.2", + "typescript": "^6.0.3", "typescript-eslint": "^8.58.2", - "vite": "^8.0.8", - "vitest": "^4.1.3" + "vite": "^8.0.9", + "vitest": "^4.1.4" }, "engines": { "node": ">=20" @@ -937,9 +937,9 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.3.tgz", - "integrity": "sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", "dev": true, "license": "MIT", "optional": true, @@ -956,9 +956,9 @@ } }, "node_modules/@oxc-project/types": { - "version": "0.124.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.124.0.tgz", - "integrity": "sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==", + "version": "0.126.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.126.0.tgz", + "integrity": "sha512-oGfVtjAgwQVVpfBrbtk4e1XDyWHRFta6BS3GWVzrF8xYBT2VGQAk39yJS/wFSMrZqoiCU4oghT3Ch0HaHGIHcQ==", "dev": true, "license": "MIT", "funding": { @@ -1776,9 +1776,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.15.tgz", - "integrity": "sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.16.tgz", + "integrity": "sha512-rhY3k7Bsae9qQfOtph2Pm2jZEA+s8Gmjoz4hhmx70K9iMQ/ddeae+xhRQcM5IuVx5ry1+bGfkvMn7D6MJggVSA==", "cpu": [ "arm64" ], @@ -1793,9 +1793,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.15.tgz", - "integrity": "sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.16.tgz", + "integrity": "sha512-rNz0yK078yrNn3DrdgN+PKiMOW8HfQ92jQiXxwX8yW899ayV00MLVdaCNeVBhG/TbH3ouYVObo8/yrkiectkcQ==", "cpu": [ "arm64" ], @@ -1810,9 +1810,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.15.tgz", - "integrity": "sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.16.tgz", + "integrity": "sha512-r/OmdR00HmD4i79Z//xO06uEPOq5hRXdhw7nzkxQxwSavs3PSHa1ijntdpOiZ2mzOQ3fVVu8C1M19FoNM+dMUQ==", "cpu": [ "x64" ], @@ -1827,9 +1827,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.15.tgz", - "integrity": "sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.16.tgz", + "integrity": "sha512-KcRE5w8h0OnjUatG8pldyD14/CQ5Phs1oxfR+3pKDjboHRo9+MkqQaiIZlZRpsxC15paeXme/I127tUa9TXJ6g==", "cpu": [ "x64" ], @@ -1844,9 +1844,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.15.tgz", - "integrity": "sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.16.tgz", + "integrity": "sha512-bT0guA1bpxEJ/ZhTRniQf7rNF8ybvXOuWbNIeLABaV5NGjx4EtOWBTSRGWFU9ZWVkPOZ+HNFP8RMcBokBiZ0Kg==", "cpu": [ "arm" ], @@ -1861,13 +1861,16 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.16.tgz", + "integrity": "sha512-+tHktCHWV8BDQSjemUqm/Jl/TPk3QObCTIjmdDy/nlupcujZghmKK2962LYrqFpWu+ai01AN/REOH3NEpqvYQg==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1878,13 +1881,16 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.15.tgz", - "integrity": "sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.16.tgz", + "integrity": "sha512-3fPzdREH806oRLxpTWW1Gt4tQHs0TitZFOECB2xzCFLPKnSOy90gwA7P29cksYilFO6XVRY1kzga0cL2nRjKPg==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1895,13 +1901,16 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.16.tgz", + "integrity": "sha512-EKwI1tSrLs7YVw+JPJT/G2dJQ1jl9qlTTTEG0V2Ok/RdOenRfBw2PQdLPyjhIu58ocdBfP7vIRN/pvMsPxs/AQ==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1912,13 +1921,16 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.16.tgz", + "integrity": "sha512-Uknladnb3Sxqu6SEcqBldQyJUpk8NleooZEc0MbRBJ4inEhRYWZX0NJu12vNf2mqAq7gsofAxHrGghiUYjhaLQ==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1929,13 +1941,16 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.15.tgz", - "integrity": "sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.16.tgz", + "integrity": "sha512-FIb8+uG49sZBtLTn+zt1AJ20TqVcqWeSIyoVt0or7uAWesgKaHbiBh6OpA/k9v0LTt+PTrb1Lao133kP4uVxkg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1946,13 +1961,16 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.15.tgz", - "integrity": "sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.16.tgz", + "integrity": "sha512-RuERhF9/EgWxZEXYWCOaViUWHIboceK4/ivdtQ3R0T44NjLkIIlGIAVAuCddFxsZ7vnRHtNQUrt2vR2n2slB2w==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1963,9 +1981,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.15.tgz", - "integrity": "sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.16.tgz", + "integrity": "sha512-mXcXnvd9GpazCxeUCCnZ2+YF7nut+ZOEbE4GtaiPtyY6AkhZWbK70y1KK3j+RDhjVq5+U8FySkKRb/+w0EeUwA==", "cpu": [ "arm64" ], @@ -1980,9 +1998,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.15.tgz", - "integrity": "sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.16.tgz", + "integrity": "sha512-3Q2KQxnC8IJOLqXmUMoYwyIPZU9hzRbnHaoV3Euz+VVnjZKcY8ktnNP8T9R4/GGQtb27C/UYKABxesKWb8lsvQ==", "cpu": [ "wasm32" ], @@ -1992,16 +2010,16 @@ "dependencies": { "@emnapi/core": "1.9.2", "@emnapi/runtime": "1.9.2", - "@napi-rs/wasm-runtime": "^1.1.3" + "@napi-rs/wasm-runtime": "^1.1.4" }, "engines": { - "node": ">=14.0.0" + "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.15.tgz", - "integrity": "sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.16.tgz", + "integrity": "sha512-tj7XRemQcOcFwv7qhpUxMTBbI5mWMlE4c1Omhg5+h8GuLXzyj8HviYgR+bB2DMDgRqUE+jiDleqSCRjx4aYk/Q==", "cpu": [ "arm64" ], @@ -2016,9 +2034,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.15.tgz", - "integrity": "sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.16.tgz", + "integrity": "sha512-PH5DRZT+F4f2PTXRXR8uJxnBq2po/xFtddyabTJVJs/ZYVHqXPEgNIr35IHTEa6bpa0Q8Awg+ymkTaGnKITw4g==", "cpu": [ "x64" ], @@ -2339,9 +2357,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.99.0", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.99.0.tgz", - "integrity": "sha512-3Jv3WQG0BCcH7G+7lf/bP8QyBfJOXeY+T08Rin3GZ1bshvwlbPt7NrDHMEzGdKIOmOzvIQmxjk28YEQX60k7pQ==", + "version": "5.99.2", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.99.2.tgz", + "integrity": "sha512-1HunU0bXVsR1ZJMZbcOPE6VtaBJxsW809RE9xPe4Gz7MlB0GWwQvuTPhMoEmQ/hIzFKJ/DWAuttIe7BOaWx0tA==", "dev": true, "license": "MIT", "funding": { @@ -2350,13 +2368,13 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.99.0", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.99.0.tgz", - "integrity": "sha512-OY2bCqPemT1LlqJ8Y2CUau4KELnIhhG9Ol3ZndPbdnB095pRbPo1cHuXTndg8iIwtoHTgwZjyaDnQ0xD0mYwAw==", + "version": "5.99.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.99.2.tgz", + "integrity": "sha512-vM91UEe45QUS9ED6OklsVL15i8qKcRqNwpWzPTVWvRPRSEgDudDgHpvyTjcdlwHcrKNa80T+xXYcchT2noPnZA==", "dev": true, "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.99.0" + "@tanstack/query-core": "5.99.2" }, "funding": { "type": "github", @@ -4943,9 +4961,9 @@ } }, "node_modules/eslint-plugin-playwright": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-2.10.1.tgz", - "integrity": "sha512-qea3UxBOb8fTwJ77FMApZKvRye5DOluDHcev0LDJwID3RELeun0JlqzrNIXAB/SXCyB/AesCW/6sZfcT9q3Edg==", + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-playwright/-/eslint-plugin-playwright-2.10.2.tgz", + "integrity": "sha512-0N+2OWc3NZbOZ0gK8mp2TK6Qu3UWcJTQ9rqU0UM2yRJXgT758pvpY0lsOLIySfbyFrLqn3TcXjixbmcK90VnuQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4992,9 +5010,9 @@ } }, "node_modules/eslint-plugin-react-hooks": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", - "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", "dev": true, "license": "MIT", "dependencies": { @@ -5008,7 +5026,7 @@ "node": ">=18" }, "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" } }, "node_modules/eslint-plugin-react/node_modules/semver": { @@ -5670,9 +5688,9 @@ } }, "node_modules/i18next": { - "version": "26.0.4", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.0.4.tgz", - "integrity": "sha512-gXF7U9bfioXPLv7mw8Qt2nfO7vij5MyINvPgVv99pX3fL1Y01pw2mKBFrlYpRxRCl2wz3ISenj6VsMJT2isfuA==", + "version": "26.0.6", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.0.6.tgz", + "integrity": "sha512-A4U6eCXodIbrhf8EarRurB9/4ebyaurH4+fu4gig9bqxmpSt+fCAFm/GpRQDcN1Xzu/LdFCx4nYHsnM1edIIbg==", "funding": [ { "type": "individual", @@ -7296,9 +7314,9 @@ } }, "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", "dev": true, "funding": [ { @@ -7505,9 +7523,9 @@ } }, "node_modules/react-i18next": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-17.0.3.tgz", - "integrity": "sha512-x4xjvUNZ56T+zfXWNedNnCET9Xq1IBYWX7IsWo5cCQ/RT+Rm7GWqt0h9PShFi4IhyMnsdiu1C6Jc4DE+/S3PFQ==", + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-17.0.4.tgz", + "integrity": "sha512-hQipmK4EF0y6RO6tt6WuqnmWpWYEXmQUUzecmMBuNsIgYd3smXcG4GtYPWhvgxn0pqMOItKlEO8H24HCs5hc3g==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.29.2", @@ -7824,14 +7842,14 @@ } }, "node_modules/rolldown": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.15.tgz", - "integrity": "sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.16.tgz", + "integrity": "sha512-rzi5WqKzEZw3SooTt7cgm4eqIoujPIyGcJNGFL7iPEuajQw7vxMHUkXylu4/vhCkJGXsgRmxqMKXUpT6FEgl0g==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.124.0", - "@rolldown/pluginutils": "1.0.0-rc.15" + "@oxc-project/types": "=0.126.0", + "@rolldown/pluginutils": "1.0.0-rc.16" }, "bin": { "rolldown": "bin/cli.mjs" @@ -7840,27 +7858,27 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.15", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.15", - "@rolldown/binding-darwin-x64": "1.0.0-rc.15", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.15", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.15", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.15", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.15", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.15", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.15", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.15", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.15", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.15", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.15", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.15", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.15" + "@rolldown/binding-android-arm64": "1.0.0-rc.16", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.16", + "@rolldown/binding-darwin-x64": "1.0.0-rc.16", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.16", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.16", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.16", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.16", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.16", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.16", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.16", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.16", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.16", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.16", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.16", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.16" } }, "node_modules/rolldown/node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.15", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.15.tgz", - "integrity": "sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==", + "version": "1.0.0-rc.16", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.16.tgz", + "integrity": "sha512-45+YtqxLYKDWQouLKCrpIZhke+nXxhsw+qAHVzHDVwttyBlHNBVs2K25rDXrZzhpTp9w1FlAlvweV1H++fdZoA==", "dev": true, "license": "MIT" }, @@ -8407,14 +8425,14 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", - "picomatch": "^4.0.3" + "picomatch": "^4.0.4" }, "engines": { "node": ">=12.0.0" @@ -8471,9 +8489,9 @@ } }, "node_modules/toktrack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/toktrack/-/toktrack-2.4.0.tgz", - "integrity": "sha512-rKqMm/9vGgxnXRs/+VfSgxYRi7wdiFMCA4PubnPyloV06SpbrndIwTk90jYC64RmaOtCqfRp7yV/hRY3a9jciQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/toktrack/-/toktrack-2.5.0.tgz", + "integrity": "sha512-WJYj86vF6uQ2NA3eBQZ7bzN9Idrn12i3u2vEqT7FkjuTAz6zaRXX1/Z1JjIyurc7OR8xfzE78PiONhCTl0T3jw==", "cpu": [ "x64", "arm64" @@ -8629,9 +8647,9 @@ } }, "node_modules/typescript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", - "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "devOptional": true, "license": "Apache-2.0", "bin": { @@ -8849,17 +8867,17 @@ } }, "node_modules/vite": { - "version": "8.0.8", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz", - "integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==", + "version": "8.0.9", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.9.tgz", + "integrity": "sha512-t7g7GVRpMXjNpa67HaVWI/8BWtdVIQPCL2WoozXXA7LBGEFK4AkkKkHx2hAQf5x1GZSlcmEDPkVLSGahxnEEZw==", "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", - "postcss": "^8.5.8", - "rolldown": "1.0.0-rc.15", - "tinyglobby": "^0.2.15" + "postcss": "^8.5.10", + "rolldown": "1.0.0-rc.16", + "tinyglobby": "^0.2.16" }, "bin": { "vite": "bin/vite.js" diff --git a/package.json b/package.json index c2f219f..ee98182 100644 --- a/package.json +++ b/package.json @@ -76,13 +76,13 @@ "@radix-ui/react-toast": "^1.2.14", "@radix-ui/react-tooltip": "^1.2.3", "@tailwindcss/vite": "^4.2.2", - "@tanstack/react-query": "^5.99.0", + "@tanstack/react-query": "^5.99.2", "@testing-library/jest-dom": "^6.9.1", "@testing-library/react": "^16.3.2", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.1", - "@vitest/coverage-v8": "^4.1.3", + "@vitest/coverage-v8": "^4.1.4", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", @@ -93,31 +93,31 @@ "eslint-plugin-jest-dom": "^5.5.0", "eslint-plugin-jsdoc": "^62.9.0", "eslint-plugin-jsx-a11y": "^6.10.2", - "eslint-plugin-playwright": "^2.10.1", + "eslint-plugin-playwright": "^2.10.2", "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-hooks": "^7.1.1", "eslint-plugin-testing-library": "^7.16.2", "framer-motion": "^12.6.5", "globals": "^17.5.0", "jsdom": "^29.0.2", - "lucide-react": "^1.7.0", + "lucide-react": "^1.8.0", "prettier": "^3.8.2", "prettier-plugin-tailwindcss": "^0.7.2", - "react": "^19.2.4", - "react-dom": "^19.2.4", + "react": "^19.2.5", + "react-dom": "^19.2.5", "recharts": "^3.8.1", "tailwind-merge": "^3.0.2", "tailwindcss": "^4.1.3", - "typescript": "^6.0.2", + "typescript": "^6.0.3", "typescript-eslint": "^8.58.2", - "vite": "^8.0.8", - "vitest": "^4.1.3" + "vite": "^8.0.9", + "vitest": "^4.1.4" }, "dependencies": { "cross-spawn": "^7.0.6", - "i18next": "^26.0.3", - "react-i18next": "^17.0.3", - "react-is": "^19.2.4", - "toktrack": "2.4.0" + "i18next": "^26.0.6", + "react-i18next": "^17.0.4", + "react-is": "^19.2.5", + "toktrack": "2.5.0" } } diff --git a/server.js b/server.js index 06e825a..d2fb122 100755 --- a/server.js +++ b/server.js @@ -43,12 +43,9 @@ const MAX_BODY_SIZE = 10 * 1024 * 1024; // 10 MB const IS_WINDOWS = process.platform === 'win32'; const SECURE_DIR_MODE = 0o700; const SECURE_FILE_MODE = 0o600; -const TOKTRACK_LOCAL_BIN = path.join( - ROOT, - 'node_modules', - '.bin', - IS_WINDOWS ? 'toktrack.cmd' : 'toktrack', -); +const TOKTRACK_LOCAL_BIN = + process.env.TTDASH_TOKTRACK_LOCAL_BIN || + path.join(ROOT, 'node_modules', '.bin', IS_WINDOWS ? 'toktrack.cmd' : 'toktrack'); const SECURITY_HEADERS = { 'X-Content-Type-Options': 'nosniff', 'Referrer-Policy': 'no-referrer', @@ -63,10 +60,16 @@ const USAGE_BACKUP_KIND = 'ttdash-usage-backup'; const IS_BACKGROUND_CHILD = process.env.TTDASH_BACKGROUND_CHILD === '1'; const FORCE_OPEN_BROWSER = process.env.TTDASH_FORCE_OPEN_BROWSER === '1'; const BACKGROUND_START_TIMEOUT_MS = 15000; -const TOKTRACK_RUNNER_PROBE_TIMEOUT_MS = 7000; -const TOKTRACK_LATEST_LOOKUP_TIMEOUT_MS = 7000; +const TOKTRACK_LOCAL_RUNNER_PROBE_TIMEOUT_MS = 7000; +const TOKTRACK_LOCAL_RUNNER_VERSION_CHECK_TIMEOUT_MS = 7000; +const TOKTRACK_LOCAL_RUNNER_IMPORT_TIMEOUT_MS = 60000; +const TOKTRACK_PACKAGE_RUNNER_PROBE_TIMEOUT_MS = 45000; +const TOKTRACK_PACKAGE_RUNNER_VERSION_CHECK_TIMEOUT_MS = 45000; +const TOKTRACK_PACKAGE_RUNNER_IMPORT_TIMEOUT_MS = 60000; +const TOKTRACK_LATEST_LOOKUP_TIMEOUT_MS = 15000; const TOKTRACK_LATEST_CACHE_SUCCESS_TTL_MS = 5 * 60 * 1000; const TOKTRACK_LATEST_CACHE_FAILURE_TTL_MS = 60 * 1000; +const PROCESS_TERMINATION_GRACE_MS = 1000; const DASHBOARD_DATE_PRESETS = dashboardPreferences.datePresets; const DASHBOARD_SECTION_IDS = dashboardPreferences.sectionDefinitions.map((section) => section.id); const DEFAULT_SETTINGS = { @@ -560,12 +563,12 @@ function isProcessRunning(pid) { } } -async function fetchRuntimeIdentity(url, timeoutMs = 1000) { +async function fetchRuntimeIdentity(url, apiPrefix = API_PREFIX, timeoutMs = 1000) { if (typeof url !== 'string' || !url.trim()) { return null; } - const runtimePath = `${API_PREFIX.replace(/\/+$/, '')}/runtime`; + const runtimePath = `${String(apiPrefix || API_PREFIX).replace(/\/+$/, '')}/runtime`; const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), timeoutMs); @@ -600,14 +603,12 @@ async function isBackgroundInstanceOwned(instance) { return false; } - const runtime = await fetchRuntimeIdentity(instance.url); + const runtime = await fetchRuntimeIdentity(instance.url, instance.apiPrefix); if (!runtime || typeof runtime.id !== 'string') { return false; } - return ( - runtime.id === instance.id && runtime.pid === instance.pid && runtime.port === instance.port - ); + return runtime.id === instance.id && runtime.port === instance.port; } function normalizeBackgroundInstance(value) { @@ -621,6 +622,10 @@ function normalizeBackgroundInstance(value) { const id = typeof value.id === 'string' && value.id.trim() ? value.id.trim() : null; const url = typeof value.url === 'string' && value.url.trim() ? value.url.trim() : null; const host = typeof value.host === 'string' && value.host.trim() ? value.host.trim() : BIND_HOST; + const apiPrefix = + typeof value.apiPrefix === 'string' && value.apiPrefix.trim() + ? value.apiPrefix.trim() + : API_PREFIX; if ( !id || @@ -640,6 +645,7 @@ function normalizeBackgroundInstance(value) { port, url, host, + apiPrefix, startedAt, logFile: typeof value.logFile === 'string' && value.logFile.trim() ? value.logFile.trim() : null, @@ -789,6 +795,7 @@ function createBackgroundInstance({ port, url }) { port, url, host: BIND_HOST, + apiPrefix: API_PREFIX, startedAt: RUNTIME_INSTANCE.startedAt, logFile: process.env.TTDASH_BACKGROUND_LOG_FILE || null, }; @@ -1146,6 +1153,18 @@ function createAutoImportError(message, key, vars = {}) { return error; } +function summarizeCommandError(error, fallbackMessage = 'Unknown error') { + if (error instanceof Error && error.message.trim()) { + return error.message.trim(); + } + + return fallbackMessage; +} + +function getTimeoutSeconds(timeoutMs) { + return Math.max(1, Math.ceil(Number(timeoutMs) / 1000)); +} + function toAutoImportErrorEvent(error) { if (error && typeof error.messageKey === 'string') { return createAutoImportMessageEvent(error.messageKey, error.messageVars || {}); @@ -1159,7 +1178,9 @@ function toAutoImportErrorEvent(error) { function formatAutoImportMessageEvent(event) { switch (event?.key) { case 'startingLocalImport': - return 'Starting local toktrack import...'; + return 'Starting toktrack import...'; + case 'warmingUpPackageRunner': + return `Preparing ${event.vars?.runner || 'package runner'} (the first run may take longer while toktrack is downloaded)...`; case 'loadingUsageData': return `Loading usage data via ${event.vars?.command || 'unknown command'}...`; case 'processingUsageData': @@ -1168,6 +1189,24 @@ function formatAutoImportMessageEvent(event) { return 'An auto-import is already running. Please wait.'; case 'noRunnerFound': return 'No local toktrack, Bun, or npm exec installation found.'; + case 'localToktrackVersionMismatch': + return `Local toktrack v${event.vars?.detectedVersion || 'unknown'} does not match the required v${event.vars?.expectedVersion || TOKTRACK_VERSION}.`; + case 'localToktrackFailed': + return `Local toktrack could not be started: ${event.vars?.message || 'Unknown error'}`; + case 'packageRunnerFailed': + return `No compatible bunx or npm exec runner succeeded: ${event.vars?.message || 'Unknown error'}`; + case 'packageRunnerWarmupTimedOut': + return `${event.vars?.runner || 'The package runner'} took longer than ${event.vars?.seconds || 0}s to prepare toktrack. The first run may need to download the package first. Please try again or verify network access.`; + case 'toktrackVersionCheckFailed': + return `Toktrack was found, but the version check failed: ${event.vars?.message || 'Unknown error'}`; + case 'toktrackExecutionFailed': + return `Toktrack failed while loading usage data: ${event.vars?.message || 'Unknown error'}`; + case 'toktrackExecutionTimedOut': + return `Toktrack did not finish loading usage data within ${event.vars?.seconds || 0}s via ${event.vars?.runner || 'the selected runner'}. Please try again.`; + case 'toktrackInvalidJson': + return `Toktrack returned invalid JSON output: ${event.vars?.message || 'Unknown error'}`; + case 'toktrackInvalidData': + return `Toktrack returned data that TTDash could not process: ${event.vars?.message || 'Unknown error'}`; case 'errorPrefix': return `Error: ${event.vars?.message || 'Unknown error'}`; default: @@ -1566,39 +1605,59 @@ function getCacheControl(filePath) { return 'public, max-age=86400'; } +function writeStaticErrorResponse(res, status, message) { + res.writeHead(status, { + 'Content-Type': 'application/json; charset=utf-8', + ...SECURITY_HEADERS, + }); + res.end(JSON.stringify({ message })); +} + function serveFile(res, reqPath) { const ext = path.extname(reqPath).toLowerCase(); const contentType = MIME_TYPES[ext] || 'application/octet-stream'; - fs.readFile(reqPath, (err, data) => { - if (err) { - if (err.code === 'ENOENT') { - fs.readFile(path.join(STATIC_ROOT, 'index.html'), (err2, html) => { - if (err2) { - res.writeHead(500); - res.end('Internal Server Error'); - return; - } - res.writeHead(200, { - 'Content-Type': 'text/html; charset=utf-8', - 'Cache-Control': 'no-cache', - ...SECURITY_HEADERS, + try { + fs.readFile(reqPath, (err, data) => { + if (err) { + if (err.code === 'ENOENT') { + fs.readFile(path.join(STATIC_ROOT, 'index.html'), (err2, html) => { + if (err2) { + writeStaticErrorResponse(res, 500, 'Internal Server Error'); + return; + } + res.writeHead(200, { + 'Content-Type': 'text/html; charset=utf-8', + 'Cache-Control': 'no-cache', + ...SECURITY_HEADERS, + }); + res.end(html); }); - res.end(html); - }); + return; + } + writeStaticErrorResponse( + res, + err.code === 'ERR_INVALID_ARG_VALUE' ? 400 : 500, + err.code === 'ERR_INVALID_ARG_VALUE' ? 'Invalid request path' : 'Internal Server Error', + ); return; } - res.writeHead(500); - res.end('Internal Server Error'); - return; - } - res.writeHead(200, { - 'Content-Type': contentType, - 'Cache-Control': getCacheControl(reqPath), - ...SECURITY_HEADERS, + res.writeHead(200, { + 'Content-Type': contentType, + 'Cache-Control': getCacheControl(reqPath), + ...SECURITY_HEADERS, + }); + res.end(data); }); - res.end(data); - }); + } catch (error) { + writeStaticErrorResponse( + res, + error && error.code === 'ERR_INVALID_ARG_VALUE' ? 400 : 500, + error && error.code === 'ERR_INVALID_ARG_VALUE' + ? 'Invalid request path' + : 'Internal Server Error', + ); + } } // --- API helpers --- @@ -1685,11 +1744,13 @@ async function updateSettings(patch) { }); } -const { json, readBody, resolveApiPath, sendBuffer, validateMutationRequest } = createHttpUtils({ - apiPrefix: API_PREFIX, - maxBodySize: MAX_BODY_SIZE, - securityHeaders: SECURITY_HEADERS, -}); +const { json, readBody, resolveApiPath, sendBuffer, validateMutationRequest, validateRequestHost } = + createHttpUtils({ + apiPrefix: API_PREFIX, + maxBodySize: MAX_BODY_SIZE, + securityHeaders: SECURITY_HEADERS, + bindHost: BIND_HOST, + }); // --- SSE helpers --- @@ -1698,6 +1759,7 @@ function sendSSE(res, event, data) { } let autoImportRunning = false; +let autoImportStreamRunning = false; let latestToktrackVersionCache = null; let latestToktrackVersionLookupPromise = null; @@ -1749,10 +1811,20 @@ function createLocalToktrackRunner() { env: process.env, method: 'local', label: 'local toktrack', - displayCommand: 'node_modules/.bin/toktrack daily --json', + displayCommand: getLocalToktrackDisplayCommand(), }; } +function getLocalToktrackDisplayCommand(isWindows = IS_WINDOWS) { + if (process.env.TTDASH_TOKTRACK_LOCAL_BIN) { + return `${TOKTRACK_LOCAL_BIN} daily --json`; + } + + return isWindows + ? 'node_modules\\.bin\\toktrack.cmd daily --json' + : 'node_modules/.bin/toktrack daily --json'; +} + function createBunxToktrackRunner() { return { command: getExecutableName('bunx'), @@ -1778,21 +1850,101 @@ function createNpxToktrackRunner() { }; } +function isPackageToktrackRunner(runner) { + return runner?.method === 'bunx' || runner?.method === 'npm'; +} + +function getToktrackRunnerTimeouts(runner) { + if (isPackageToktrackRunner(runner)) { + return { + probeMs: TOKTRACK_PACKAGE_RUNNER_PROBE_TIMEOUT_MS, + versionCheckMs: TOKTRACK_PACKAGE_RUNNER_VERSION_CHECK_TIMEOUT_MS, + importMs: TOKTRACK_PACKAGE_RUNNER_IMPORT_TIMEOUT_MS, + }; + } + + return { + probeMs: TOKTRACK_LOCAL_RUNNER_PROBE_TIMEOUT_MS, + versionCheckMs: TOKTRACK_LOCAL_RUNNER_VERSION_CHECK_TIMEOUT_MS, + importMs: TOKTRACK_LOCAL_RUNNER_IMPORT_TIMEOUT_MS, + }; +} + +function formatCommandForDisplay(command, args = []) { + return [command, ...args].join(' ').trim(); +} + +function createCommandError( + message, + { command, args = [], stdout = '', stderr = '', exitCode = null, timedOut = false } = {}, +) { + const error = new Error(message); + error.command = command; + error.args = args; + error.stdout = stdout; + error.stderr = stderr; + error.exitCode = exitCode; + error.timedOut = timedOut; + return error; +} + +function terminateChildProcess(child) { + if (!child || child.exitCode !== null) { + return; + } + + child.kill('SIGTERM'); + + const forceKillTimeout = setTimeout(() => { + if (child.exitCode === null) { + child.kill('SIGKILL'); + } + }, PROCESS_TERMINATION_GRACE_MS); + + child.once('close', () => { + clearTimeout(forceKillTimeout); + }); +} + function runCommand( command, args, { env = process.env, streamStderr = false, onStderr, signalOnClose, timeoutMs = null } = {}, +) { + return runCommandWithSpawn(command, args, { + env, + streamStderr, + onStderr, + signalOnClose, + timeoutMs, + spawnImpl: spawnCommand, + }); +} + +function runCommandWithSpawn( + command, + args, + { + env = process.env, + streamStderr = false, + onStderr, + signalOnClose, + timeoutMs = null, + spawnImpl = spawnCommand, + } = {}, ) { return new Promise((resolve, reject) => { - const child = spawnCommand(command, args, { + const child = spawnImpl(command, args, { stdio: ['ignore', 'pipe', 'pipe'], env, }); + const commandLabel = formatCommandForDisplay(command, args); let stdout = ''; let stderr = ''; let finished = false; let timeoutId = null; + let timeoutError = null; const settle = (handler, value) => { if (finished) { @@ -1806,13 +1958,22 @@ function runCommand( }; if (signalOnClose) { - signalOnClose(() => child.kill('SIGTERM')); + signalOnClose(() => terminateChildProcess(child)); } if (typeof timeoutMs === 'number' && Number.isFinite(timeoutMs) && timeoutMs > 0) { timeoutId = setTimeout(() => { - child.kill('SIGTERM'); - settle(reject, new Error(`Command timed out after ${timeoutMs}ms: ${command}`)); + timeoutError = createCommandError( + `Command timed out after ${timeoutMs}ms: ${commandLabel}`, + { + command, + args, + stdout, + stderr, + timedOut: true, + }, + ); + terminateChildProcess(child); }, timeoutMs); } @@ -1828,63 +1989,206 @@ function runCommand( } }); - child.on('error', (error) => settle(reject, error)); + child.on('error', (error) => + settle( + reject, + createCommandError(error.message || `Could not start ${commandLabel}.`, { + command, + args, + stdout, + stderr, + }), + ), + ); child.on('close', (code) => { if (finished) { return; } + if (timeoutError) { + settle(reject, timeoutError); + return; + } if (code === 0) { settle(resolve, stdout.trimEnd()); return; } - settle(reject, new Error(stderr.trim() || `Could not start ${command}.`)); + settle( + reject, + createCommandError( + stderr.trim() || stdout.trim() || `Command exited with code ${code}: ${commandLabel}`, + { + command, + args, + stdout, + stderr, + exitCode: code, + }, + ), + ); }); }); } -async function probeToktrackRunner(runner, timeoutMs = TOKTRACK_RUNNER_PROBE_TIMEOUT_MS) { +async function probeToktrackRunner(runner, timeoutMs = getToktrackRunnerTimeouts(runner).probeMs) { try { await runToktrack(runner, ['--version'], { timeoutMs }); - return true; + return { + ok: true, + errorMessage: null, + timedOut: false, + }; } catch (error) { - const message = - error instanceof Error && error.message.trim() - ? error.message.trim() - : `Could not start ${runner.label}.`; + const message = summarizeCommandError(error, `Could not start ${runner.label}.`); console.warn(`Failed to probe ${runner.label}: ${message}`); - return false; + return { + ok: false, + errorMessage: message, + timedOut: Boolean(error?.timedOut), + }; } } -async function resolveToktrackRunner() { +async function resolveToktrackRunnerWithDiagnostics() { + const resolution = { + runner: null, + localVersionMismatch: null, + localFailure: null, + runnerFailures: [], + }; + if (fs.existsSync(TOKTRACK_LOCAL_BIN)) { const localRunner = createLocalToktrackRunner(); try { const localVersion = parseToktrackVersionOutput( await runToktrack(localRunner, ['--version'], { - timeoutMs: TOKTRACK_RUNNER_PROBE_TIMEOUT_MS, + timeoutMs: getToktrackRunnerTimeouts(localRunner).probeMs, }), ); if (localVersion === TOKTRACK_VERSION) { - return localRunner; + resolution.runner = localRunner; + return resolution; } - } catch { - // Fall back to pinned package runners when the local binary is missing or invalid. + resolution.localVersionMismatch = { + detectedVersion: localVersion || 'unknown', + expectedVersion: TOKTRACK_VERSION, + }; + } catch (error) { + resolution.localFailure = summarizeCommandError( + error, + 'The local toktrack binary could not be started.', + ); } } const bunxRunner = createBunxToktrackRunner(); - if (await probeToktrackRunner(bunxRunner)) { - return bunxRunner; + const bunxProbe = await probeToktrackRunner(bunxRunner); + if (bunxProbe.ok) { + resolution.runner = bunxRunner; + return resolution; + } + if (bunxProbe.errorMessage) { + resolution.runnerFailures.push({ + label: bunxRunner.label, + message: bunxProbe.errorMessage, + timedOut: bunxProbe.timedOut, + }); } const npxRunner = createNpxToktrackRunner(); - if (await probeToktrackRunner(npxRunner)) { - return npxRunner; + const npxProbe = await probeToktrackRunner(npxRunner); + if (npxProbe.ok) { + resolution.runner = npxRunner; + return resolution; + } + if (npxProbe.errorMessage) { + resolution.runnerFailures.push({ + label: npxRunner.label, + message: npxProbe.errorMessage, + timedOut: npxProbe.timedOut, + }); } - return null; + return resolution; +} + +async function resolveToktrackRunner() { + const resolution = await resolveToktrackRunnerWithDiagnostics(); + return resolution.runner; +} + +function toAutoImportRunnerResolutionError(resolution) { + if (resolution.localVersionMismatch) { + return createAutoImportError( + formatAutoImportMessageEvent( + createAutoImportMessageEvent( + 'localToktrackVersionMismatch', + resolution.localVersionMismatch, + ), + ), + 'localToktrackVersionMismatch', + resolution.localVersionMismatch, + ); + } + + if (resolution.localFailure) { + return createAutoImportError( + formatAutoImportMessageEvent( + createAutoImportMessageEvent('localToktrackFailed', { + message: resolution.localFailure, + }), + ), + 'localToktrackFailed', + { + message: resolution.localFailure, + }, + ); + } + + if (resolution.runnerFailures.length > 0) { + const timedOutRunnerFailures = resolution.runnerFailures.filter((failure) => failure.timedOut); + if ( + timedOutRunnerFailures.length > 0 && + timedOutRunnerFailures.length === resolution.runnerFailures.length + ) { + const runners = timedOutRunnerFailures.map((failure) => failure.label).join(' / '); + const seconds = getTimeoutSeconds(TOKTRACK_PACKAGE_RUNNER_PROBE_TIMEOUT_MS); + return createAutoImportError( + formatAutoImportMessageEvent( + createAutoImportMessageEvent('packageRunnerWarmupTimedOut', { + runner: runners, + seconds, + }), + ), + 'packageRunnerWarmupTimedOut', + { + runner: runners, + seconds, + }, + ); + } + + return createAutoImportError( + formatAutoImportMessageEvent( + createAutoImportMessageEvent('packageRunnerFailed', { + message: resolution.runnerFailures + .map((failure) => `${failure.label}: ${failure.message}`) + .join(' | '), + }), + ), + 'packageRunnerFailed', + { + message: resolution.runnerFailures + .map((failure) => `${failure.label}: ${failure.message}`) + .join(' | '), + }, + ); + } + + return createAutoImportError( + 'No local toktrack, Bun, or npm exec installation found.', + 'noRunnerFound', + ); } function runToktrack( @@ -1989,16 +2293,58 @@ async function performAutoImport({ onCheck({ tool: 'toktrack', status: 'checking' }); onProgress(createAutoImportMessageEvent('startingLocalImport')); - const runner = await resolveToktrackRunner(); + const resolution = await resolveToktrackRunnerWithDiagnostics(); + const runner = resolution.runner; if (!runner) { - onCheck({ tool: 'toktrack', status: 'not_found' }); - throw createAutoImportError( - 'No local toktrack, Bun, or npm exec installation found.', - 'noRunnerFound', + const resolutionError = toAutoImportRunnerResolutionError(resolution); + if (resolutionError.messageKey === 'noRunnerFound') { + onCheck({ tool: 'toktrack', status: 'not_found' }); + } + throw resolutionError; + } + + if (isPackageToktrackRunner(runner)) { + onProgress( + createAutoImportMessageEvent('warmingUpPackageRunner', { + runner: runner.label, + }), ); } - const versionResult = await runToktrack(runner, ['--version']); + let versionResult; + try { + versionResult = await runToktrack(runner, ['--version'], { + timeoutMs: getToktrackRunnerTimeouts(runner).versionCheckMs, + }); + } catch (error) { + if (isPackageToktrackRunner(runner) && error?.timedOut) { + throw createAutoImportError( + formatAutoImportMessageEvent( + createAutoImportMessageEvent('packageRunnerWarmupTimedOut', { + runner: runner.label, + seconds: getTimeoutSeconds(getToktrackRunnerTimeouts(runner).versionCheckMs), + }), + ), + 'packageRunnerWarmupTimedOut', + { + runner: runner.label, + seconds: getTimeoutSeconds(getToktrackRunnerTimeouts(runner).versionCheckMs), + }, + ); + } + + throw createAutoImportError( + formatAutoImportMessageEvent( + createAutoImportMessageEvent('toktrackVersionCheckFailed', { + message: summarizeCommandError(error), + }), + ), + 'toktrackVersionCheckFailed', + { + message: summarizeCommandError(error), + }, + ); + } onCheck({ tool: 'toktrack', status: 'found', @@ -2011,15 +2357,79 @@ async function performAutoImport({ }), ); - const rawJson = await runToktrack(runner, ['daily', '--json'], { - streamStderr: true, - onStderr: (line) => { - onOutput(line); - }, - signalOnClose, - }); + let rawJson; + try { + rawJson = await runToktrack(runner, ['daily', '--json'], { + streamStderr: true, + onStderr: (line) => { + onOutput(line); + }, + signalOnClose, + timeoutMs: getToktrackRunnerTimeouts(runner).importMs, + }); + } catch (error) { + if (error?.timedOut) { + throw createAutoImportError( + formatAutoImportMessageEvent( + createAutoImportMessageEvent('toktrackExecutionTimedOut', { + runner: runner.label, + seconds: getTimeoutSeconds(getToktrackRunnerTimeouts(runner).importMs), + }), + ), + 'toktrackExecutionTimedOut', + { + runner: runner.label, + seconds: getTimeoutSeconds(getToktrackRunnerTimeouts(runner).importMs), + }, + ); + } + + throw createAutoImportError( + formatAutoImportMessageEvent( + createAutoImportMessageEvent('toktrackExecutionFailed', { + message: summarizeCommandError(error), + }), + ), + 'toktrackExecutionFailed', + { + message: summarizeCommandError(error), + }, + ); + } + + let parsedJson; + try { + parsedJson = JSON.parse(rawJson); + } catch (error) { + throw createAutoImportError( + formatAutoImportMessageEvent( + createAutoImportMessageEvent('toktrackInvalidJson', { + message: summarizeCommandError(error), + }), + ), + 'toktrackInvalidJson', + { + message: summarizeCommandError(error), + }, + ); + } - const normalized = normalizeIncomingData(JSON.parse(rawJson)); + let normalized; + try { + normalized = normalizeIncomingData(parsedJson); + } catch (error) { + throw createAutoImportError( + formatAutoImportMessageEvent( + createAutoImportMessageEvent('toktrackInvalidData', { + message: summarizeCommandError(error), + }), + ), + 'toktrackInvalidData', + { + message: summarizeCommandError(error), + }, + ); + } await withSettingsAndDataMutationLock(async () => { await writeData(normalized); await updateDataLoadState({ @@ -2069,7 +2479,7 @@ async function runStartupAutoLoad({ source = 'cli-auto-load' } = {}) { // --- Server --- -const server = http.createServer(async (req, res) => { +async function handleServerRequest(req, res) { let url; let pathname; @@ -2080,6 +2490,11 @@ const server = http.createServer(async (req, res) => { return json(res, 400, { message: 'Invalid request path' }); } + const hostValidationError = validateRequestHost(req); + if (hostValidationError) { + return json(res, hostValidationError.status, { message: hostValidationError.message }); + } + // API routing const apiPath = resolveApiPath(pathname); @@ -2140,8 +2555,6 @@ const server = http.createServer(async (req, res) => { return json(res, 200, { id: RUNTIME_INSTANCE.id, - pid: RUNTIME_INSTANCE.pid, - startedAt: RUNTIME_INSTANCE.startedAt, mode: RUNTIME_INSTANCE.mode, port: runtimePort, url: runtimeUrl, @@ -2291,6 +2704,14 @@ const server = http.createServer(async (req, res) => { return json(res, validationError.status, { message: validationError.message }); } + if (autoImportStreamRunning || autoImportRunning) { + return json(res, 409, { + message: formatAutoImportMessageEvent(createAutoImportMessageEvent('autoImportRunning')), + }); + } + + autoImportStreamRunning = true; + res.writeHead(200, { 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', @@ -2341,6 +2762,8 @@ const server = http.createServer(async (req, res) => { sendSSE(res, 'error', toAutoImportErrorEvent(err)); sendSSE(res, 'done', {}); res.end(); + } finally { + autoImportStreamRunning = false; } return; } @@ -2410,6 +2833,9 @@ const server = http.createServer(async (req, res) => { // Static file serving const safePath = pathname === '/' ? '/index.html' : pathname; + if (safePath.includes('\0')) { + return json(res, 400, { message: 'Invalid request path' }); + } const filePath = path.resolve(STATIC_ROOT, `.${safePath}`); if ( @@ -2420,6 +2846,31 @@ const server = http.createServer(async (req, res) => { } serveFile(res, filePath); +} + +const server = http.createServer((req, res) => { + void handleServerRequest(req, res).catch((error) => { + console.error(error); + if (res.headersSent) { + res.end(); + return; + } + json(res, 500, { message: 'Internal Server Error' }); + }); +}); + +server.on('clientError', (error, socket) => { + console.error(error); + if (!socket.writable) { + return; + } + socket.end( + 'HTTP/1.1 400 Bad Request\r\n' + + 'Content-Type: application/json; charset=utf-8\r\n' + + 'Connection: close\r\n' + + '\r\n' + + JSON.stringify({ message: 'Invalid request path' }), + ); }); function tryListen(port) { @@ -2493,10 +2944,15 @@ module.exports = { __test__: { commandExists, getExecutableName, + getLocalToktrackDisplayCommand, parseToktrackVersionOutput, resolveToktrackRunner, + toAutoImportRunnerResolutionError, runToktrack, + runCommandWithSpawn, lookupLatestToktrackVersion, + getToktrackRunnerTimeouts, + getToktrackLatestLookupTimeoutMs: () => TOKTRACK_LATEST_LOOKUP_TIMEOUT_MS, resetLatestToktrackVersionCache: () => { latestToktrackVersionCache = null; latestToktrackVersionLookupPromise = null; diff --git a/server/http-utils.js b/server/http-utils.js index 9879f52..3790dbc 100644 --- a/server/http-utils.js +++ b/server/http-utils.js @@ -1,4 +1,18 @@ -function createHttpUtils({ apiPrefix, maxBodySize, securityHeaders }) { +const { isLoopbackHost } = require('./runtime.js'); + +function normalizeHostname(host) { + return String(host || '') + .trim() + .toLowerCase() + .replace(/^\[|\]$/g, ''); +} + +function isWildcardHost(host) { + const normalized = normalizeHostname(host); + return normalized === '0.0.0.0' || normalized === '::'; +} + +function createHttpUtils({ apiPrefix, maxBodySize, securityHeaders, bindHost }) { function readBody(req) { return new Promise((resolve, reject) => { const chunks = []; @@ -110,20 +124,96 @@ function createHttpUtils({ apiPrefix, maxBodySize, securityHeaders }) { return contentType.split(';', 1)[0].trim().toLowerCase() === 'application/json'; } - function hasTrustedOrigin(req) { - const originHeader = getHeaderValue(req, 'origin').trim(); - if (!originHeader) { - return true; + function getHostHeaderHost(req) { + const hostHeader = getHeaderValue(req, 'host').trim(); + if (!hostHeader) { + return ''; + } + + if (hostHeader.startsWith('[')) { + const closingBracketIndex = hostHeader.indexOf(']'); + if (closingBracketIndex === -1) { + return ''; + } + return normalizeHostname(hostHeader.slice(0, closingBracketIndex + 1)); + } + + const colonMatches = hostHeader.match(/:/g) || []; + if (colonMatches.length <= 1) { + return normalizeHostname(hostHeader.split(':', 1)[0]); } + return normalizeHostname(hostHeader); + } + + function getNormalizedHostHeader(req) { const hostHeader = getHeaderValue(req, 'host').trim(); - if (!hostHeader || originHeader === 'null') { + if (!hostHeader) { + return ''; + } + + if (hostHeader.startsWith('[')) { + const closingBracketIndex = hostHeader.indexOf(']'); + if (closingBracketIndex === -1) { + return ''; + } + + const hostname = normalizeHostname(hostHeader.slice(0, closingBracketIndex + 1)); + const remainder = hostHeader.slice(closingBracketIndex + 1); + return remainder ? `[${hostname}]${remainder}` : `[${hostname}]`; + } + + const colonMatches = hostHeader.match(/:/g) || []; + if (colonMatches.length <= 1) { + const [hostname, port = ''] = hostHeader.split(':'); + return port ? `${normalizeHostname(hostname)}:${port}` : normalizeHostname(hostname); + } + + return normalizeHostname(hostHeader); + } + + function getSocketLocalAddress(req) { + return normalizeHostname(req.socket?.localAddress || ''); + } + + function hasTrustedHost(req) { + const hostHeaderHost = getHostHeaderHost(req); + if (!hostHeaderHost) { + return false; + } + + const normalizedBindHost = normalizeHostname(bindHost); + const socketLocalAddress = getSocketLocalAddress(req); + + if (isLoopbackHost(normalizedBindHost) || isLoopbackHost(socketLocalAddress)) { + return isLoopbackHost(hostHeaderHost); + } + + if (hostHeaderHost === normalizedBindHost) { + return true; + } + + if (socketLocalAddress && hostHeaderHost === socketLocalAddress) { + return true; + } + + if (isWildcardHost(normalizedBindHost)) { + return hostHeaderHost === socketLocalAddress; + } + + return false; + } + + function hasTrustedOrigin(req) { + const originHeader = getHeaderValue(req, 'origin').trim(); + const hostHeader = getNormalizedHostHeader(req); + if (!originHeader || !hostHeader || originHeader === 'null') { return false; } try { const origin = new URL(originHeader); - return origin.host === hostHeader; + return origin.host.toLowerCase() === hostHeader; } catch { return false; } @@ -133,6 +223,17 @@ function createHttpUtils({ apiPrefix, maxBodySize, securityHeaders }) { return getHeaderValue(req, 'sec-fetch-site').trim().toLowerCase() === 'cross-site'; } + function validateRequestHost(req) { + if (hasTrustedHost(req)) { + return null; + } + + return { + status: 403, + message: 'Untrusted host header', + }; + } + function validateMutationRequest(req, { requiresJsonContentType = false } = {}) { if (isCrossSiteFetch(req) || !hasTrustedOrigin(req)) { return { @@ -156,6 +257,7 @@ function createHttpUtils({ apiPrefix, maxBodySize, securityHeaders }) { json, sendBuffer, resolveApiPath, + validateRequestHost, validateMutationRequest, }; } diff --git a/shared/toktrack-version.d.ts b/shared/toktrack-version.d.ts index a8395c1..538d026 100644 --- a/shared/toktrack-version.d.ts +++ b/shared/toktrack-version.d.ts @@ -1,6 +1,6 @@ /** Canonical npm package name used for toktrack lookups and execution. */ export const TOKTRACK_PACKAGE_NAME: 'toktrack' /** Pinned toktrack version validated by TTDash. */ -export const TOKTRACK_VERSION: '2.4.0' +export const TOKTRACK_VERSION: '2.5.0' /** Fully qualified toktrack package spec used by npm and bun executors. */ -export const TOKTRACK_PACKAGE_SPEC: 'toktrack@2.4.0' +export const TOKTRACK_PACKAGE_SPEC: 'toktrack@2.5.0' diff --git a/shared/toktrack-version.js b/shared/toktrack-version.js index c750498..e8f7286 100644 --- a/shared/toktrack-version.js +++ b/shared/toktrack-version.js @@ -1,5 +1,5 @@ const TOKTRACK_PACKAGE_NAME = 'toktrack' -const TOKTRACK_VERSION = '2.4.0' +const TOKTRACK_VERSION = '2.5.0' const TOKTRACK_PACKAGE_SPEC = `${TOKTRACK_PACKAGE_NAME}@${TOKTRACK_VERSION}` module.exports = { diff --git a/src/components/features/auto-import/AutoImportModal.tsx b/src/components/features/auto-import/AutoImportModal.tsx index 2dea986..2a95cd9 100644 --- a/src/components/features/auto-import/AutoImportModal.tsx +++ b/src/components/features/auto-import/AutoImportModal.tsx @@ -40,6 +40,7 @@ export function AutoImportModal({ open, onOpenChange, onSuccess }: AutoImportMod const [status, setStatus] = useState('idle') const [lines, setLines] = useState([]) const [summary, setSummary] = useState(null) + const [errorMessage, setErrorMessage] = useState(null) const scrollRef = useRef(null) const closeRef = useRef<{ close: () => void } | null>(null) @@ -57,6 +58,7 @@ export function AutoImportModal({ open, onOpenChange, onSuccess }: AutoImportMod setStatus('checking') setLines([]) setSummary(null) + setErrorMessage(null) const handle = startAutoImport( { @@ -89,11 +91,13 @@ export function AutoImportModal({ open, onOpenChange, onSuccess }: AutoImportMod t('autoImportModal.importedDays', { days: data.days, cost: data.totalCost.toFixed(2) }), ) setSummary(data) + setErrorMessage(null) setStatus('success') onSuccess() }, onError: (data) => { addLine('error', data.message) + setErrorMessage(data.message) setStatus('error') }, onDone: () => { @@ -119,6 +123,10 @@ export function AutoImportModal({ open, onOpenChange, onSuccess }: AutoImportMod }, [lines]) const isRunning = status === 'checking' || status === 'running' + const handleCancel = () => { + closeRef.current?.close() + onOpenChange(false) + } return ( - {t('autoImportModal.errorOccurred')} + + {errorMessage ?? t('autoImportModal.errorOccurred')} + )} + {isRunning && ( + + )} + {!isRunning && (