diff --git a/webclient/package-lock.json b/webclient/package-lock.json index e51c495d6..9f48a49b2 100644 --- a/webclient/package-lock.json +++ b/webclient/package-lock.json @@ -11,25 +11,25 @@ "@bufbuild/protobuf": "^2.11.0", "@emotion/react": "^11.8.2", "@emotion/styled": "^11.8.1", - "@mui/icons-material": "^7.3.10", - "@mui/material": "^7.3.10", + "@mui/icons-material": "^9.0.0", + "@mui/material": "^9.0.0", "@reduxjs/toolkit": "^2.11.2", "crypto-js": "^4.2.0", "dexie": "^4.4.2", - "dompurify": "^3.3.3", + "dompurify": "^3.4.0", "final-form": "^5.0.0", "final-form-set-field-touched": "^1.0.1", - "i18next": "^26.0.4", + "i18next": "^26.0.5", "i18next-browser-languagedetector": "^8.2.1", "i18next-icu": "^2.0.3", "intl-messageformat": "^11.2.1", "lodash": "^4.17.21", "prop-types": "^15.8.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", "react-final-form": "^7.0.0", "react-final-form-listeners": "^3.0.0", - "react-i18next": "^17.0.2", + "react-i18next": "^17.0.3", "react-redux": "^9.2.0", "react-router-dom": "^7.14.1", "react-virtualized-auto-sizer": "^2.0.3", @@ -37,10 +37,10 @@ "rxjs": "^7.5.4" }, "devDependencies": { - "@bufbuild/buf": "^1.67.0", + "@bufbuild/buf": "^1.68.1", "@bufbuild/protoc-gen-es": "^2.11.0", "@eslint/js": "^10.0.1", - "@mui/types": "^7.1.3", + "@mui/types": "^9.0.0", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.4.0", "@testing-library/react": "^16.3.2", @@ -48,23 +48,22 @@ "@types/lodash": "^4.14.179", "@types/node": "^22.19.17", "@types/prop-types": "^15.7.4", - "@types/react": "18.0.24", - "@types/react-dom": "18.0.8", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", "@types/react-virtualized-auto-sizer": "^1.0.1", "@types/react-window": "^1.8.5", "@typescript-eslint/eslint-plugin": "^8.58.2", "@typescript-eslint/parser": "^8.58.2", - "@vitejs/plugin-react": "^5.2.0", + "@vitejs/plugin-react": "^6.0.1", "@vitest/coverage-v8": "^4.1.4", "eslint": "^10.2.0", "fs-extra": "^11.3.4", "globals": "^17.5.0", "husky": "^9.1.7", "jsdom": "^29.0.2", - "typescript": "~5.8", + "typescript": "~6.0", "typescript-eslint": "^8.58.2", - "vite": "^6.4.2", - "vite-tsconfig-paths": "^5.1.4", + "vite": "^8.0.8", "vitest": "^4.1.4" } }, @@ -128,64 +127,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/generator": { "version": "7.29.1", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", @@ -202,33 +143,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/@babel/helper-globals": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", @@ -251,34 +165,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -297,30 +183,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/parser": { "version": "7.29.2", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", @@ -336,38 +198,6 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/runtime": { "version": "7.29.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", @@ -446,9 +276,9 @@ } }, "node_modules/@bufbuild/buf": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@bufbuild/buf/-/buf-1.67.0.tgz", - "integrity": "sha512-BLfgGmNFiHM79PcaafFNiP/+xxbdyFp1neDDdJd6R0tu7McO+WgJHM6vyNYRm7vXOSgO1uUPE4X3YFdBgcWk2Q==", + "version": "1.68.1", + "resolved": "https://registry.npmjs.org/@bufbuild/buf/-/buf-1.68.1.tgz", + "integrity": "sha512-QDJ3oy4qZ5EVS2JYtmpE1n9FuaoABthxIddXB050huGddatr1sjHJSSAXXpLotOI18pW3KQ4zzU1x5Ms+pEEOw==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -461,19 +291,19 @@ "node": ">=12" }, "optionalDependencies": { - "@bufbuild/buf-darwin-arm64": "1.67.0", - "@bufbuild/buf-darwin-x64": "1.67.0", - "@bufbuild/buf-linux-aarch64": "1.67.0", - "@bufbuild/buf-linux-armv7": "1.67.0", - "@bufbuild/buf-linux-x64": "1.67.0", - "@bufbuild/buf-win32-arm64": "1.67.0", - "@bufbuild/buf-win32-x64": "1.67.0" + "@bufbuild/buf-darwin-arm64": "1.68.1", + "@bufbuild/buf-darwin-x64": "1.68.1", + "@bufbuild/buf-linux-aarch64": "1.68.1", + "@bufbuild/buf-linux-armv7": "1.68.1", + "@bufbuild/buf-linux-x64": "1.68.1", + "@bufbuild/buf-win32-arm64": "1.68.1", + "@bufbuild/buf-win32-x64": "1.68.1" } }, "node_modules/@bufbuild/buf-darwin-arm64": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-arm64/-/buf-darwin-arm64-1.67.0.tgz", - "integrity": "sha512-9h/1E2FNCSIt9m4wriGiXt8gHrg8VBOOpmUPVr68axZxb17krPQrIZBPsx05yNpbyvSrPj26/jO2aoqpZsG1vw==", + "version": "1.68.1", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-arm64/-/buf-darwin-arm64-1.68.1.tgz", + "integrity": "sha512-+Cu/2Kr6Add3s+Zk/edcF9QdpnrsukQkdR/z4fk4+qr6YZqfWfiV8f+s14I3h7qPrPnGeCeynvmZ9NmJ1BMYuA==", "cpu": [ "arm64" ], @@ -488,9 +318,9 @@ } }, "node_modules/@bufbuild/buf-darwin-x64": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-x64/-/buf-darwin-x64-1.67.0.tgz", - "integrity": "sha512-9kNu0JBR+TQvxCD6NBooy03g8sLNZGEd0umkWHzdO/05HuV/J6GecMGx1kJ2MYlZQHM4/MljfIuYQUblP1nP4A==", + "version": "1.68.1", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-darwin-x64/-/buf-darwin-x64-1.68.1.tgz", + "integrity": "sha512-hvAs452aJ6io9hZKSfr3TvC+//16zW5y5u3ucsIXVkl5mkmKWSCkPbZwGpjNCfRGGUsyRJGL6rixxTgLKw2k5w==", "cpu": [ "x64" ], @@ -505,9 +335,9 @@ } }, "node_modules/@bufbuild/buf-linux-aarch64": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-aarch64/-/buf-linux-aarch64-1.67.0.tgz", - "integrity": "sha512-hlA20Oot20nW/9CzPBMPPPMfUarKvzqni+Njgrw8T43IFoQWQv8iIRoWWOgOQTGCm4PmjYwiojzEHOEaaKrzTg==", + "version": "1.68.1", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-aarch64/-/buf-linux-aarch64-1.68.1.tgz", + "integrity": "sha512-GLCakHzZVKUPlAiJEPGMBLW+yBk8tuz6NNcoeQU5lB5AO7ks8V8x9cy4CQjne4YSl3niF1JtvAQckLKhEWPueQ==", "cpu": [ "arm64" ], @@ -522,9 +352,9 @@ } }, "node_modules/@bufbuild/buf-linux-armv7": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-armv7/-/buf-linux-armv7-1.67.0.tgz", - "integrity": "sha512-hO9FEEtloITNaxW89rzKUjAsgnX1+rth7IZbK0Z+ohatXdanYg7Kv66yWffytaYf2iHltTbY6W/H4C3x0Uimbg==", + "version": "1.68.1", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-armv7/-/buf-linux-armv7-1.68.1.tgz", + "integrity": "sha512-lUMCULl3MOYQe0oAPWnqNVYy8pL+F3Jeq6C4sSY+0E9udaACMc2mZ32gYingaMop9O1qS58HjJbezFxxL+CJqg==", "cpu": [ "arm" ], @@ -539,9 +369,9 @@ } }, "node_modules/@bufbuild/buf-linux-x64": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-x64/-/buf-linux-x64-1.67.0.tgz", - "integrity": "sha512-KBOWZ0NbhJSfXLM3JEX2AEs32jyHvTKD7wkIYudqOTxPUqwM1MXUg7m2Xw5nP1pcKH4RKS5HFijPMeOW/XUQ8Q==", + "version": "1.68.1", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-linux-x64/-/buf-linux-x64-1.68.1.tgz", + "integrity": "sha512-eRU3UWiZQthAgx+qFTG3EeJ/VeOcZzAkKYGt5ansOnOIJHBm+3RG2KqA+Jm8q3EFqB1XpVcGxPXnIu/qmFJXaQ==", "cpu": [ "x64" ], @@ -556,9 +386,9 @@ } }, "node_modules/@bufbuild/buf-win32-arm64": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-arm64/-/buf-win32-arm64-1.67.0.tgz", - "integrity": "sha512-ARGPwOv0lkUp3FU7bUMpYzqoJInx2qkk1ECBEC9XZMnRKmhCbyzmBoBKChBBJhEyDFdzPivhjg//zk5AlQ3bFA==", + "version": "1.68.1", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-arm64/-/buf-win32-arm64-1.68.1.tgz", + "integrity": "sha512-v3xlKzs3l2C+mYv+T0sYol05DTmsFKYmM5Vz8+AyrXdjxRwq2QH7m0arVWwxHX2MwyhQxKA+qqjoF8bCUM7xxA==", "cpu": [ "arm64" ], @@ -573,9 +403,9 @@ } }, "node_modules/@bufbuild/buf-win32-x64": { - "version": "1.67.0", - "resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-x64/-/buf-win32-x64-1.67.0.tgz", - "integrity": "sha512-x9fkxEbjb2U4petBbESvNx+sfSQJONJxKOQzPfEKALksqRlvh7ktoHrYbygErnRZBSTNgrXzAqFI1GxMGEGSLQ==", + "version": "1.68.1", + "resolved": "https://registry.npmjs.org/@bufbuild/buf-win32-x64/-/buf-win32-x64-1.68.1.tgz", + "integrity": "sha512-b62pwu+G7n5tF8n1QIoT85K7xgKJZS8SzdN020weOa7IVvMNHCDqMq7nrkz46fXCkK7MtD1YJ6sUp86sWSZPsw==", "cpu": [ "x64" ], @@ -786,6 +616,40 @@ "node": ">=20.19.0" } }, + "node_modules/@emnapi/core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emotion/babel-plugin": { "version": "11.13.5", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", @@ -932,448 +796,6 @@ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", "license": "MIT" }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", - "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", @@ -1590,17 +1012,6 @@ "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", @@ -1627,9 +1038,9 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "7.3.10", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.3.10.tgz", - "integrity": "sha512-vrOpWRmPJSuwLo23J62wggEm/jvGdzqctej+UOCtgDUz6nZJQuj3ByPccVyaa7eQmwAzUwKN56FQPMKkqbj1GA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-9.0.0.tgz", + "integrity": "sha512-uwQNGkhv0lf7ufxw6QXev77BW6pWbW+7uxYjU5+rfp4lBkFtMEgJCsarTM3Tn+i0lGx6+Ol2u88JdGXr0GDskA==", "license": "MIT", "funding": { "type": "opencollective", @@ -1637,12 +1048,12 @@ } }, "node_modules/@mui/icons-material": { - "version": "7.3.10", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.3.10.tgz", - "integrity": "sha512-Au0ma4NSKGKNiimukj8UT/W1x2Qx6Qwn2RvFGykiSqVLYBNlIOPbjnIMvrwLGLu89EEpTVdu/ys/OduZR+tWqw==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-9.0.0.tgz", + "integrity": "sha512-oDwyvI6LgjWRC9MBcSGvLkPud9S9ELgSBQFYxa1rYcZn6Br55dn22SyvsPDMsn0G8OndFk53iMT45W5mNqrogw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.6" + "@babel/runtime": "^7.29.2" }, "engines": { "node": ">=14.0.0" @@ -1652,7 +1063,7 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^7.3.10", + "@mui/material": "^9.0.0", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -1663,22 +1074,22 @@ } }, "node_modules/@mui/material": { - "version": "7.3.10", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.3.10.tgz", - "integrity": "sha512-cHvGOk2ZEfbQt3LnGe0ZKd/ETs9gsUpkW66DCO+GSjMZhpdKU4XsuIr7zJ/B/2XaN8ihxuzHfYAR4zPtCN4RYg==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-9.0.0.tgz", + "integrity": "sha512-+VP/oQCDhDR87NQQgXnNBG8dwy6GNuQLnenS1pZvkbn2dKFSxRSRMybTpH9xUxXP+316mlYDy5CSbYtusnCWtw==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.6", - "@mui/core-downloads-tracker": "^7.3.10", - "@mui/system": "^7.3.10", - "@mui/types": "^7.4.12", - "@mui/utils": "^7.3.10", + "@babel/runtime": "^7.29.2", + "@mui/core-downloads-tracker": "^9.0.0", + "@mui/system": "^9.0.0", + "@mui/types": "^9.0.0", + "@mui/utils": "^9.0.0", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.12", "clsx": "^2.1.1", "csstype": "^3.2.3", "prop-types": "^15.8.1", - "react-is": "^19.2.3", + "react-is": "^19.2.4", "react-transition-group": "^4.4.5" }, "engines": { @@ -1691,7 +1102,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^7.3.10", + "@mui/material-pigment-css": "^9.0.0", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -1712,13 +1123,13 @@ } }, "node_modules/@mui/private-theming": { - "version": "7.3.10", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.3.10.tgz", - "integrity": "sha512-j3EZN+zOctxUISvJSmsEPo5o2F8zse4l5vRkBY+ps6UtnL6J7o14kUaI4w7gwo73id9e3cDNMVQK/9BVaMHVBw==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-9.0.0.tgz", + "integrity": "sha512-JtuZoaiCqwD6vjgYu6Xp3T7DZkrxJlgtDz5yESzhI34fEX5hHMh2VJUbuL9UOg8xrfIFMrq6dcYoH/7Zi4G0RA==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.6", - "@mui/utils": "^7.3.10", + "@babel/runtime": "^7.29.2", + "@mui/utils": "^9.0.0", "prop-types": "^15.8.1" }, "engines": { @@ -1739,12 +1150,12 @@ } }, "node_modules/@mui/styled-engine": { - "version": "7.3.10", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.3.10.tgz", - "integrity": "sha512-WxE9SiF8xskAQqGjsp0poXCkCqsoXFEsSr0HBXfApmGHR+DBnXRp+z46Vsltg4gpPM4Z96DeAQRpeAOnhNg7Ng==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-9.0.0.tgz", + "integrity": "sha512-9RLGdX4Jg0aQPRuvqh/OLzYSPlgd5zyEw5/1HIRfdavSiOd03WtUaGZH9/w1RoTYuRKwpgy0hpIFaMHIqPVIWg==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.6", + "@babel/runtime": "^7.29.2", "@emotion/cache": "^11.14.0", "@emotion/serialize": "^1.3.3", "@emotion/sheet": "^1.4.0", @@ -1773,16 +1184,16 @@ } }, "node_modules/@mui/system": { - "version": "7.3.10", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.3.10.tgz", - "integrity": "sha512-/sfPpdpJaQn7BSF+avjIdHSYmxHp0UOBYNxSG9QGKfMOD6sLANCpRPCnanq1Pe0lFf0NHkO2iUk0TNzdWC1USQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-9.0.0.tgz", + "integrity": "sha512-YnC5Zg6j04IxiLc/boAKs0464jfZlLFVa7mf5E8lF0XOtZVUvG6R6gJK50lgUYdaaLdyLfxF6xR7LaPuEpeT/g==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.6", - "@mui/private-theming": "^7.3.10", - "@mui/styled-engine": "^7.3.10", - "@mui/types": "^7.4.12", - "@mui/utils": "^7.3.10", + "@babel/runtime": "^7.29.2", + "@mui/private-theming": "^9.0.0", + "@mui/styled-engine": "^9.0.0", + "@mui/types": "^9.0.0", + "@mui/utils": "^9.0.0", "clsx": "^2.1.1", "csstype": "^3.2.3", "prop-types": "^15.8.1" @@ -1813,12 +1224,12 @@ } }, "node_modules/@mui/types": { - "version": "7.4.12", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.12.tgz", - "integrity": "sha512-iKNAF2u9PzSIj40CjvKJWxFXJo122jXVdrmdh0hMYd+FR+NuJMkr/L88XwWLCRiJ5P1j+uyac25+Kp6YC4hu6w==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-9.0.0.tgz", + "integrity": "sha512-i1cuFCAWN44b3AJWO7mh7tuh1sqbQSeVr/94oG0TX5uXivac8XalgE4/6fQZcmGZigzbQ35IXxj/4jLpRIBYZg==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.6" + "@babel/runtime": "^7.29.2" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" @@ -1830,17 +1241,17 @@ } }, "node_modules/@mui/utils": { - "version": "7.3.10", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.3.10.tgz", - "integrity": "sha512-7y2eIfy0h7JPz+Yy4pS+wgV68d46PuuxDqKBN4Q8VlPQSsCAGwroMCV6xWyc7g9dvEp8ZNFsknc59GHWO+r6Ow==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-9.0.0.tgz", + "integrity": "sha512-bQcqyg/gjULUqTuyUjSAFr6LQGLvtkNtDbJerAtoUn9kGZ0hg5QJiN1PLHMLbeFpe3te1831uq7GFl2ITokGdg==", "license": "MIT", "dependencies": { - "@babel/runtime": "^7.28.6", - "@mui/types": "^7.4.12", + "@babel/runtime": "^7.29.2", + "@mui/types": "^9.0.0", "@types/prop-types": "^15.7.15", "clsx": "^2.1.1", "prop-types": "^15.8.1", - "react-is": "^19.2.3" + "react-is": "^19.2.4" }, "engines": { "node": ">=14.0.0" @@ -1859,6 +1270,35 @@ } } }, + "node_modules/@napi-rs/wasm-runtime": { + "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, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "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==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -1895,60 +1335,27 @@ } } }, - "node_modules/@reduxjs/toolkit/node_modules/redux": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", - "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", - "license": "MIT" - }, - "node_modules/@reduxjs/toolkit/node_modules/redux-thunk": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", - "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "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==", + "cpu": [ + "arm64" + ], + "dev": true, "license": "MIT", - "peerDependencies": { - "redux": "^5.0.0" + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.3", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", - "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", - "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", - "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", - "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "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==", "cpu": [ "arm64" ], @@ -1957,12 +1364,15 @@ "optional": true, "os": [ "darwin" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", - "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "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==", "cpu": [ "x64" ], @@ -1971,26 +1381,15 @@ "optional": true, "os": [ "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", - "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", - "cpu": [ - "arm64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", - "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "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==", "cpu": [ "x64" ], @@ -1999,12 +1398,15 @@ "optional": true, "os": [ "freebsd" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", - "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "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==", "cpu": [ "arm" ], @@ -2013,26 +1415,15 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", - "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", - "cpu": [ - "arm" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", - "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "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==", "cpu": [ "arm64" ], @@ -2041,12 +1432,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", - "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "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==", "cpu": [ "arm64" ], @@ -2055,40 +1449,15 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", - "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", - "cpu": [ - "loong64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", - "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", - "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "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==", "cpu": [ "ppc64" ], @@ -2097,54 +1466,15 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", - "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", - "cpu": [ - "ppc64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", - "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", - "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", - "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "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==", "cpu": [ "s390x" ], @@ -2153,12 +1483,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", - "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "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==", "cpu": [ "x64" ], @@ -2167,12 +1500,15 @@ "optional": true, "os": [ "linux" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", - "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "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==", "cpu": [ "x64" ], @@ -2181,26 +1517,15 @@ "optional": true, "os": [ "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", - "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", - "cpu": [ - "x64" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", - "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "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==", "cpu": [ "arm64" ], @@ -2209,12 +1534,34 @@ "optional": true, "os": [ "openharmony" - ] + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", - "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "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==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.9.2", + "@emnapi/runtime": "1.9.2", + "@napi-rs/wasm-runtime": "^1.1.3" + }, + "engines": { + "node": ">=14.0.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==", "cpu": [ "arm64" ], @@ -2223,26 +1570,15 @@ "optional": true, "os": [ "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", - "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", - "cpu": [ - "ia32" ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", - "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "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==", "cpu": [ "x64" ], @@ -2251,21 +1587,17 @@ "optional": true, "os": [ "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", - "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", - "cpu": [ - "x64" ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", + "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==", "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] + "license": "MIT" }, "node_modules/@standard-schema/spec": { "version": "1.1.0", @@ -2299,23 +1631,6 @@ "node": ">=18" } }, - "node_modules/@testing-library/dom/node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/@testing-library/dom/node_modules/dom-accessibility-api": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true, - "license": "MIT" - }, "node_modules/@testing-library/jest-dom": { "version": "6.9.1", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", @@ -2336,6 +1651,13 @@ "yarn": ">=1" } }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, "node_modules/@testing-library/react": { "version": "16.3.2", "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", @@ -2364,6 +1686,17 @@ } } }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -2371,51 +1704,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", @@ -2495,25 +1783,23 @@ "license": "MIT" }, "node_modules/@types/react": { - "version": "18.0.24", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.0.24.tgz", - "integrity": "sha512-wRJWT6ouziGUy+9uX0aW4YOJxAY0bG6/AOk5AW5QSvZqI7dk6VBIbXvcVgIw/W5Jrl24f77df98GEKTJGOLx7Q==", + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "dev": true, "license": "MIT", "dependencies": { - "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" + "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { - "version": "18.0.8", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.8.tgz", - "integrity": "sha512-C3GYO0HLaOkk9dDAz3Dl4sbe4AKUGTCfFIZsz3n/82dPNN8Du533HzKatDxeUYWu24wJgMP1xICqkWk1YOLOIw==", + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "dev": true, "license": "MIT", - "dependencies": { - "@types/react": "*" + "peerDependencies": { + "@types/react": "^19.2.0" } }, "node_modules/@types/react-transition-group": { @@ -2545,13 +1831,6 @@ "@types/react": "*" } }, - "node_modules/@types/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-WFHp9YUJQ6CKshqoC37iOlHnQSmxNc795UhB26CyBBttrN9svdIrUjl/NjnNmfcwtncN0h/0PPAFWv9ovP8mLA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/trusted-types": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", @@ -2812,24 +2091,29 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", - "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", + "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.29.0", - "@babel/plugin-transform-react-jsx-self": "^7.27.1", - "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-rc.3", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.18.0" + "@rolldown/pluginutils": "1.0.0-rc.7" }, "engines": { "node": "^20.19.0 || >=22.12.0" }, "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } } }, "node_modules/@vitest/coverage-v8": { @@ -3047,13 +2331,13 @@ } }, "node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" + "dependencies": { + "dequal": "^2.0.3" } }, "node_modules/assertion-error": { @@ -3110,19 +2394,6 @@ "node": "18 || 20 || >=22" } }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.18", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.18.tgz", - "integrity": "sha512-VSnGQAOLtP5mib/DPyg2/t+Tlv65NTBz83BJBJvmLVHHuKJVaDOBvJJykiT5TR++em5nfAySPccDZDa4oSrn8A==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/bidi-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", @@ -3146,40 +2417,6 @@ "node": "18 || 20 || >=22" } }, - "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", - "update-browserslist-db": "^1.2.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -3189,27 +2426,6 @@ "node": ">=6" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001787", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001787.tgz", - "integrity": "sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, "node_modules/chai": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", @@ -3367,6 +2583,16 @@ "node": ">=6" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/dexie": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/dexie/-/dexie-4.4.2.tgz", @@ -3374,9 +2600,9 @@ "license": "Apache-2.0" }, "node_modules/dom-accessibility-api": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", - "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, "license": "MIT" }, @@ -3391,21 +2617,14 @@ } }, "node_modules/dompurify": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.3.tgz", - "integrity": "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==", + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.0.tgz", + "integrity": "sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" } }, - "node_modules/electron-to-chromium": { - "version": "1.5.335", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.335.tgz", - "integrity": "sha512-q9n5T4BR4Xwa2cwbrwcsDJtHD/enpQ5S1xF1IAtdqf5AAgqDFmR/aakqH3ChFdqd/QXJhS3rnnXFtexU7rax6Q==", - "dev": true, - "license": "ISC" - }, "node_modules/entities": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", @@ -3444,58 +2663,6 @@ "dev": true, "license": "MIT" }, - "node_modules/esbuild": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", - "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -3876,16 +3043,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -3912,13 +3069,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globrex": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", - "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", - "dev": true, - "license": "MIT" - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -4009,9 +3159,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.5", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.0.5.tgz", + "integrity": "sha512-9uHb4T27TdV36phJXcbpnRPt5yzAfqHXVrdASvmHZyPuZJtrLythd+GyXhiaHV5LlpuuskbAqhwPjmfTbKbi8w==", "funding": [ { "type": "individual", @@ -4267,16 +3417,6 @@ } } }, - "node_modules/jsdom/node_modules/lru-cache": { - "version": "11.3.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", - "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", @@ -4316,19 +3456,6 @@ "dev": true, "license": "MIT" }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/jsonfile": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", @@ -4366,6 +3493,267 @@ "node": ">= 0.8.0" } }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -4407,13 +3795,13 @@ } }, "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", + "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" } }, "node_modules/lz-string": { @@ -4529,13 +3917,6 @@ "dev": true, "license": "MIT" }, - "node_modules/node-releases": { - "version": "2.0.37", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", - "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", - "dev": true, - "license": "MIT" - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -4697,10 +4078,23 @@ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "license": "ISC" }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/postcss": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", - "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", "dev": true, "funding": [ { @@ -4786,28 +4180,24 @@ } }, "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "version": "19.2.5", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", + "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - }, "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "version": "19.2.5", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz", + "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==", "license": "MIT", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" + "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^18.3.1" + "react": "^19.2.5" } }, "node_modules/react-final-form": { @@ -4842,9 +4232,9 @@ } }, "node_modules/react-i18next": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-17.0.2.tgz", - "integrity": "sha512-shBftH2vaTWK2Bsp7FiL+cevx3xFJlvFxmsDFQSrJc+6twHkP0tv/bGa01VVWzpreUVVwU+3Hev5iFqRg65RwA==", + "version": "17.0.3", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-17.0.3.tgz", + "integrity": "sha512-x4xjvUNZ56T+zfXWNedNnCET9Xq1IBYWX7IsWo5cCQ/RT+Rm7GWqt0h9PShFi4IhyMnsdiu1C6Jc4DE+/S3PFQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.29.2", @@ -4897,16 +4287,6 @@ } } }, - "node_modules/react-refresh": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", - "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/react-router": { "version": "7.14.1", "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.1.tgz", @@ -4995,6 +4375,21 @@ "node": ">=8" } }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" + }, + "node_modules/redux-thunk": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz", + "integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==", + "license": "MIT", + "peerDependencies": { + "redux": "^5.0.0" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -5041,51 +4436,47 @@ "node": ">=4" } }, - "node_modules/rollup": { - "version": "4.60.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", - "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "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==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" + "@oxc-project/types": "=0.124.0", + "@rolldown/pluginutils": "1.0.0-rc.15" }, "bin": { - "rollup": "dist/bin/rollup" + "rolldown": "bin/cli.mjs" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.1", - "@rollup/rollup-android-arm64": "4.60.1", - "@rollup/rollup-darwin-arm64": "4.60.1", - "@rollup/rollup-darwin-x64": "4.60.1", - "@rollup/rollup-freebsd-arm64": "4.60.1", - "@rollup/rollup-freebsd-x64": "4.60.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", - "@rollup/rollup-linux-arm-musleabihf": "4.60.1", - "@rollup/rollup-linux-arm64-gnu": "4.60.1", - "@rollup/rollup-linux-arm64-musl": "4.60.1", - "@rollup/rollup-linux-loong64-gnu": "4.60.1", - "@rollup/rollup-linux-loong64-musl": "4.60.1", - "@rollup/rollup-linux-ppc64-gnu": "4.60.1", - "@rollup/rollup-linux-ppc64-musl": "4.60.1", - "@rollup/rollup-linux-riscv64-gnu": "4.60.1", - "@rollup/rollup-linux-riscv64-musl": "4.60.1", - "@rollup/rollup-linux-s390x-gnu": "4.60.1", - "@rollup/rollup-linux-x64-gnu": "4.60.1", - "@rollup/rollup-linux-x64-musl": "4.60.1", - "@rollup/rollup-openbsd-x64": "4.60.1", - "@rollup/rollup-openharmony-arm64": "4.60.1", - "@rollup/rollup-win32-arm64-msvc": "4.60.1", - "@rollup/rollup-win32-ia32-msvc": "4.60.1", - "@rollup/rollup-win32-x64-gnu": "4.60.1", - "@rollup/rollup-win32-x64-msvc": "4.60.1", - "fsevents": "~2.3.2" + "@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" } }, + "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==", + "dev": true, + "license": "MIT" + }, "node_modules/rxjs": { "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", @@ -5109,13 +4500,10 @@ } }, "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" }, "node_modules/semver": { "version": "7.7.4", @@ -5193,9 +4581,9 @@ "license": "MIT" }, "node_modules/std-env": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.0.0.tgz", - "integrity": "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", "dev": true, "license": "MIT" }, @@ -5284,19 +4672,6 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/tinyrainbow": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", @@ -5366,27 +4741,6 @@ "typescript": ">=4.8.4" } }, - "node_modules/tsconfck": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", - "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", - "dev": true, - "license": "MIT", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, - "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -5407,9 +4761,9 @@ } }, "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5471,37 +4825,6 @@ "node": ">= 10.0.0" } }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -5522,24 +4845,23 @@ } }, "node_modules/vite": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", - "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.8.tgz", + "integrity": "sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.15", + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -5548,14 +4870,15 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@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": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", + "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" @@ -5564,15 +4887,18 @@ "@types/node": { "optional": true }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, "jiti": { "optional": true }, "less": { "optional": true }, - "lightningcss": { - "optional": true - }, "sass": { "optional": true }, @@ -5596,39 +4922,6 @@ } } }, - "node_modules/vite-tsconfig-paths": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", - "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "globrex": "^0.1.2", - "tsconfck": "^3.0.3" - }, - "peerDependencies": { - "vite": "*" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/vitest": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.4.tgz", @@ -5719,19 +5012,6 @@ } } }, - "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/void-elements": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", @@ -5849,13 +5129,6 @@ "dev": true, "license": "MIT" }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, "node_modules/yaml": { "version": "1.10.3", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", diff --git a/webclient/package.json b/webclient/package.json index 1259a3aa7..b2927025f 100644 --- a/webclient/package.json +++ b/webclient/package.json @@ -21,25 +21,25 @@ "@bufbuild/protobuf": "^2.11.0", "@emotion/react": "^11.8.2", "@emotion/styled": "^11.8.1", - "@mui/icons-material": "^7.3.10", - "@mui/material": "^7.3.10", + "@mui/icons-material": "^9.0.0", + "@mui/material": "^9.0.0", "@reduxjs/toolkit": "^2.11.2", "crypto-js": "^4.2.0", "dexie": "^4.4.2", - "dompurify": "^3.3.3", + "dompurify": "^3.4.0", "final-form": "^5.0.0", "final-form-set-field-touched": "^1.0.1", - "i18next": "^26.0.4", + "i18next": "^26.0.5", "i18next-browser-languagedetector": "^8.2.1", "i18next-icu": "^2.0.3", "intl-messageformat": "^11.2.1", "lodash": "^4.17.21", "prop-types": "^15.8.1", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", "react-final-form": "^7.0.0", "react-final-form-listeners": "^3.0.0", - "react-i18next": "^17.0.2", + "react-i18next": "^17.0.3", "react-redux": "^9.2.0", "react-router-dom": "^7.14.1", "react-virtualized-auto-sizer": "^2.0.3", @@ -47,10 +47,10 @@ "rxjs": "^7.5.4" }, "devDependencies": { - "@bufbuild/buf": "^1.67.0", + "@bufbuild/buf": "^1.68.1", "@bufbuild/protoc-gen-es": "^2.11.0", "@eslint/js": "^10.0.1", - "@mui/types": "^7.1.3", + "@mui/types": "^9.0.0", "@testing-library/dom": "^10.4.1", "@testing-library/jest-dom": "^6.4.0", "@testing-library/react": "^16.3.2", @@ -58,23 +58,22 @@ "@types/lodash": "^4.14.179", "@types/node": "^22.19.17", "@types/prop-types": "^15.7.4", - "@types/react": "18.0.24", - "@types/react-dom": "18.0.8", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", "@types/react-virtualized-auto-sizer": "^1.0.1", "@types/react-window": "^1.8.5", "@typescript-eslint/eslint-plugin": "^8.58.2", "@typescript-eslint/parser": "^8.58.2", - "@vitejs/plugin-react": "^5.2.0", + "@vitejs/plugin-react": "^6.0.1", "@vitest/coverage-v8": "^4.1.4", "eslint": "^10.2.0", "fs-extra": "^11.3.4", "globals": "^17.5.0", "husky": "^9.1.7", "jsdom": "^29.0.2", - "typescript": "~5.8", + "typescript": "~6.0", "typescript-eslint": "^8.58.2", - "vite": "^6.4.2", - "vite-tsconfig-paths": "^5.1.4", + "vite": "^8.0.8", "vitest": "^4.1.4" }, "browserslist": { diff --git a/webclient/src/__test-utils__/globalGuards.ts b/webclient/src/__test-utils__/globalGuards.ts new file mode 100644 index 000000000..b350d440f --- /dev/null +++ b/webclient/src/__test-utils__/globalGuards.ts @@ -0,0 +1,48 @@ +// Shared lifecycle helpers for test files that need to mutate global state. +// +// The root `setupTests.ts` guards catch leaks even when callers forget to +// clean up, but opt-in helpers make intent explicit at the call site and +// avoid piling cleanup logic onto the shared safety net. + +/** + * Temporarily override fields on `window.location` and return a restore fn. + * + * `Object.defineProperty(window, 'location', ...)` is not a `vi.spyOn` target, + * so `vi.restoreAllMocks()` will NOT undo it. Always pair with the returned + * `restore` callback (ideally in `afterEach`). + */ +export function withMockLocation(overrides: Partial): () => void { + const originalDescriptor = Object.getOwnPropertyDescriptor(window, 'location'); + + Object.defineProperty(window, 'location', { + value: { ...window.location, ...overrides }, + writable: true, + configurable: true, + }); + + return () => { + if (originalDescriptor) { + Object.defineProperty(window, 'location', originalDescriptor); + } + }; +} + +/** + * Push an entry onto a shared event-handler registry array and return a + * teardown function that removes exactly that entry. + * + * Used by ProtobufService specs which install temporary handlers into the + * (mocked) `GameEvents` / `RoomEvents` / `SessionEvents` arrays. Manual + * `.push()`/`.pop()` inside a test body corrupts the array if an assertion + * throws between them — this helper makes the teardown safe to run in + * `afterEach`. + */ +export function withEventRegistry(registry: T[], entry: T): () => void { + registry.push(entry); + return () => { + const index = registry.lastIndexOf(entry); + if (index !== -1) { + registry.splice(index, 1); + } + }; +} diff --git a/webclient/src/__test-utils__/index.ts b/webclient/src/__test-utils__/index.ts new file mode 100644 index 000000000..6d606210a --- /dev/null +++ b/webclient/src/__test-utils__/index.ts @@ -0,0 +1 @@ +export { withMockLocation, withEventRegistry } from './globalGuards'; diff --git a/webclient/src/components/ThreePaneLayout/ThreePaneLayout.tsx b/webclient/src/components/ThreePaneLayout/ThreePaneLayout.tsx index 2b202238c..287e58382 100644 --- a/webclient/src/components/ThreePaneLayout/ThreePaneLayout.tsx +++ b/webclient/src/components/ThreePaneLayout/ThreePaneLayout.tsx @@ -1,42 +1,40 @@ -import { Component, CElement } from 'react'; +import { ReactElement } from 'react'; import Grid from '@mui/material/Grid'; import './ThreePaneLayout.css'; // @DEPRECATED // This component sucks balls, dont use it. It will be removed sooner than later. -class ThreePaneLayout extends Component { - render() { - return ( -
- - - - {this.props.top} - - - {this.props.bottom} - +function ThreePaneLayout(props: ThreePaneLayoutProps) { + return ( +
+ + + + {props.top} - - {this.props.side} + + {props.bottom} -
- ); - } + + {props.side} + +
+
+ ); } interface ThreePaneLayoutProps { - top: CElement, - bottom: CElement, - side?: CElement, + top: ReactElement, + bottom: ReactElement, + side?: ReactElement, fixedHeight?: boolean, } diff --git a/webclient/src/components/Toast/Toast.tsx b/webclient/src/components/Toast/Toast.tsx index ac7c029e7..faf4af586 100644 --- a/webclient/src/components/Toast/Toast.tsx +++ b/webclient/src/components/Toast/Toast.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import ReactDOM from 'react-dom' +import { createPortal } from 'react-dom' import Alert from '@mui/material/Alert'; import CheckCircleIcon from '@mui/icons-material/CheckCircle'; @@ -46,7 +46,7 @@ function Toast(props) { return null } - return ReactDOM.createPortal( + return createPortal( node, rootElemRef.current ); diff --git a/webclient/src/containers/App/AppShell.tsx b/webclient/src/containers/App/AppShell.tsx index 9476d76cd..66618d865 100644 --- a/webclient/src/containers/App/AppShell.tsx +++ b/webclient/src/containers/App/AppShell.tsx @@ -1,4 +1,4 @@ -import { Component, Suspense } from 'react'; +import { Suspense, useEffect } from 'react'; import { Provider } from 'react-redux'; import { MemoryRouter as Router } from 'react-router-dom'; import CssBaseline from '@mui/material/CssBaseline'; @@ -10,33 +10,31 @@ import './AppShell.css'; import { ToastProvider } from '@app/components' -class AppShell extends Component { - componentDidMount() { +function AppShell() { + useEffect(() => { // @TODO (1) window.onbeforeunload = () => true; - } + }, []); - handleContextMenu(event) { + const handleContextMenu = (event) => { event.preventDefault(); - } + }; - render() { - return ( - - - - -
- - - - -
-
-
-
- ); - } + return ( + + + + +
+ + + + +
+
+
+
+ ); } export default AppShell; diff --git a/webclient/src/containers/Decks/Decks.tsx b/webclient/src/containers/Decks/Decks.tsx index f37d67261..e5856eaeb 100644 --- a/webclient/src/containers/Decks/Decks.tsx +++ b/webclient/src/containers/Decks/Decks.tsx @@ -1,20 +1,15 @@ -// eslint-disable-next-line -import React, { Component } from "react"; - import { AuthGuard } from '@app/components'; import Layout from '../Layout/Layout'; import './Decks.css'; -class Decks extends Component { - render() { - return ( - - - "Decks" - - ) - } +function Decks() { + return ( + + + "Decks" + + ); } export default Decks; diff --git a/webclient/src/containers/Game/Game.tsx b/webclient/src/containers/Game/Game.tsx index 6be3ab1ca..139147ab1 100644 --- a/webclient/src/containers/Game/Game.tsx +++ b/webclient/src/containers/Game/Game.tsx @@ -1,20 +1,15 @@ -// eslint-disable-next-line -import React, { Component } from "react"; - import { AuthGuard } from '@app/components'; import Layout from '../Layout/Layout'; import './Game.css'; -class Game extends Component { - render() { - return ( - - - "Game" - - ) - } +function Game() { + return ( + + + "Game" + + ); } export default Game; diff --git a/webclient/src/containers/Layout/LeftNav.tsx b/webclient/src/containers/Layout/LeftNav.tsx index db4227b36..0730011d5 100644 --- a/webclient/src/containers/Layout/LeftNav.tsx +++ b/webclient/src/containers/Layout/LeftNav.tsx @@ -5,7 +5,7 @@ import Menu from '@mui/material/Menu'; import MenuItem from '@mui/material/MenuItem'; import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'; import CloseIcon from '@mui/icons-material/Close'; -import MailOutlineRoundedIcon from '@mui/icons-material/MailOutline'; +import MailOutlineRoundedIcon from '@mui/icons-material/MailOutlineRounded'; import MenuRoundedIcon from '@mui/icons-material/MenuRounded'; import { AuthenticationService, RoomsService } from '@app/api'; @@ -56,7 +56,7 @@ const LeftNav = () => { } const handleMenuItemClick = (option: string) => { - const route = RouteEnum[option.toUpperCase()]; + const route = App.RouteEnum[option.toUpperCase()]; navigate(generatePath(route)); } @@ -149,10 +149,12 @@ const LeftNav = () => { keepMounted open={!!state.anchorEl} onClose={() => handleMenuClose()} - PaperProps={{ - style: { - marginTop: '32px', - width: '20ch', + slotProps={{ + paper: { + style: { + marginTop: '32px', + width: '20ch', + }, }, }} > diff --git a/webclient/src/containers/Player/Player.tsx b/webclient/src/containers/Player/Player.tsx index 58f5403cf..899285054 100644 --- a/webclient/src/containers/Player/Player.tsx +++ b/webclient/src/containers/Player/Player.tsx @@ -1,18 +1,14 @@ -// eslint-disable-next-line -import React, { Component } from "react"; import Layout from '../Layout/Layout'; import { AuthGuard } from '@app/components'; -class Player extends Component { - render() { - return ( - - - "Player" - - ) - } +function Player() { + return ( + + + "Player" + + ); } export default Player; diff --git a/webclient/src/hooks/useReduxEffect.tsx b/webclient/src/hooks/useReduxEffect.tsx index a84d5d728..3f9ec853d 100644 --- a/webclient/src/hooks/useReduxEffect.tsx +++ b/webclient/src/hooks/useReduxEffect.tsx @@ -4,7 +4,7 @@ File is adapted from https://github.com/Qeepsake/use-redux-effect under MIT Lice * @description */ -import { useRef, useEffect, DependencyList } from 'react' +import { useEffect, useRef, DependencyList } from 'react' import { useStore } from 'react-redux' import { castArray } from 'lodash' @@ -14,36 +14,44 @@ import { castArray } from 'lodash' export type ReduxEffect = (action: any) => void /** - * Subscribes to redux store events - * - * @param effect - * @param type - * @param deps - */ + * Subscribes to redux store events. + * + * On mount, synchronously inspects the current `state.action` so an action + * dispatched between render and effect-commit is still observed — this is + * what lets `` catch a `JOIN_ROOM` that auto-join fired while the + * route was transitioning. + */ export function useReduxEffect( effect: ReduxEffect, type: string | string[], deps: DependencyList = [], ): void { - const currentValue = useRef(null); const store = useStore(); + const effectRef = useRef(effect); + const typeRef = useRef(type); + // Persists across StrictMode's mount → unmount → remount cycle so we + // don't re-fire for an action we already handled on the first mount. + const lastHandledCountRef = useRef(-1); - const handleChange = (): void => { - const state: any = store.getState(); - const action = state.action; - const previousValue = currentValue.current; - currentValue.current = action.count; - - if ( - previousValue !== action.count && - castArray(type).includes(action.type) - ) { - effect(action); - } - } + effectRef.current = effect; + typeRef.current = type; useEffect(() => { - const unsubscribe = store.subscribe(handleChange); + const check = (): void => { + const action = (store.getState() as any).action; + if (!action || action.count === lastHandledCountRef.current) { + return; + } + lastHandledCountRef.current = action.count; + if (castArray(typeRef.current).includes(action.type)) { + effectRef.current(action); + } + }; + + check(); + + const unsubscribe = store.subscribe(check); return (): void => unsubscribe(); - }, deps) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, deps); } diff --git a/webclient/src/i18n-backend.ts b/webclient/src/i18n-backend.ts index 9c481f5eb..86642c5e1 100644 --- a/webclient/src/i18n-backend.ts +++ b/webclient/src/i18n-backend.ts @@ -7,12 +7,12 @@ class I18nBackend { static BASE_URL = `${import.meta.env.BASE_URL}locales`; read(language, namespace, callback) { - if (!language[App.Language]) { + if (!language[App.Language as unknown as string]) { callback(true, null); return; } - fetch(`${I18nBackend.BASE_URL}/${language[App.Language]}/${namespace}.json`) + fetch(`${I18nBackend.BASE_URL}/${language[App.Language as unknown as string]}/${namespace}.json`) .then(resp => resp.json().then(json => callback(null, json))) .catch(error => callback(error, null)); } diff --git a/webclient/src/setupTests.ts b/webclient/src/setupTests.ts index d58601667..5c422f120 100644 --- a/webclient/src/setupTests.ts +++ b/webclient/src/setupTests.ts @@ -33,8 +33,30 @@ import '@testing-library/jest-dom/vitest'; // `mockImplementation`, it should set it in that test's body and rely on // the next test overwriting or the global `clearAllMocks` clearing calls — // it should NOT assume the mock is reset to its factory default automatically. +// +// Global snapshot/restore guards for non-`vi.spyOn` globals that tests mutate +// directly. `vi.restoreAllMocks()` only restores `vi.spyOn` targets, so bare +// `Object.defineProperty` writes on `window.location` and `globalThis.WebSocket` +// reassignments leak between tests unless we explicitly capture and restore them. +let _locationDescriptor: PropertyDescriptor | undefined; +let _originalWebSocket: typeof globalThis.WebSocket | undefined; + +beforeEach(() => { + _locationDescriptor = Object.getOwnPropertyDescriptor(window, 'location'); + _originalWebSocket = globalThis.WebSocket; +}); + afterEach(() => { vi.clearAllMocks(); vi.restoreAllMocks(); vi.useRealTimers(); + + const currentLocationDescriptor = Object.getOwnPropertyDescriptor(window, 'location'); + if (currentLocationDescriptor !== _locationDescriptor && _locationDescriptor) { + Object.defineProperty(window, 'location', _locationDescriptor); + } + + if (globalThis.WebSocket !== _originalWebSocket) { + globalThis.WebSocket = _originalWebSocket as typeof globalThis.WebSocket; + } }); diff --git a/webclient/src/store/game/game.dispatch.spec.ts b/webclient/src/store/game/game.dispatch.spec.ts index a06d940e4..0f8102955 100644 --- a/webclient/src/store/game/game.dispatch.spec.ts +++ b/webclient/src/store/game/game.dispatch.spec.ts @@ -1,8 +1,11 @@ -vi.mock('../store', () => ({ store: { dispatch: vi.fn() } })); +// Use `vi.hoisted` so the mocked `store.dispatch` reference stays stable across +// re-runs of the factory under `isolate: false`. See rooms.dispatch.spec.ts for +// the same pattern and rationale. +const { mockDispatch } = vi.hoisted(() => ({ mockDispatch: vi.fn() })); +vi.mock('../store', () => ({ store: { dispatch: mockDispatch } })); import { create } from '@bufbuild/protobuf'; import { Data } from '@app/types'; -import { store } from '..'; import { Actions } from './game.actions'; import { Dispatch } from './game.dispatch'; import { @@ -12,31 +15,35 @@ import { makePlayerProperties, } from './__mocks__/fixtures'; +beforeEach(() => { + mockDispatch.mockClear(); +}); + describe('Dispatch', () => { it('clearStore dispatches Actions.clearStore()', () => { Dispatch.clearStore(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.clearStore()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.clearStore()); }); it('gameJoined dispatches Actions.gameJoined()', () => { const data = create(Data.Event_GameJoinedSchema, { hostId: 1, playerId: 2 }); Dispatch.gameJoined(data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.gameJoined(data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.gameJoined(data)); }); it('gameLeft dispatches Actions.gameLeft()', () => { Dispatch.gameLeft(2); - expect(store.dispatch).toHaveBeenCalledWith(Actions.gameLeft(2)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.gameLeft(2)); }); it('gameClosed dispatches Actions.gameClosed()', () => { Dispatch.gameClosed(3); - expect(store.dispatch).toHaveBeenCalledWith(Actions.gameClosed(3)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.gameClosed(3)); }); it('gameHostChanged dispatches Actions.gameHostChanged()', () => { Dispatch.gameHostChanged(1, 7); - expect(store.dispatch).toHaveBeenCalledWith(Actions.gameHostChanged(1, 7)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.gameHostChanged(1, 7)); }); it('gameStateChanged dispatches Actions.gameStateChanged()', () => { @@ -44,156 +51,156 @@ describe('Dispatch', () => { playerList: [], gameStarted: false, activePlayerId: 0, activePhase: 0, secondsElapsed: 0 }); Dispatch.gameStateChanged(1, data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.gameStateChanged(1, data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.gameStateChanged(1, data)); }); it('playerJoined dispatches Actions.playerJoined()', () => { const props = makePlayerProperties(); Dispatch.playerJoined(1, props); - expect(store.dispatch).toHaveBeenCalledWith(Actions.playerJoined(1, props)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.playerJoined(1, props)); }); it('playerLeft dispatches Actions.playerLeft()', () => { Dispatch.playerLeft(1, 2, 3); - expect(store.dispatch).toHaveBeenCalledWith(Actions.playerLeft(1, 2, 3)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.playerLeft(1, 2, 3)); }); it('playerPropertiesChanged dispatches Actions.playerPropertiesChanged()', () => { const props = makePlayerProperties(); Dispatch.playerPropertiesChanged(1, 2, props); - expect(store.dispatch).toHaveBeenCalledWith(Actions.playerPropertiesChanged(1, 2, props)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.playerPropertiesChanged(1, 2, props)); }); it('kicked dispatches Actions.kicked()', () => { Dispatch.kicked(1); - expect(store.dispatch).toHaveBeenCalledWith(Actions.kicked(1)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.kicked(1)); }); it('cardMoved dispatches Actions.cardMoved()', () => { const data = create(Data.Event_MoveCardSchema, { cardId: 1 }); Dispatch.cardMoved(1, 2, data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.cardMoved(1, 2, data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.cardMoved(1, 2, data)); }); it('cardFlipped dispatches Actions.cardFlipped()', () => { const data = create(Data.Event_FlipCardSchema, { cardId: 1 }); Dispatch.cardFlipped(1, 2, data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.cardFlipped(1, 2, data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.cardFlipped(1, 2, data)); }); it('cardDestroyed dispatches Actions.cardDestroyed()', () => { const data = create(Data.Event_DestroyCardSchema, { cardId: 1 }); Dispatch.cardDestroyed(1, 2, data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.cardDestroyed(1, 2, data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.cardDestroyed(1, 2, data)); }); it('cardAttached dispatches Actions.cardAttached()', () => { const data = create(Data.Event_AttachCardSchema, { cardId: 1 }); Dispatch.cardAttached(1, 2, data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.cardAttached(1, 2, data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.cardAttached(1, 2, data)); }); it('tokenCreated dispatches Actions.tokenCreated()', () => { const data = create(Data.Event_CreateTokenSchema, { cardId: 1 }); Dispatch.tokenCreated(1, 2, data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.tokenCreated(1, 2, data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.tokenCreated(1, 2, data)); }); it('cardAttrChanged dispatches Actions.cardAttrChanged()', () => { const data = create(Data.Event_SetCardAttrSchema, { cardId: 1 }); Dispatch.cardAttrChanged(1, 2, data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.cardAttrChanged(1, 2, data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.cardAttrChanged(1, 2, data)); }); it('cardCounterChanged dispatches Actions.cardCounterChanged()', () => { const data = create(Data.Event_SetCardCounterSchema, { cardId: 1 }); Dispatch.cardCounterChanged(1, 2, data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.cardCounterChanged(1, 2, data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.cardCounterChanged(1, 2, data)); }); it('arrowCreated dispatches Actions.arrowCreated()', () => { const data = create(Data.Event_CreateArrowSchema, { arrowInfo: makeArrow() }); Dispatch.arrowCreated(1, 2, data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.arrowCreated(1, 2, data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.arrowCreated(1, 2, data)); }); it('arrowDeleted dispatches Actions.arrowDeleted()', () => { const data = create(Data.Event_DeleteArrowSchema, { arrowId: 3 }); Dispatch.arrowDeleted(1, 2, data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.arrowDeleted(1, 2, data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.arrowDeleted(1, 2, data)); }); it('counterCreated dispatches Actions.counterCreated()', () => { const data = create(Data.Event_CreateCounterSchema, { counterInfo: makeCounter() }); Dispatch.counterCreated(1, 2, data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.counterCreated(1, 2, data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.counterCreated(1, 2, data)); }); it('counterSet dispatches Actions.counterSet()', () => { const data = create(Data.Event_SetCounterSchema, { counterId: 1, value: 10 }); Dispatch.counterSet(1, 2, data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.counterSet(1, 2, data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.counterSet(1, 2, data)); }); it('counterDeleted dispatches Actions.counterDeleted()', () => { const data = create(Data.Event_DelCounterSchema, { counterId: 1 }); Dispatch.counterDeleted(1, 2, data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.counterDeleted(1, 2, data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.counterDeleted(1, 2, data)); }); it('cardsDrawn dispatches Actions.cardsDrawn()', () => { const data = create(Data.Event_DrawCardsSchema, { number: 2, cards: [makeCard()] }); Dispatch.cardsDrawn(1, 2, data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.cardsDrawn(1, 2, data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.cardsDrawn(1, 2, data)); }); it('cardsRevealed dispatches Actions.cardsRevealed()', () => { const data = create(Data.Event_RevealCardsSchema, { zoneName: 'hand', cards: [] }); Dispatch.cardsRevealed(1, 2, data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.cardsRevealed(1, 2, data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.cardsRevealed(1, 2, data)); }); it('zoneShuffled dispatches Actions.zoneShuffled()', () => { const data = create(Data.Event_ShuffleSchema, { zoneName: 'deck', start: 0, end: 39 }); Dispatch.zoneShuffled(1, 2, data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.zoneShuffled(1, 2, data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.zoneShuffled(1, 2, data)); }); it('dieRolled dispatches Actions.dieRolled()', () => { const data = create(Data.Event_RollDieSchema, { sides: 6, value: 4, values: [4] }); Dispatch.dieRolled(1, 2, data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.dieRolled(1, 2, data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.dieRolled(1, 2, data)); }); it('activePlayerSet dispatches Actions.activePlayerSet()', () => { Dispatch.activePlayerSet(1, 3); - expect(store.dispatch).toHaveBeenCalledWith(Actions.activePlayerSet(1, 3)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.activePlayerSet(1, 3)); }); it('activePhaseSet dispatches Actions.activePhaseSet()', () => { Dispatch.activePhaseSet(1, 2); - expect(store.dispatch).toHaveBeenCalledWith(Actions.activePhaseSet(1, 2)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.activePhaseSet(1, 2)); }); it('turnReversed dispatches Actions.turnReversed()', () => { Dispatch.turnReversed(1, true); - expect(store.dispatch).toHaveBeenCalledWith(Actions.turnReversed(1, true)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.turnReversed(1, true)); }); it('zoneDumped dispatches Actions.zoneDumped()', () => { const data = create(Data.Event_DumpZoneSchema, { zoneOwnerId: 1, zoneName: 'hand', numberCards: 3, isReversed: false }); Dispatch.zoneDumped(1, 2, data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.zoneDumped(1, 2, data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.zoneDumped(1, 2, data)); }); it('zonePropertiesChanged dispatches Actions.zonePropertiesChanged()', () => { const data = create(Data.Event_ChangeZonePropertiesSchema, { zoneName: 'deck', alwaysRevealTopCard: true, alwaysLookAtTopCard: false }); Dispatch.zonePropertiesChanged(1, 2, data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.zonePropertiesChanged(1, 2, data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.zonePropertiesChanged(1, 2, data)); }); it('gameSay dispatches Actions.gameSay()', () => { Dispatch.gameSay(1, 2, 'gg wp'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.gameSay(1, 2, 'gg wp')); + expect(mockDispatch).toHaveBeenCalledWith(Actions.gameSay(1, 2, 'gg wp')); }); }); diff --git a/webclient/src/store/game/game.reducer.spec.ts b/webclient/src/store/game/game.reducer.spec.ts index 048dbbd93..d92a1132c 100644 --- a/webclient/src/store/game/game.reducer.spec.ts +++ b/webclient/src/store/game/game.reducer.spec.ts @@ -898,17 +898,20 @@ describe('2J: Turn, phase, and chat', () => { it('GAME_SAY → appends message with mocked Date.now() as timeReceived', () => { const state = makeState(); - vi.spyOn(Date, 'now').mockReturnValue(123456789); - const result = gamesReducer(state, { - type: Types.GAME_SAY, - gameId: 1, - playerId: 2, - message: 'gg', - }); - vi.restoreAllMocks(); + const dateNowSpy = vi.spyOn(Date, 'now').mockReturnValue(123456789); + try { + const result = gamesReducer(state, { + type: Types.GAME_SAY, + gameId: 1, + playerId: 2, + message: 'gg', + }); - expect(result.games[1].messages).toHaveLength(1); - expect(result.games[1].messages[0]).toEqual({ playerId: 2, message: 'gg', timeReceived: 123456789 }); + expect(result.games[1].messages).toHaveLength(1); + expect(result.games[1].messages[0]).toEqual({ playerId: 2, message: 'gg', timeReceived: 123456789 }); + } finally { + dateNowSpy.mockRestore(); + } }); }); diff --git a/webclient/src/store/rooms/__mocks__/rooms-fixtures.ts b/webclient/src/store/rooms/__mocks__/rooms-fixtures.ts index 940ec76ea..2a4454f2a 100644 --- a/webclient/src/store/rooms/__mocks__/rooms-fixtures.ts +++ b/webclient/src/store/rooms/__mocks__/rooms-fixtures.ts @@ -55,12 +55,15 @@ export function makeGame( }; } -export function makeMessage(overrides: Partial = {}): Enriched.Message { +export function makeMessage(overrides: Partial> = {}): Enriched.Message { + const { timeReceived = 0, ...protoOverrides } = overrides; return { - message: 'hello', - messageType: 0, - timeReceived: 0, - ...overrides, + ...create(Data.Event_RoomSaySchema, { + message: 'hello', + messageType: 0, + ...protoOverrides, + }), + timeReceived, }; } diff --git a/webclient/src/store/rooms/rooms.dispatch.spec.ts b/webclient/src/store/rooms/rooms.dispatch.spec.ts index 9b305c894..aa12c0034 100644 --- a/webclient/src/store/rooms/rooms.dispatch.spec.ts +++ b/webclient/src/store/rooms/rooms.dispatch.spec.ts @@ -1,84 +1,95 @@ -vi.mock('..', () => ({ store: { dispatch: vi.fn() } })); +// Use `vi.hoisted` so the mocked `store.dispatch` reference stays stable across +// re-runs of the factory under `isolate: false`. Other dispatch specs mock the +// same `..` path with their own factories; under the shared module graph, the +// cache entry for `..` can flip between competing `vi.fn()` instances. Asserting +// against the hoisted `mockDispatch` directly (rather than reaching through +// `store.dispatch`) decouples the assertions from whatever the module cache +// currently resolves `store` to. +const { mockDispatch } = vi.hoisted(() => ({ mockDispatch: vi.fn() })); +vi.mock('..', () => ({ store: { dispatch: mockDispatch } })); -import { store } from '..'; import { Actions } from './rooms.actions'; import { Dispatch } from './rooms.dispatch'; import { makeGame, makeMessage, makeRoom, makeUser } from './__mocks__/rooms-fixtures'; import { App } from '@app/types'; +beforeEach(() => { + mockDispatch.mockClear(); +}); + describe('Dispatch', () => { it('clearStore dispatches Actions.clearStore()', () => { Dispatch.clearStore(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.clearStore()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.clearStore()); }); it('updateRooms dispatches Actions.updateRooms()', () => { const rooms = [makeRoom()]; Dispatch.updateRooms(rooms); - expect(store.dispatch).toHaveBeenCalledWith(Actions.updateRooms(rooms)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.updateRooms(rooms)); }); it('joinRoom dispatches Actions.joinRoom()', () => { const roomInfo = makeRoom({ roomId: 2 }); Dispatch.joinRoom(roomInfo); - expect(store.dispatch).toHaveBeenCalledWith(Actions.joinRoom(roomInfo)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.joinRoom(roomInfo)); }); it('leaveRoom dispatches Actions.leaveRoom()', () => { Dispatch.leaveRoom(3); - expect(store.dispatch).toHaveBeenCalledWith(Actions.leaveRoom(3)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.leaveRoom(3)); }); it('addMessage with message.name falsy → dispatches only Actions.addMessage()', () => { const message = { ...makeMessage(), name: undefined }; Dispatch.addMessage(1, message); - expect(store.dispatch).toHaveBeenCalledTimes(1); - expect(store.dispatch).toHaveBeenCalledWith(Actions.addMessage(1, message)); + expect(mockDispatch).toHaveBeenCalledTimes(1); + expect(mockDispatch).toHaveBeenCalledWith(Actions.addMessage(1, message)); }); it('addMessage with message.name truthy → dispatches Actions.addMessage()', () => { const message = { ...makeMessage(), name: 'Alice' }; Dispatch.addMessage(1, message); - expect(store.dispatch).toHaveBeenCalledTimes(1); - expect(store.dispatch).toHaveBeenCalledWith(Actions.addMessage(1, message)); + expect(mockDispatch).toHaveBeenCalledTimes(1); + expect(mockDispatch).toHaveBeenCalledWith(Actions.addMessage(1, message)); }); it('updateGames dispatches Actions.updateGames()', () => { const games = [makeGame()]; Dispatch.updateGames(1, games); - expect(store.dispatch).toHaveBeenCalledWith(Actions.updateGames(1, games)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.updateGames(1, games)); }); it('userJoined dispatches Actions.userJoined()', () => { const user = makeUser(); Dispatch.userJoined(1, user); - expect(store.dispatch).toHaveBeenCalledWith(Actions.userJoined(1, user)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.userJoined(1, user)); }); it('userLeft dispatches Actions.userLeft()', () => { Dispatch.userLeft(1, 'Alice'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.userLeft(1, 'Alice')); + expect(mockDispatch).toHaveBeenCalledWith(Actions.userLeft(1, 'Alice')); }); it('sortGames dispatches Actions.sortGames()', () => { Dispatch.sortGames(1, App.GameSortField.START_TIME, App.SortDirection.ASC); - expect(store.dispatch).toHaveBeenCalledWith( + expect(mockDispatch).toHaveBeenCalledWith( Actions.sortGames(1, App.GameSortField.START_TIME, App.SortDirection.ASC) ); }); it('removeMessages dispatches Actions.removeMessages()', () => { Dispatch.removeMessages(1, 'Alice', 5); - expect(store.dispatch).toHaveBeenCalledWith(Actions.removeMessages(1, 'Alice', 5)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.removeMessages(1, 'Alice', 5)); }); it('gameCreated dispatches Actions.gameCreated()', () => { Dispatch.gameCreated(2); - expect(store.dispatch).toHaveBeenCalledWith(Actions.gameCreated(2)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.gameCreated(2)); }); it('joinedGame dispatches Actions.joinedGame()', () => { Dispatch.joinedGame(1, 5); - expect(store.dispatch).toHaveBeenCalledWith(Actions.joinedGame(1, 5)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.joinedGame(1, 5)); }); }); diff --git a/webclient/src/store/server/server.dispatch.spec.ts b/webclient/src/store/server/server.dispatch.spec.ts index d58d3fcb1..5523e3e2a 100644 --- a/webclient/src/store/server/server.dispatch.spec.ts +++ b/webclient/src/store/server/server.dispatch.spec.ts @@ -1,6 +1,9 @@ -vi.mock('..', () => ({ store: { dispatch: vi.fn() } })); +// Use `vi.hoisted` so the mocked `store.dispatch` reference stays stable across +// re-runs of the factory under `isolate: false`. See rooms.dispatch.spec.ts for +// the same pattern and rationale. +const { mockDispatch } = vi.hoisted(() => ({ mockDispatch: vi.fn() })); +vi.mock('..', () => ({ store: { dispatch: mockDispatch } })); -import { store } from '..'; import { Actions } from './server.actions'; import { Dispatch } from './server.dispatch'; import { App, Data } from '@app/types'; @@ -17,378 +20,382 @@ import { makeWarnListItem, } from './__mocks__/server-fixtures'; +beforeEach(() => { + mockDispatch.mockClear(); +}); + describe('Dispatch', () => { it('initialized dispatches Actions.initialized()', () => { Dispatch.initialized(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.initialized()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.initialized()); }); it('clearStore dispatches Actions.clearStore()', () => { Dispatch.clearStore(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.clearStore()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.clearStore()); }); it('connectionAttempted dispatches Actions.connectionAttempted()', () => { Dispatch.connectionAttempted(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.connectionAttempted()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.connectionAttempted()); }); it('loginSuccessful dispatches Actions.loginSuccessful()', () => { const options = makeLoginSuccessContext(); Dispatch.loginSuccessful(options); - expect(store.dispatch).toHaveBeenCalledWith(Actions.loginSuccessful(options)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.loginSuccessful(options)); }); it('loginFailed dispatches Actions.loginFailed()', () => { Dispatch.loginFailed(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.loginFailed()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.loginFailed()); }); it('connectionFailed dispatches Actions.connectionFailed()', () => { Dispatch.connectionFailed(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.connectionFailed()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.connectionFailed()); }); it('testConnectionSuccessful dispatches Actions.testConnectionSuccessful()', () => { Dispatch.testConnectionSuccessful(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.testConnectionSuccessful()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.testConnectionSuccessful()); }); it('testConnectionFailed dispatches Actions.testConnectionFailed()', () => { Dispatch.testConnectionFailed(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.testConnectionFailed()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.testConnectionFailed()); }); it('updateBuddyList dispatches Actions.updateBuddyList()', () => { const list = [makeUser()]; Dispatch.updateBuddyList(list); - expect(store.dispatch).toHaveBeenCalledWith(Actions.updateBuddyList(list)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.updateBuddyList(list)); }); it('addToBuddyList dispatches Actions.addToBuddyList()', () => { const user = makeUser(); Dispatch.addToBuddyList(user); - expect(store.dispatch).toHaveBeenCalledWith(Actions.addToBuddyList(user)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.addToBuddyList(user)); }); it('removeFromBuddyList dispatches Actions.removeFromBuddyList()', () => { Dispatch.removeFromBuddyList('Alice'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.removeFromBuddyList('Alice')); + expect(mockDispatch).toHaveBeenCalledWith(Actions.removeFromBuddyList('Alice')); }); it('updateIgnoreList dispatches Actions.updateIgnoreList()', () => { const list = [makeUser()]; Dispatch.updateIgnoreList(list); - expect(store.dispatch).toHaveBeenCalledWith(Actions.updateIgnoreList(list)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.updateIgnoreList(list)); }); it('addToIgnoreList dispatches Actions.addToIgnoreList()', () => { const user = makeUser(); Dispatch.addToIgnoreList(user); - expect(store.dispatch).toHaveBeenCalledWith(Actions.addToIgnoreList(user)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.addToIgnoreList(user)); }); it('removeFromIgnoreList dispatches Actions.removeFromIgnoreList()', () => { Dispatch.removeFromIgnoreList('Bob'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.removeFromIgnoreList('Bob')); + expect(mockDispatch).toHaveBeenCalledWith(Actions.removeFromIgnoreList('Bob')); }); it('updateInfo dispatches Actions.updateInfo({ name, version })', () => { Dispatch.updateInfo('Servatrice', '2.9'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.updateInfo({ name: 'Servatrice', version: '2.9' })); + expect(mockDispatch).toHaveBeenCalledWith(Actions.updateInfo({ name: 'Servatrice', version: '2.9' })); }); it('updateStatus dispatches Actions.updateStatus({ state, description })', () => { Dispatch.updateStatus(App.StatusEnum.CONNECTED, 'ok'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.updateStatus({ state: App.StatusEnum.CONNECTED, description: 'ok' })); + expect(mockDispatch).toHaveBeenCalledWith(Actions.updateStatus({ state: App.StatusEnum.CONNECTED, description: 'ok' })); }); it('updateUser dispatches Actions.updateUser()', () => { const user = makeUser(); Dispatch.updateUser(user); - expect(store.dispatch).toHaveBeenCalledWith(Actions.updateUser(user)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.updateUser(user)); }); it('updateUsers dispatches Actions.updateUsers()', () => { const users = [makeUser()]; Dispatch.updateUsers(users); - expect(store.dispatch).toHaveBeenCalledWith(Actions.updateUsers(users)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.updateUsers(users)); }); it('userJoined dispatches Actions.userJoined()', () => { const user = makeUser(); Dispatch.userJoined(user); - expect(store.dispatch).toHaveBeenCalledWith(Actions.userJoined(user)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.userJoined(user)); }); it('userLeft dispatches Actions.userLeft()', () => { Dispatch.userLeft('Carol'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.userLeft('Carol')); + expect(mockDispatch).toHaveBeenCalledWith(Actions.userLeft('Carol')); }); it('viewLogs dispatches Actions.viewLogs()', () => { const logs = [create(Data.ServerInfo_ChatMessageSchema, { targetType: 'room' })]; Dispatch.viewLogs(logs); - expect(store.dispatch).toHaveBeenCalledWith(Actions.viewLogs(logs)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.viewLogs(logs)); }); it('clearLogs dispatches Actions.clearLogs()', () => { Dispatch.clearLogs(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.clearLogs()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.clearLogs()); }); it('serverMessage dispatches Actions.serverMessage()', () => { Dispatch.serverMessage('Welcome!'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.serverMessage('Welcome!')); + expect(mockDispatch).toHaveBeenCalledWith(Actions.serverMessage('Welcome!')); }); it('registrationRequiresEmail dispatches correctly', () => { Dispatch.registrationRequiresEmail(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.registrationRequiresEmail()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.registrationRequiresEmail()); }); it('registrationSuccess dispatches correctly', () => { Dispatch.registrationSuccess(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.registrationSuccess()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.registrationSuccess()); }); it('registrationFailed passes reason and endTime to action', () => { Dispatch.registrationFailed('reason', 999); - expect(store.dispatch).toHaveBeenCalledWith(Actions.registrationFailed('reason', 999)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.registrationFailed('reason', 999)); }); it('registrationFailed passes reason only when no endTime', () => { Dispatch.registrationFailed('plain reason'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.registrationFailed('plain reason', undefined)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.registrationFailed('plain reason', undefined)); }); it('registrationEmailError dispatches correctly', () => { Dispatch.registrationEmailError('bad'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.registrationEmailError('bad')); + expect(mockDispatch).toHaveBeenCalledWith(Actions.registrationEmailError('bad')); }); it('registrationPasswordError dispatches correctly', () => { Dispatch.registrationPasswordError('weak'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.registrationPasswordError('weak')); + expect(mockDispatch).toHaveBeenCalledWith(Actions.registrationPasswordError('weak')); }); it('registrationUserNameError dispatches correctly', () => { Dispatch.registrationUserNameError('taken'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.registrationUserNameError('taken')); + expect(mockDispatch).toHaveBeenCalledWith(Actions.registrationUserNameError('taken')); }); it('accountAwaitingActivation dispatches correctly', () => { const options = makePendingActivationContext(); Dispatch.accountAwaitingActivation(options); - expect(store.dispatch).toHaveBeenCalledWith(Actions.accountAwaitingActivation(options)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.accountAwaitingActivation(options)); }); it('accountActivationSuccess dispatches correctly', () => { Dispatch.accountActivationSuccess(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.accountActivationSuccess()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.accountActivationSuccess()); }); it('accountActivationFailed dispatches correctly', () => { Dispatch.accountActivationFailed(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.accountActivationFailed()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.accountActivationFailed()); }); it('resetPassword dispatches correctly', () => { Dispatch.resetPassword(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.resetPassword()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.resetPassword()); }); it('resetPasswordFailed dispatches correctly', () => { Dispatch.resetPasswordFailed(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.resetPasswordFailed()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.resetPasswordFailed()); }); it('resetPasswordChallenge dispatches correctly', () => { Dispatch.resetPasswordChallenge(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.resetPasswordChallenge()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.resetPasswordChallenge()); }); it('resetPasswordSuccess dispatches correctly', () => { Dispatch.resetPasswordSuccess(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.resetPasswordSuccess()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.resetPasswordSuccess()); }); it('adjustMod dispatches Actions.adjustMod()', () => { Dispatch.adjustMod('Dan', true, false); - expect(store.dispatch).toHaveBeenCalledWith(Actions.adjustMod('Dan', true, false)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.adjustMod('Dan', true, false)); }); it('reloadConfig dispatches correctly', () => { Dispatch.reloadConfig(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.reloadConfig()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.reloadConfig()); }); it('shutdownServer dispatches correctly', () => { Dispatch.shutdownServer(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.shutdownServer()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.shutdownServer()); }); it('updateServerMessage dispatches correctly', () => { Dispatch.updateServerMessage(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.updateServerMessage()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.updateServerMessage()); }); it('accountPasswordChange dispatches correctly', () => { Dispatch.accountPasswordChange(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.accountPasswordChange()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.accountPasswordChange()); }); it('accountEditChanged dispatches correctly', () => { const user = makeUser(); Dispatch.accountEditChanged(user); - expect(store.dispatch).toHaveBeenCalledWith(Actions.accountEditChanged(user)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.accountEditChanged(user)); }); it('accountImageChanged dispatches correctly', () => { const user = makeUser(); Dispatch.accountImageChanged(user); - expect(store.dispatch).toHaveBeenCalledWith(Actions.accountImageChanged(user)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.accountImageChanged(user)); }); it('getUserInfo dispatches correctly', () => { const userInfo = makeUser({ name: 'Frank' }); Dispatch.getUserInfo(userInfo); - expect(store.dispatch).toHaveBeenCalledWith(Actions.getUserInfo(userInfo)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.getUserInfo(userInfo)); }); it('notifyUser dispatches correctly', () => { const notification = create(Data.Event_NotifyUserSchema, { type: 1, warningReason: '', customTitle: '', customContent: '' }); Dispatch.notifyUser(notification); - expect(store.dispatch).toHaveBeenCalledWith(Actions.notifyUser(notification)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.notifyUser(notification)); }); it('serverShutdown dispatches correctly', () => { const data = create(Data.Event_ServerShutdownSchema, { reason: 'maintenance', minutes: 5 }); Dispatch.serverShutdown(data); - expect(store.dispatch).toHaveBeenCalledWith(Actions.serverShutdown(data)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.serverShutdown(data)); }); it('userMessage dispatches correctly', () => { const messageData = create(Data.Event_UserMessageSchema, { senderName: 'Alice', receiverName: 'Bob', message: 'hey' }); Dispatch.userMessage(messageData); - expect(store.dispatch).toHaveBeenCalledWith(Actions.userMessage(messageData)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.userMessage(messageData)); }); it('addToList dispatches correctly', () => { Dispatch.addToList('buddyList', 'Grace'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.addToList('buddyList', 'Grace')); + expect(mockDispatch).toHaveBeenCalledWith(Actions.addToList('buddyList', 'Grace')); }); it('removeFromList dispatches correctly', () => { Dispatch.removeFromList('buddyList', 'Hank'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.removeFromList('buddyList', 'Hank')); + expect(mockDispatch).toHaveBeenCalledWith(Actions.removeFromList('buddyList', 'Hank')); }); it('banFromServer dispatches correctly', () => { Dispatch.banFromServer('Ira'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.banFromServer('Ira')); + expect(mockDispatch).toHaveBeenCalledWith(Actions.banFromServer('Ira')); }); it('banHistory dispatches correctly', () => { const history = [makeBanHistoryItem()]; Dispatch.banHistory('Ira', history); - expect(store.dispatch).toHaveBeenCalledWith(Actions.banHistory('Ira', history)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.banHistory('Ira', history)); }); it('warnHistory dispatches correctly', () => { const history = [makeWarnHistoryItem()]; Dispatch.warnHistory('Jack', history); - expect(store.dispatch).toHaveBeenCalledWith(Actions.warnHistory('Jack', history)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.warnHistory('Jack', history)); }); it('warnListOptions dispatches correctly', () => { const list = [makeWarnListItem()]; Dispatch.warnListOptions(list); - expect(store.dispatch).toHaveBeenCalledWith(Actions.warnListOptions(list)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.warnListOptions(list)); }); it('warnUser dispatches correctly', () => { Dispatch.warnUser('Kelly'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.warnUser('Kelly')); + expect(mockDispatch).toHaveBeenCalledWith(Actions.warnUser('Kelly')); }); it('grantReplayAccess dispatches correctly', () => { Dispatch.grantReplayAccess(7, 'Moe'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.grantReplayAccess(7, 'Moe')); + expect(mockDispatch).toHaveBeenCalledWith(Actions.grantReplayAccess(7, 'Moe')); }); it('forceActivateUser dispatches correctly', () => { Dispatch.forceActivateUser('Ned', 'Moe'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.forceActivateUser('Ned', 'Moe')); + expect(mockDispatch).toHaveBeenCalledWith(Actions.forceActivateUser('Ned', 'Moe')); }); it('getAdminNotes dispatches correctly', () => { Dispatch.getAdminNotes('Ned', 'notes'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.getAdminNotes('Ned', 'notes')); + expect(mockDispatch).toHaveBeenCalledWith(Actions.getAdminNotes('Ned', 'notes')); }); it('updateAdminNotes dispatches correctly', () => { Dispatch.updateAdminNotes('Ned', 'updated'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.updateAdminNotes('Ned', 'updated')); + expect(mockDispatch).toHaveBeenCalledWith(Actions.updateAdminNotes('Ned', 'updated')); }); it('replayList dispatches correctly', () => { const list = [makeReplayMatch()]; Dispatch.replayList(list); - expect(store.dispatch).toHaveBeenCalledWith(Actions.replayList(list)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.replayList(list)); }); it('replayAdded dispatches correctly', () => { const match = makeReplayMatch(); Dispatch.replayAdded(match); - expect(store.dispatch).toHaveBeenCalledWith(Actions.replayAdded(match)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.replayAdded(match)); }); it('replayModifyMatch dispatches correctly', () => { Dispatch.replayModifyMatch(5, true); - expect(store.dispatch).toHaveBeenCalledWith(Actions.replayModifyMatch(5, true)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.replayModifyMatch(5, true)); }); it('replayDeleteMatch dispatches correctly', () => { Dispatch.replayDeleteMatch(5); - expect(store.dispatch).toHaveBeenCalledWith(Actions.replayDeleteMatch(5)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.replayDeleteMatch(5)); }); it('backendDecks dispatches correctly', () => { const deckList = makeDeckList(); Dispatch.backendDecks(deckList); - expect(store.dispatch).toHaveBeenCalledWith(Actions.backendDecks(deckList)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.backendDecks(deckList)); }); it('deckNewDir dispatches correctly', () => { Dispatch.deckNewDir('a/b', 'newFolder'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.deckNewDir('a/b', 'newFolder')); + expect(mockDispatch).toHaveBeenCalledWith(Actions.deckNewDir('a/b', 'newFolder')); }); it('deckDelDir dispatches correctly', () => { Dispatch.deckDelDir('a/b'); - expect(store.dispatch).toHaveBeenCalledWith(Actions.deckDelDir('a/b')); + expect(mockDispatch).toHaveBeenCalledWith(Actions.deckDelDir('a/b')); }); it('deckUpload dispatches correctly', () => { const treeItem = makeDeckTreeItem(); Dispatch.deckUpload('a/b', treeItem); - expect(store.dispatch).toHaveBeenCalledWith(Actions.deckUpload('a/b', treeItem)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.deckUpload('a/b', treeItem)); }); it('deckDelete dispatches correctly', () => { Dispatch.deckDelete(42); - expect(store.dispatch).toHaveBeenCalledWith(Actions.deckDelete(42)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.deckDelete(42)); }); it('gamesOfUser dispatches correctly', () => { const response = create(Data.Response_GetGamesOfUserSchema, { roomList: [], gameList: [] }); Dispatch.gamesOfUser('alice', response); - expect(store.dispatch).toHaveBeenCalledWith(Actions.gamesOfUser('alice', response)); + expect(mockDispatch).toHaveBeenCalledWith(Actions.gamesOfUser('alice', response)); }); it('clearRegistrationErrors dispatches correctly', () => { Dispatch.clearRegistrationErrors(); - expect(store.dispatch).toHaveBeenCalledWith(Actions.clearRegistrationErrors()); + expect(mockDispatch).toHaveBeenCalledWith(Actions.clearRegistrationErrors()); }); }); diff --git a/webclient/src/websocket/commands/session/login.ts b/webclient/src/websocket/commands/session/login.ts index 57332a5a6..4291ddb17 100644 --- a/webclient/src/websocket/commands/session/login.ts +++ b/webclient/src/websocket/commands/session/login.ts @@ -17,14 +17,14 @@ import { export function login(options: Omit, password?: string, passwordSalt?: string): void { const { userName, hashedPassword } = options; - const loginConfig: MessageInitShape = { + const loginConfig = { ...CLIENT_CONFIG, clientid: 'webatrice', userName, ...(passwordSalt ? { hashedPassword: hashedPassword || hashPassword(passwordSalt, password) } : { password }), - }; + } satisfies MessageInitShape; const onLoginError = (message: string, extra?: () => void) => { updateStatus(App.StatusEnum.DISCONNECTED, message); diff --git a/webclient/src/websocket/events/game/joinGame.ts b/webclient/src/websocket/events/game/joinGame.ts index 376801f73..bd59dbb6e 100644 --- a/webclient/src/websocket/events/game/joinGame.ts +++ b/webclient/src/websocket/events/game/joinGame.ts @@ -1,6 +1,6 @@ import { GamePersistence } from '../../persistence'; import type { Data, Enriched } from '@app/types'; -export function joinGame(data: { playerProperties: Data.ServerInfo_PlayerProperties }, meta: Enriched.GameEventMeta): void { +export function joinGame(data: Data.Event_Join, meta: Enriched.GameEventMeta): void { GamePersistence.playerJoined(meta.gameId, data.playerProperties); } diff --git a/webclient/src/websocket/events/game/playerPropertiesChanged.ts b/webclient/src/websocket/events/game/playerPropertiesChanged.ts index dcc0acef6..9f2fab867 100644 --- a/webclient/src/websocket/events/game/playerPropertiesChanged.ts +++ b/webclient/src/websocket/events/game/playerPropertiesChanged.ts @@ -1,6 +1,6 @@ import type { Data, Enriched } from '@app/types'; import { GamePersistence } from '../../persistence'; -export function playerPropertiesChanged(data: { playerProperties: Data.ServerInfo_PlayerProperties }, meta: Enriched.GameEventMeta): void { +export function playerPropertiesChanged(data: Data.Event_PlayerPropertiesChanged, meta: Enriched.GameEventMeta): void { GamePersistence.playerPropertiesChanged(meta.gameId, meta.playerId, data.playerProperties); } diff --git a/webclient/src/websocket/events/session/sessionEvents.spec.ts b/webclient/src/websocket/events/session/sessionEvents.spec.ts index 37948043d..b8993313c 100644 --- a/webclient/src/websocket/events/session/sessionEvents.spec.ts +++ b/webclient/src/websocket/events/session/sessionEvents.spec.ts @@ -204,6 +204,9 @@ describe('addToList', () => { beforeEach(() => { logSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); }); + afterEach(() => { + logSpy.mockRestore(); + }); it('buddy list → addToBuddyList', () => { const data = create(Data.Event_AddToListSchema, { diff --git a/webclient/src/websocket/services/ProtobufService.spec.ts b/webclient/src/websocket/services/ProtobufService.spec.ts index 0c578de77..7430bd435 100644 --- a/webclient/src/websocket/services/ProtobufService.spec.ts +++ b/webclient/src/websocket/services/ProtobufService.spec.ts @@ -1,5 +1,5 @@ -vi.mock('@bufbuild/protobuf', () => ({ - create: vi.fn((_schema: unknown, fields?: Record) => ({ ...(fields ?? {}) })), +vi.mock('@bufbuild/protobuf', async (importOriginal) => ({ + ...(await importOriginal()), fromBinary: vi.fn(), toBinary: vi.fn().mockReturnValue(new Uint8Array()), hasExtension: vi.fn().mockReturnValue(false), @@ -7,20 +7,6 @@ vi.mock('@bufbuild/protobuf', () => ({ setExtension: vi.fn(), })); -vi.mock('../../generated/proto/commands_pb', () => ({ - CommandContainerSchema: {}, -})); - -vi.mock('../../generated/proto/server_message_pb', () => ({ - ServerMessageSchema: {}, - ServerMessage_MessageType: { - RESPONSE: 1, - ROOM_EVENT: 2, - SESSION_EVENT: 3, - GAME_EVENT_CONTAINER: 4, - }, -})); - vi.mock('../events', () => ({ GameEvents: [], RoomEvents: [], @@ -40,6 +26,7 @@ import { GameEvents, RoomEvents, SessionEvents } from '../events'; import type { GameExtensionRegistry } from '../events/game'; import type { RoomExtensionRegistry } from '../events/room'; import type { SessionExtensionRegistry } from '../events/session'; +import { withEventRegistry } from '../../__test-utils__'; import { Data } from '@app/types'; @@ -53,12 +40,20 @@ type ProtobufInternal = ProtobufService & { }; let mockSocket: { isOpen: ReturnType; send: ReturnType }; +let registryTeardowns: Array<() => void>; beforeEach(() => { mockSocket = { isOpen: vi.fn().mockReturnValue(true), send: vi.fn(), }; + registryTeardowns = []; +}); + +afterEach(() => { + while (registryTeardowns.length > 0) { + registryTeardowns.pop()!(); + } }); describe('ProtobufService', () => { @@ -348,8 +343,7 @@ describe('ProtobufService', () => { const mockExt = {} as GenExtension; const payload = { someData: 1 }; - // Temporarily override GameEvents for this test - (GameEvents as GameExtensionRegistry).push([mockExt, handler]); + registryTeardowns.push(withEventRegistry(GameEvents as GameExtensionRegistry, [mockExt, handler])); vi.mocked(hasExtension).mockReturnValue(true); vi.mocked(getExtension).mockReturnValue(payload); @@ -359,7 +353,6 @@ describe('ProtobufService', () => { }, {}); expect(handler).toHaveBeenCalledWith(payload, expect.objectContaining({ gameId: 42, playerId: 5 })); - (GameEvents as GameExtensionRegistry).pop(); }); it('defaults gameId and playerId to -1 when undefined', () => { @@ -368,7 +361,7 @@ describe('ProtobufService', () => { const mockExt = {} as GenExtension; const payload = { someData: 1 }; - (GameEvents as GameExtensionRegistry).push([mockExt, handler]); + registryTeardowns.push(withEventRegistry(GameEvents as GameExtensionRegistry, [mockExt, handler])); vi.mocked(hasExtension).mockReturnValue(true); vi.mocked(getExtension).mockReturnValue(payload); @@ -378,7 +371,6 @@ describe('ProtobufService', () => { }); expect(handler).toHaveBeenCalledWith(payload, expect.objectContaining({ gameId: -1, playerId: -1 })); - (GameEvents as GameExtensionRegistry).pop(); }); }); @@ -405,7 +397,7 @@ describe('ProtobufService', () => { const mockExt = {} as GenExtension; const payload = { roomData: 1 }; - (RoomEvents as RoomExtensionRegistry).push([mockExt, handler]); + registryTeardowns.push(withEventRegistry(RoomEvents as RoomExtensionRegistry, [mockExt, handler])); vi.mocked(hasExtension).mockReturnValue(true); vi.mocked(getExtension).mockReturnValue(payload); @@ -413,7 +405,6 @@ describe('ProtobufService', () => { (service as ProtobufInternal).processRoomEvent(event); expect(handler).toHaveBeenCalledWith(payload, event); - (RoomEvents as RoomExtensionRegistry).pop(); }); }); @@ -431,14 +422,13 @@ describe('ProtobufService', () => { const mockExt = {} as GenExtension; const payload = { sessionData: 1 }; - (SessionEvents as SessionExtensionRegistry).push([mockExt, handler]); + registryTeardowns.push(withEventRegistry(SessionEvents as SessionExtensionRegistry, [mockExt, handler])); vi.mocked(hasExtension).mockReturnValue(true); vi.mocked(getExtension).mockReturnValue(payload); (service as ProtobufInternal).processSessionEvent({ sessionId: 7 }); - expect(handler).toHaveBeenCalledWith(payload); - (SessionEvents as SessionExtensionRegistry).pop(); + expect(handler).toHaveBeenCalledWith(payload, undefined); }); }); diff --git a/webclient/src/websocket/services/ProtobufService.ts b/webclient/src/websocket/services/ProtobufService.ts index 4bc148ece..9c04f10d6 100644 --- a/webclient/src/websocket/services/ProtobufService.ts +++ b/webclient/src/websocket/services/ProtobufService.ts @@ -175,7 +175,7 @@ export class ProtobufService { } for (const [ext, handler] of SessionEvents) { if (hasExtension(event, ext)) { - handler(getExtension(event, ext)); + handler(getExtension(event, ext), undefined); return; } } diff --git a/webclient/src/websocket/services/WebSocketService.spec.ts b/webclient/src/websocket/services/WebSocketService.spec.ts index a806b512f..58e468dbf 100644 --- a/webclient/src/websocket/services/WebSocketService.spec.ts +++ b/webclient/src/websocket/services/WebSocketService.spec.ts @@ -1,4 +1,5 @@ import { installMockWebSocket } from '../__mocks__/helpers'; +import { withMockLocation } from '../../__test-utils__'; import { Mock } from 'vitest'; vi.mock('../WebClient', () => ({ @@ -37,6 +38,7 @@ let MockWS: Mock; let mockInstance: ReturnType['mockInstance']; let restoreWebSocket: ReturnType['restore']; let mockConfig: WebSocketServiceConfig; +let locationRestores: Array<() => void>; beforeEach(() => { vi.useFakeTimers(); @@ -49,9 +51,14 @@ beforeEach(() => { mockConfig = { keepAliveFn: vi.fn(), }; + + locationRestores = []; }); afterEach(() => { + while (locationRestores.length > 0) { + locationRestores.pop()!(); + } restoreWebSocket(); vi.useRealTimers(); }); @@ -88,22 +95,14 @@ describe('WebSocketService', () => { describe('connect', () => { it('creates a WebSocket with wss protocol by default', () => { const service = new WebSocketService(mockConfig); - Object.defineProperty(window, 'location', { - value: { hostname: 'example.com' }, - writable: true, - configurable: true, - }); + locationRestores.push(withMockLocation({ hostname: 'example.com' })); service.connect({ host: 'example.com', port: '8080' }); expect(MockWS).toHaveBeenCalledWith('wss://example.com:8080'); }); it('switches to ws protocol when hostname is localhost', () => { const service = new WebSocketService(mockConfig); - Object.defineProperty(window, 'location', { - value: { hostname: 'localhost' }, - writable: true, - configurable: true, - }); + locationRestores.push(withMockLocation({ hostname: 'localhost' })); service.connect({ host: 'somehost', port: '1234' }); expect(MockWS).toHaveBeenCalledWith('ws://somehost:1234'); }); @@ -243,22 +242,14 @@ describe('WebSocketService', () => { describe('testConnect', () => { it('creates a test WebSocket with correct URL', () => { const service = new WebSocketService(mockConfig); - Object.defineProperty(window, 'location', { - value: { hostname: 'example.com' }, - writable: true, - configurable: true, - }); + locationRestores.push(withMockLocation({ hostname: 'example.com' })); service.testConnect({ host: 'example.com', port: '9000' }); expect(MockWS).toHaveBeenCalledWith('wss://example.com:9000'); }); it('uses ws protocol on localhost', () => { const service = new WebSocketService(mockConfig); - Object.defineProperty(window, 'location', { - value: { hostname: 'localhost' }, - writable: true, - configurable: true, - }); + locationRestores.push(withMockLocation({ hostname: 'localhost' })); service.testConnect({ host: 'h', port: '1' }); expect(MockWS).toHaveBeenCalledWith('ws://h:1'); }); diff --git a/webclient/src/websocket/services/WebSocketService.ts b/webclient/src/websocket/services/WebSocketService.ts index 0ca13b7e6..f4e8fed4e 100644 --- a/webclient/src/websocket/services/WebSocketService.ts +++ b/webclient/src/websocket/services/WebSocketService.ts @@ -65,7 +65,7 @@ export class WebSocketService { } public send(message: Uint8Array): void { - this.socket.send(message); + this.socket.send(message as unknown as ArrayBufferView); } private createWebSocket(url: string): WebSocket { diff --git a/webclient/src/websocket/services/command-options.spec.ts b/webclient/src/websocket/services/command-options.spec.ts index 8548c6c06..23065d8e6 100644 --- a/webclient/src/websocket/services/command-options.spec.ts +++ b/webclient/src/websocket/services/command-options.spec.ts @@ -9,9 +9,11 @@ import { create, getExtension } from '@bufbuild/protobuf'; import { handleResponse } from './command-options'; -beforeEach(() => { - vi.resetAllMocks(); -}); +// NOTE: do NOT call `vi.resetAllMocks()` here — under `isolate: false` it +// resets `vi.fn()` implementations set inside other files' `vi.mock(...)` +// factories, which breaks any spec that relied on those factory defaults +// (e.g. ProtobufService.spec.ts expects `hasExtension` to return `false`). +// The root `setupTests.ts` afterEach already calls `vi.clearAllMocks()`. describe('handleResponse', () => { it('calls onResponse and returns early when provided', () => { diff --git a/webclient/src/websocket/utils/passwordHasher.spec.ts b/webclient/src/websocket/utils/passwordHasher.spec.ts index 388261b36..0d2a22142 100644 --- a/webclient/src/websocket/utils/passwordHasher.spec.ts +++ b/webclient/src/websocket/utils/passwordHasher.spec.ts @@ -1,4 +1,5 @@ -vi.mock('../../generated/proto/event_server_identification_pb', () => ({ +vi.mock('../../generated/proto/event_server_identification_pb', async (importOriginal) => ({ + ...(await importOriginal()), Event_ServerIdentification_ServerOptions: { SupportsPasswordHash: 2 }, })); diff --git a/webclient/vite.config.ts b/webclient/vite.config.ts index cff39dd64..8e96ac328 100644 --- a/webclient/vite.config.ts +++ b/webclient/vite.config.ts @@ -1,9 +1,11 @@ import react from '@vitejs/plugin-react'; import { defineConfig } from 'vite'; -import tsconfigPaths from 'vite-tsconfig-paths'; export default defineConfig({ - plugins: [react(), tsconfigPaths()], + plugins: [react()], + resolve: { + tsconfigPaths: true, + }, publicDir: 'public', build: { outDir: 'build',