diff --git a/apps/web/.gitignore b/apps/web/.gitignore new file mode 100644 index 0000000..41bc172 --- /dev/null +++ b/apps/web/.gitignore @@ -0,0 +1,28 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +node_modules/ +dist/ +test-results/ +playwright-report/ diff --git a/apps/web/README.md b/apps/web/README.md new file mode 100644 index 0000000..7dbf7eb --- /dev/null +++ b/apps/web/README.md @@ -0,0 +1,73 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs) +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + + // Remove tseslint.configs.recommended and replace with this + tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + tseslint.configs.stylisticTypeChecked, + + // Other configs... + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + // Enable lint rules for React + reactX.configs['recommended-typescript'], + // Enable lint rules for React DOM + reactDom.configs.recommended, + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) +``` diff --git a/apps/web/eslint.config.js b/apps/web/eslint.config.js new file mode 100644 index 0000000..5e6b472 --- /dev/null +++ b/apps/web/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/apps/web/index.html b/apps/web/index.html new file mode 100644 index 0000000..b35acdb --- /dev/null +++ b/apps/web/index.html @@ -0,0 +1,19 @@ + + + + + + + + + + + + + HMS 健康管理平台 + + +
+ + + diff --git a/apps/web/package-lock.json b/apps/web/package-lock.json new file mode 100644 index 0000000..181277a --- /dev/null +++ b/apps/web/package-lock.json @@ -0,0 +1,8482 @@ +{ + "name": "web", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "web", + "version": "0.0.0", + "dependencies": { + "@ant-design/charts": "^2.6.7", + "@ant-design/icons": "^6.1.1", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-react": "^1.0.6", + "@xyflow/react": "^12.10.2", + "antd": "^6.3.5", + "axios": "^1.15.0", + "dayjs": "^1.11.20", + "dompurify": "^3.4.5", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "react-router-dom": "^7.14.0", + "snabbdom": "^3.6.3", + "zustand": "^5.0.12" + }, + "devDependencies": { + "@eslint/js": "^9.39.4", + "@playwright/test": "^1.52.0", + "@tailwindcss/vite": "^4.2.2", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", + "@types/dompurify": "^3.2.0", + "@types/node": "^24.12.2", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "@vitest/coverage-v8": "^4.1.5", + "eslint": "^9.39.4", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.4.0", + "jsdom": "^29.0.2", + "msw": "^2.13.6", + "tailwindcss": "^4.2.2", + "typescript": "~6.0.2", + "typescript-eslint": "^8.58.0", + "vite": "^8.0.4", + "vitest": "^4.1.5" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.5.0.tgz", + "integrity": "sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@ant-design/charts": { + "version": "2.6.7", + "resolved": "https://registry.npmjs.org/@ant-design/charts/-/charts-2.6.7.tgz", + "integrity": "sha512-XfmsnspUpfrMlRFGTwmHJ2TPKcosq5a5nSxAfIOpEXAvmJBT2N16oejGTZhUFTzba8W3XtBOziwRAXmDmLUqvA==", + "license": "MIT", + "dependencies": { + "@ant-design/graphs": "^2.1.1", + "@ant-design/plots": "^2.6.7", + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": ">=16.8.4", + "react-dom": ">=16.8.4" + } + }, + "node_modules/@ant-design/charts-util": { + "version": "0.0.1-alpha.7", + "resolved": "https://registry.npmjs.org/@ant-design/charts-util/-/charts-util-0.0.1-alpha.7.tgz", + "integrity": "sha512-Yh0o6EdO6SvdSnStFZMbnUzjyymkVzV+TQ9ymVW9hlVgO/fUkUII3JYSdV+UVcFnYwUF0YiDKuSTLCZNAzg2bQ==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": ">=16.8.4", + "react-dom": ">=16.8.4" + } + }, + "node_modules/@ant-design/colors": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-8.0.1.tgz", + "integrity": "sha512-foPVl0+SWIslGUtD/xBr1p9U4AKzPhNYEseXYRRo5QSzGACYZrQbe11AYJbYfAWnWSpGBx6JjBmSeugUsD9vqQ==", + "license": "MIT", + "dependencies": { + "@ant-design/fast-color": "^3.0.0" + } + }, + "node_modules/@ant-design/cssinjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs/-/cssinjs-2.1.2.tgz", + "integrity": "sha512-2Hy8BnCEH31xPeSLbhhB2ctCPXE2ZnASdi+KbSeS79BNbUhL9hAEe20SkUk+BR8aKTmqb6+FKFruk7w8z0VoRQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@emotion/hash": "^0.8.0", + "@emotion/unitless": "^0.7.5", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "stylis": "^4.3.4" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/cssinjs-utils": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@ant-design/cssinjs-utils/-/cssinjs-utils-2.1.2.tgz", + "integrity": "sha512-5fTHQ158jJJ5dC/ECeyIdZUzKxE/mpEMRZxthyG1sw/AKRHKgJBg00Yi6ACVXgycdje7KahRNvNET/uBccwCnA==", + "license": "MIT", + "dependencies": { + "@ant-design/cssinjs": "^2.1.2", + "@babel/runtime": "^7.23.2", + "@rc-component/util": "^1.4.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/@ant-design/fast-color": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@ant-design/fast-color/-/fast-color-3.0.1.tgz", + "integrity": "sha512-esKJegpW4nckh0o6kV3Tkb7NPIZYbPnnFxmQDUmL08ukXZAvV85TZBr70eGuke/CIArLaP6aw8lt9KILjnWuOw==", + "license": "MIT", + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@ant-design/graphs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@ant-design/graphs/-/graphs-2.1.1.tgz", + "integrity": "sha512-qT3Oo8BWeoAmZEy9gfR6uIk+rczbNJ3sWXKonoOD5koATWv7dY0kgvS1JnhdM1QW4FkfPPJTeQVSlRRUtvWDwA==", + "license": "MIT", + "dependencies": { + "@ant-design/charts-util": "0.0.1-alpha.7", + "@antv/g6": "^5.0.44", + "@antv/g6-extension-react": "^0.2.0", + "@antv/graphin": "^3.0.4", + "lodash": "^4.17.21", + "styled-components": "^6.1.15" + }, + "peerDependencies": { + "react": ">=16.8.4", + "react-dom": ">=16.8.4" + } + }, + "node_modules/@ant-design/icons": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-6.2.5.tgz", + "integrity": "sha512-0hKtoKqTjGFOndUyJLJmC9Cg6k4rEO7rLo6xmgbNJH+/ZX1C57RVals2v1j1knHl9n7Q+sBOveTvn931wLOCKw==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^8.0.1", + "@ant-design/icons-svg": "^4.4.2", + "@rc-component/util": "^1.11.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@ant-design/icons-svg": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz", + "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==", + "license": "MIT" + }, + "node_modules/@ant-design/plots": { + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/@ant-design/plots/-/plots-2.6.8.tgz", + "integrity": "sha512-QsunUs2d5rbq/1BwVhga/siA5H50OaG23YopMYwPD4sPsza6NQzPQ8FM3elNIsD/BIk298tihqX1cJ/MmvVJbQ==", + "license": "MIT", + "dependencies": { + "@ant-design/charts-util": "0.0.3", + "@antv/event-emitter": "^0.1.3", + "@antv/g": "^6.1.7", + "@antv/g2": "^5.2.7", + "@antv/g2-extension-plot": "^0.2.1", + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": ">=16.8.4", + "react-dom": ">=16.8.4" + } + }, + "node_modules/@ant-design/plots/node_modules/@ant-design/charts-util": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@ant-design/charts-util/-/charts-util-0.0.3.tgz", + "integrity": "sha512-x1H7UT6t4dXAyGRoHqlOnEsEqBSTANFGTZEAMI0CWYhYUpp13n0o9grl9oPtoL6FEQMjUBTY+zGJKlHkz8smMw==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": ">=16.8.4", + "react-dom": ">=16.8.4" + } + }, + "node_modules/@ant-design/react-slick": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@ant-design/react-slick/-/react-slick-2.0.0.tgz", + "integrity": "sha512-HMS9sRoEmZey8LsE/Yo6+klhlzU12PisjrVcydW3So7RdklyEd2qehyU6a7Yp+OYN72mgsYs3NFCyP2lCPFVqg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "clsx": "^2.1.1", + "json2mq": "^0.2.0", + "throttle-debounce": "^5.0.0" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@antv/algorithm": { + "version": "0.1.26", + "resolved": "https://registry.npmjs.org/@antv/algorithm/-/algorithm-0.1.26.tgz", + "integrity": "sha512-DVhcFSQ8YQnMNW34Mk8BSsfc61iC1sAnmcfYoXTAshYHuU50p/6b7x3QYaGctDNKWGvi1ub7mPcSY0bK+aN0qg==", + "license": "MIT", + "dependencies": { + "@antv/util": "^2.0.13", + "tslib": "^2.0.0" + } + }, + "node_modules/@antv/algorithm/node_modules/@antv/util": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/@antv/util/-/util-2.0.17.tgz", + "integrity": "sha512-o6I9hi5CIUvLGDhth0RxNSFDRwXeywmt6ExR4+RmVAzIi48ps6HUy+svxOCayvrPBN37uE6TAc2KDofRo0nK9Q==", + "license": "ISC", + "dependencies": { + "csstype": "^3.0.8", + "tslib": "^2.0.3" + } + }, + "node_modules/@antv/component": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/@antv/component/-/component-2.1.11.tgz", + "integrity": "sha512-dTdz8VAd3rpjOaGEZTluz82mtzrP4XCtNlNQyrxY7VNRNcjtvpTLDn57bUL2lRu1T+iklKvgbE2llMriWkq9vQ==", + "license": "MIT", + "dependencies": { + "@antv/g": "^6.1.11", + "@antv/scale": "^0.4.16", + "@antv/util": "^3.3.10", + "svg-path-parser": "^1.1.0" + } + }, + "node_modules/@antv/component/node_modules/@antv/scale": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/@antv/scale/-/scale-0.4.16.tgz", + "integrity": "sha512-5wg/zB5kXHxpTV5OYwJD3ja6R8yTiqIOkjOhmpEJiowkzRlbEC/BOyMvNUq5fqFIHnMCE9woO7+c3zxEQCKPjw==", + "license": "MIT", + "dependencies": { + "@antv/util": "^3.3.7", + "color-string": "^1.5.5", + "fecha": "^4.2.1" + } + }, + "node_modules/@antv/coord": { + "version": "0.4.7", + "resolved": "https://registry.npmjs.org/@antv/coord/-/coord-0.4.7.tgz", + "integrity": "sha512-UTbrMLhwJUkKzqJx5KFnSRpU3BqrdLORJbwUbHK2zHSCT3q3bjcFA//ZYLVfIlwqFDXp/hzfMyRtp0c77A9ZVA==", + "license": "MIT", + "dependencies": { + "@antv/scale": "^0.4.12", + "@antv/util": "^2.0.13", + "gl-matrix": "^3.4.3" + } + }, + "node_modules/@antv/coord/node_modules/@antv/scale": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/@antv/scale/-/scale-0.4.16.tgz", + "integrity": "sha512-5wg/zB5kXHxpTV5OYwJD3ja6R8yTiqIOkjOhmpEJiowkzRlbEC/BOyMvNUq5fqFIHnMCE9woO7+c3zxEQCKPjw==", + "license": "MIT", + "dependencies": { + "@antv/util": "^3.3.7", + "color-string": "^1.5.5", + "fecha": "^4.2.1" + } + }, + "node_modules/@antv/coord/node_modules/@antv/scale/node_modules/@antv/util": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/@antv/util/-/util-3.3.11.tgz", + "integrity": "sha512-FII08DFM4ABh2q5rPYdr0hMtKXRgeZazvXaFYCs7J7uTcWDHUhczab2qOCJLNDugoj8jFag1djb7wS9ehaRYBg==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "gl-matrix": "^3.3.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@antv/coord/node_modules/@antv/util": { + "version": "2.0.17", + "resolved": "https://registry.npmjs.org/@antv/util/-/util-2.0.17.tgz", + "integrity": "sha512-o6I9hi5CIUvLGDhth0RxNSFDRwXeywmt6ExR4+RmVAzIi48ps6HUy+svxOCayvrPBN37uE6TAc2KDofRo0nK9Q==", + "license": "ISC", + "dependencies": { + "csstype": "^3.0.8", + "tslib": "^2.0.3" + } + }, + "node_modules/@antv/event-emitter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@antv/event-emitter/-/event-emitter-0.1.3.tgz", + "integrity": "sha512-4ddpsiHN9Pd4UIlWuKVK1C4IiZIdbwQvy9i7DUSI3xNJ89FPUFt8lxDYj8GzzfdllV0NkJTRxnG+FvLk0llidg==", + "license": "MIT" + }, + "node_modules/@antv/expr": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@antv/expr/-/expr-1.0.2.tgz", + "integrity": "sha512-vrfdmPHkTuiS5voVutKl2l06w1ihBh9A8SFdQPEE+2KMVpkymzGOF1eWpfkbGZ7tiFE15GodVdhhHomD/hdIwg==", + "license": "MIT" + }, + "node_modules/@antv/g": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@antv/g/-/g-6.3.1.tgz", + "integrity": "sha512-WYEKqy86LHB2PzTmrZXrIsIe+3Epeds2f68zceQ+BJtRoGki7Sy4IhlC8LrUMztgfT1t3d/0L745NWZwITroKA==", + "license": "MIT", + "dependencies": { + "@antv/g-lite": "2.7.0", + "@antv/util": "^3.3.5", + "@babel/runtime": "^7.25.6", + "gl-matrix": "^3.4.3", + "html2canvas": "^1.4.1" + } + }, + "node_modules/@antv/g-canvas": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@antv/g-canvas/-/g-canvas-2.2.0.tgz", + "integrity": "sha512-h7zVBBo2aO64DuGKvq9sG+yTU3sCUb9DALCVm7nz8qGPs8hhLuFOkKPEzUDNfNYZGJUGzY8UDtJ3QRGRFcvEQg==", + "license": "MIT", + "dependencies": { + "@antv/g-lite": "2.7.0", + "@antv/g-math": "3.1.0", + "@antv/util": "^3.3.5", + "@babel/runtime": "^7.25.6", + "gl-matrix": "^3.4.3", + "tslib": "^2.5.3" + } + }, + "node_modules/@antv/g-lite": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@antv/g-lite/-/g-lite-2.7.0.tgz", + "integrity": "sha512-uSzgHYa5bwR5L2Au7/5tsOhFmXKZKLPBH90+Q9bP9teVs5VT4kOAi0isPSpDI8uhdDC2/VrfTWu5K9HhWI6FWw==", + "license": "MIT", + "dependencies": { + "@antv/g-math": "3.1.0", + "@antv/util": "^3.3.5", + "@antv/vendor": "^1.0.3", + "@babel/runtime": "^7.25.6", + "eventemitter3": "^5.0.1", + "gl-matrix": "^3.4.3", + "tslib": "^2.5.3" + } + }, + "node_modules/@antv/g-math": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@antv/g-math/-/g-math-3.1.0.tgz", + "integrity": "sha512-DtN1Gj/yI0UiK18nSBsZX8RK0LszGwqfb+cBYWgE+ddyTm8dZnW4tPUhV7QXePsS6/A5hHC+JFpAAK7OEGo5ZQ==", + "license": "MIT", + "dependencies": { + "@antv/util": "^3.3.5", + "@babel/runtime": "^7.25.6", + "gl-matrix": "^3.4.3", + "tslib": "^2.5.3" + } + }, + "node_modules/@antv/g-plugin-dragndrop": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@antv/g-plugin-dragndrop/-/g-plugin-dragndrop-2.1.1.tgz", + "integrity": "sha512-+aesDUJVQDs6UJ2bOBbDlaGAPCfHmU0MbrMTlQlfpwNplWueqtgVAZ3L57oZ2ZGHRWUHiRwZGPjXMBM3O2LELw==", + "license": "MIT", + "dependencies": { + "@antv/g-lite": "2.7.0", + "@antv/util": "^3.3.5", + "@babel/runtime": "^7.25.6", + "tslib": "^2.5.3" + } + }, + "node_modules/@antv/g-svg": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@antv/g-svg/-/g-svg-2.1.1.tgz", + "integrity": "sha512-gVzBkjqA8FzDTbkuIxj6L0Omz/X/hFbYLzK6alWr0sHTfywqP6czcjDUJU8DF2MRIY1Twy55uZYW4dqqLXOXXg==", + "license": "MIT", + "dependencies": { + "@antv/g-lite": "2.7.0", + "@antv/util": "^3.3.5", + "@babel/runtime": "^7.25.6", + "gl-matrix": "^3.4.3", + "tslib": "^2.5.3" + } + }, + "node_modules/@antv/g2": { + "version": "5.4.8", + "resolved": "https://registry.npmjs.org/@antv/g2/-/g2-5.4.8.tgz", + "integrity": "sha512-IvgIpwmT4M5/QAd3Mn2WiHIDeBqFJ4WA2gcZhRRSZuZ2KmgCqZWZwwIT0hc+kIGxwYeDoCQqf//t6FMVu3ryBg==", + "license": "MIT", + "dependencies": { + "@antv/component": "^2.1.9", + "@antv/coord": "^0.4.7", + "@antv/event-emitter": "^0.1.3", + "@antv/expr": "^1.0.2", + "@antv/g": "^6.1.24", + "@antv/g-canvas": "^2.0.43", + "@antv/g-plugin-dragndrop": "^2.0.35", + "@antv/scale": "^0.5.1", + "@antv/util": "^3.3.10", + "@antv/vendor": "^1.0.11", + "flru": "^1.0.2", + "pdfast": "^0.2.0" + } + }, + "node_modules/@antv/g2-extension-plot": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@antv/g2-extension-plot/-/g2-extension-plot-0.2.2.tgz", + "integrity": "sha512-KJXCXO7as+h0hDqirGXf1omrNuYzQmY3VmBmp7lIvkepbQ7sz3pPwy895r1FWETGF3vTk5UeFcAF5yzzBHWgbw==", + "dependencies": { + "@antv/g2": "^5.1.8", + "@antv/util": "^3.3.5", + "@antv/vendor": "^1.0.10" + } + }, + "node_modules/@antv/g6": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@antv/g6/-/g6-5.1.1.tgz", + "integrity": "sha512-50bXxMUf4mChyOv4ePVeWZLwotih9VunKfp0a++Wofv/wCyY8fb9+CV2wouIBCOZnd5ydBRA4NNaX9yLJzqa2w==", + "license": "MIT", + "dependencies": { + "@antv/algorithm": "^0.1.26", + "@antv/component": "^2.1.7", + "@antv/event-emitter": "^0.1.3", + "@antv/g": "^6.1.28", + "@antv/g-canvas": "^2.0.48", + "@antv/g-plugin-dragndrop": "^2.0.38", + "@antv/graphlib": "^2.0.4", + "@antv/hierarchy": "^0.7.1", + "@antv/layout": "^2.0.0", + "@antv/util": "^3.3.11", + "bubblesets-js": "^2.3.4" + } + }, + "node_modules/@antv/g6-extension-react": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@antv/g6-extension-react/-/g6-extension-react-0.2.7.tgz", + "integrity": "sha512-X/zxGiL/kyJ+5xteX1+P2mI07oLw+zfvKcIHxfynL7IGCQCwQ6q91LkJaOlSDTuWhNRXwnwJ4Cf2Nt/9Dhq5Dg==", + "license": "MIT", + "dependencies": { + "@antv/g": "^6.1.24", + "@antv/g-svg": "^2.0.38" + }, + "peerDependencies": { + "@antv/g6": "^5.1.0", + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/@antv/graphin": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@antv/graphin/-/graphin-3.0.5.tgz", + "integrity": "sha512-V/j8R8Ty44wUqxVIYLdpPuIO8WWCTIVq1eBJg5YRunL5t5o5qAFpC/qkQxslbBMWyKdIH0oWBnvHA74riGi7cw==", + "license": "MIT", + "dependencies": { + "@antv/g6": "^5.0.28" + }, + "peerDependencies": { + "react": "^18.0.0 || ^19.1.0", + "react-dom": "^18.0.0 || ^19.1.0" + } + }, + "node_modules/@antv/graphlib": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@antv/graphlib/-/graphlib-2.0.4.tgz", + "integrity": "sha512-zc/5oQlsdk42Z0ib1mGklwzhJ5vczLFiPa1v7DgJkTbgJ2YxRh9xdarf86zI49sKVJmgbweRpJs7Nu5bIiwv4w==", + "license": "MIT", + "dependencies": { + "@antv/event-emitter": "^0.1.3" + } + }, + "node_modules/@antv/hierarchy": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@antv/hierarchy/-/hierarchy-0.7.1.tgz", + "integrity": "sha512-7r22r+HxfcRZp79ZjGmsn97zgC1Iajrv0Mm9DIgx3lPfk+Kme2MG/+EKdZj1iEBsN0rJRzjWVPGL5YrBdVHchw==", + "license": "MIT" + }, + "node_modules/@antv/layout": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@antv/layout/-/layout-2.0.0.tgz", + "integrity": "sha512-aCZ3UdNc40SfT7meFV7QTADY2HCnc0DShVw56CJNTI6oExUIVU736grPuL5Dhb8/JrVaU4Y83QPN/P7KafBzlw==", + "license": "MIT", + "dependencies": { + "@antv/event-emitter": "^0.1.3", + "@antv/expr": "^1.0.2", + "@antv/graphlib": "^2.0.0", + "@antv/util": "^3.3.2", + "comlink": "^4.4.1", + "d3-force": "^3.0.0", + "d3-force-3d": "^3.0.5", + "d3-octree": "^1.0.2", + "d3-quadtree": "^3.0.1", + "dagre": "^0.8.5", + "ml-matrix": "^6.10.4", + "tslib": "^2.8.1" + } + }, + "node_modules/@antv/scale": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@antv/scale/-/scale-0.5.2.tgz", + "integrity": "sha512-rTHRAwvpHWC5PGZF/mJ2ZuTDqwwvVBDRph0Uu5PV9BXwzV7K8+9lsqGJ+XHVLxe8c6bKog5nlzvV/dcYb0d5Ow==", + "license": "MIT", + "dependencies": { + "@antv/util": "^3.3.7", + "color-string": "^1.5.5", + "fecha": "^4.2.1" + } + }, + "node_modules/@antv/util": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/@antv/util/-/util-3.3.11.tgz", + "integrity": "sha512-FII08DFM4ABh2q5rPYdr0hMtKXRgeZazvXaFYCs7J7uTcWDHUhczab2qOCJLNDugoj8jFag1djb7wS9ehaRYBg==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "gl-matrix": "^3.3.0", + "tslib": "^2.3.1" + } + }, + "node_modules/@antv/vendor": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@antv/vendor/-/vendor-1.0.11.tgz", + "integrity": "sha512-LmhPEQ+aapk3barntaiIxJ5VHno/Tyab2JnfdcPzp5xONh/8VSfed4bo/9xKo5HcUAEydko38vYLfj6lJliLiw==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.2.1", + "@types/d3-color": "^3.1.3", + "@types/d3-dispatch": "^3.0.6", + "@types/d3-dsv": "^3.0.7", + "@types/d3-ease": "^3.0.2", + "@types/d3-fetch": "^3.0.7", + "@types/d3-force": "^3.0.10", + "@types/d3-format": "^3.0.4", + "@types/d3-geo": "^3.1.0", + "@types/d3-hierarchy": "^3.1.7", + "@types/d3-interpolate": "^3.0.4", + "@types/d3-path": "^3.1.0", + "@types/d3-quadtree": "^3.0.6", + "@types/d3-random": "^3.0.3", + "@types/d3-scale": "^4.0.9", + "@types/d3-scale-chromatic": "^3.1.0", + "@types/d3-shape": "^3.1.7", + "@types/d3-time": "^3.0.4", + "@types/d3-timer": "^3.0.2", + "d3-array": "^3.2.4", + "d3-color": "^3.1.0", + "d3-dispatch": "^3.0.1", + "d3-dsv": "^3.0.1", + "d3-ease": "^3.0.1", + "d3-fetch": "^3.0.1", + "d3-force": "^3.0.0", + "d3-force-3d": "^3.0.5", + "d3-format": "^3.1.0", + "d3-geo": "^3.1.1", + "d3-geo-projection": "^4.0.0", + "d3-hierarchy": "^3.1.2", + "d3-interpolate": "^3.0.1", + "d3-path": "^3.1.0", + "d3-quadtree": "^3.0.1", + "d3-random": "^3.0.1", + "d3-regression": "^1.3.10", + "d3-scale": "^4.0.2", + "d3-scale-chromatic": "^3.1.0", + "d3-shape": "^3.2.0", + "d3-time": "^3.1.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", + "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@csstools/css-calc": "^3.2.0", + "@csstools/css-color-parser": "^4.1.0", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", + "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.2.1", + "is-potential-custom-element-name": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/generational-cache": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", + "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@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/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", + "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.1.tgz", + "integrity": "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.1.tgz", + "integrity": "sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.0.2", + "@csstools/css-calc": "^3.2.1" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.4.tgz", + "integrity": "sha512-wgsqt92b7C7tQhIdPNxj0n9zuUbQlvAuI1exyzeNrOKOi62SD7ren8zqszmpVREjAOqg8cD2FqYhQfAuKjk4sw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@dnd-kit/accessibility": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/accessibility/-/accessibility-3.1.1.tgz", + "integrity": "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/core": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", + "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", + "license": "MIT", + "dependencies": { + "@dnd-kit/accessibility": "^3.1.1", + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/sortable": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@dnd-kit/sortable/-/sortable-10.0.0.tgz", + "integrity": "sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==", + "license": "MIT", + "dependencies": { + "@dnd-kit/utilities": "^3.2.2", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@dnd-kit/core": "^6.3.0", + "react": ">=16.8.0" + } + }, + "node_modules/@dnd-kit/utilities": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@dnd-kit/utilities/-/utilities-3.2.2.tgz", + "integrity": "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "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/hash": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", + "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "license": "MIT" + }, + "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", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@exodus/bytes": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", + "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@inquirer/ansi": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.7.tgz", + "integrity": "sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + } + }, + "node_modules/@inquirer/confirm": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.1.1.tgz", + "integrity": "sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^11.2.1", + "@inquirer/type": "^4.0.7" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/core": { + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.2.1.tgz", + "integrity": "sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/ansi": "^2.0.7", + "@inquirer/figures": "^2.0.7", + "@inquirer/type": "^4.0.7", + "cli-width": "^4.1.0", + "fast-wrap-ansi": "^0.2.0", + "mute-stream": "^3.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/figures": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.7.tgz", + "integrity": "sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + } + }, + "node_modules/@inquirer/type": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.0.7.tgz", + "integrity": "sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=23.5.0 || ^22.13.0 || ^20.17.0" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@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", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mswjs/interceptors": { + "version": "0.41.9", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.41.9.tgz", + "integrity": "sha512-VVPPgHyQ6ShqnrmDWuxjmUIsO9gWyOZFmuOfLd9LfBGQJwZfy0gvv9pbHSJuoFNIYC7ZDX9aoFwowjcdSC4E8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@mswjs/interceptors/node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true, + "license": "MIT" + }, + "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/@open-draft/deferred-promise": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-3.0.0.tgz", + "integrity": "sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "node_modules/@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@oxc-project/types": { + "version": "0.133.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", + "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@playwright/test": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.60.0.tgz", + "integrity": "sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.60.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@rc-component/async-validator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.1.0.tgz", + "integrity": "sha512-n4HcR5siNUXRX23nDizbZBQPO0ZM/5oTtmKZ6/eqL0L2bo747cklFdZGRN2f+c9qWGICwDzrhW0H7tE9PptdcA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.4" + }, + "engines": { + "node": ">=14.x" + } + }, + "node_modules/@rc-component/cascader": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@rc-component/cascader/-/cascader-1.15.0.tgz", + "integrity": "sha512-ZzpMtwFCRo3fbXHuDnncARJMZQjdqA2w7aDuPofNQt+aDx39st1hgfIpEwTBLhe2Hqsvs/zOr8RTtgxTkCPySw==", + "license": "MIT", + "dependencies": { + "@rc-component/select": "~1.6.0", + "@rc-component/tree": "~1.3.0", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/checkbox": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@rc-component/checkbox/-/checkbox-2.0.0.tgz", + "integrity": "sha512-3CXGPpAR9gsPKeO2N78HAPOzU30UdemD6HGJoWVJOpa6WleaGB5kzZj3v6bdTZab31YuWgY/RxV3VKPctn0DwQ==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/collapse": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rc-component/collapse/-/collapse-1.2.0.tgz", + "integrity": "sha512-ZRYSKSS39qsFx93p26bde7JUZJshsUBEQRlRXPuJYlAiNX0vyYlF5TsAm8JZN3LcF8XvKikdzPbgAtXSbkLUkw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.10.1", + "@rc-component/motion": "^1.1.4", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/color-picker": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-3.1.1.tgz", + "integrity": "sha512-OHaCHLHszCegdXmIq2ZRIZBN/EtpT6Wm8SG/gpzLATHbVKc/avvuKi+zlOuk05FTWvgaMmpxAko44uRJ3M+2pg==", + "license": "MIT", + "dependencies": { + "@ant-design/fast-color": "^3.0.1", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/context": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/context/-/context-2.0.1.tgz", + "integrity": "sha512-HyZbYm47s/YqtP6pKXNMjPEMaukyg7P0qVfgMLzr7YiFNMHbK2fKTAGzms9ykfGHSfyf75nBbgWw+hHkp+VImw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/dialog": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@rc-component/dialog/-/dialog-1.9.0.tgz", + "integrity": "sha512-zbAAogkg4kkKum79sLE6M+vq1jSAW25zdkafrahgcTP9t9S//SD634Znd1A4c8F2Gc12ZKnehGLsVaaOvZzD2A==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.3", + "@rc-component/portal": "^2.1.0", + "@rc-component/util": "^1.9.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/drawer": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@rc-component/drawer/-/drawer-1.4.2.tgz", + "integrity": "sha512-1ib+fZEp6FBu+YvcIktm+nCQ+Q+qIpwpoaJH6opGr4ofh2QMq+qdr5DLC4oCf5qf3pcWX9lUWPYX652k4ini8Q==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.4", + "@rc-component/portal": "^2.1.3", + "@rc-component/util": "^1.9.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/dropdown": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rc-component/dropdown/-/dropdown-1.0.2.tgz", + "integrity": "sha512-6PY2ecUSYhDPhkNHHb4wfeAya04WhpmUSKzdR60G+kMNVUCX2vjT/AgTS0Lz0I/K6xrPMJ3enQbwVpeN3sHCgg==", + "license": "MIT", + "dependencies": { + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.2.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.11.0", + "react-dom": ">=16.11.0" + } + }, + "node_modules/@rc-component/form": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/@rc-component/form/-/form-1.8.2.tgz", + "integrity": "sha512-ZidCvOLmM9Xr+3vzk4UAoR7Aj1W/5IHyrzlBB7sNkygpTeRVrohQSo4TN7W/nARTH+nt8zSAPsn4BEl4zLEO2g==", + "license": "MIT", + "dependencies": { + "@rc-component/async-validator": "^5.1.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/image": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@rc-component/image/-/image-1.9.0.tgz", + "integrity": "sha512-khF7w7xkBH5B1bsBcI1FSUZdkyd1aqpl2eYyILCqCzzQH3XdfehGUaZTnptyaJJfs09/R5hv9jXWyazOMFIClQ==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.0.0", + "@rc-component/portal": "^2.1.2", + "@rc-component/util": "^1.10.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/input": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@rc-component/input/-/input-1.3.1.tgz", + "integrity": "sha512-iFvTUT9W+JC/MSin2aGAk8NqsVlTzcExNC9DZariON1IWirju9NoNeEk47an4Q8iHazkoVI/y1LnDi88+CPcig==", + "license": "MIT", + "dependencies": { + "@rc-component/resize-observer": "^1.1.1", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@rc-component/input-number": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@rc-component/input-number/-/input-number-1.6.2.tgz", + "integrity": "sha512-Gjcq7meZlCOiWN1t1xCC+7/s85humHVokTBI7PJgTfoyw5OWF74y3e6P8PHX104g9+b54jsodFIzyaj6p8LI9w==", + "license": "MIT", + "dependencies": { + "@rc-component/mini-decimal": "^1.0.1", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/mentions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@rc-component/mentions/-/mentions-1.9.0.tgz", + "integrity": "sha512-WUwfFKDSOF5S9UPsNsXcLYtzjTxBGsftTXWRbZuxX6BYrsySISTnujfJNgaaQ6qVzaCDJ35QUkZKvsYxip1C5g==", + "license": "MIT", + "dependencies": { + "@rc-component/input": "~1.3.0", + "@rc-component/menu": "~1.3.0", + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/menu": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@rc-component/menu/-/menu-1.3.1.tgz", + "integrity": "sha512-pSZl9nBPgKgxN0aaW7NilIBEwWsc+43S+ulGdWAg9afak96dNOGWsGx0DLLBB1VQsAJvo6bQMTDzXoPlEHsBEw==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.4", + "@rc-component/overflow": "^1.0.0", + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/mini-decimal": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rc-component/mini-decimal/-/mini-decimal-1.1.3.tgz", + "integrity": "sha512-bk/FJ09fLf+NLODMAFll6CfYrHPBioTedhW6lxDBuuWucJEqFUd4l/D/5JgIi3dina6sYahB8iuPAZTNz2pMxw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.0" + }, + "engines": { + "node": ">=8.x" + } + }, + "node_modules/@rc-component/motion": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@rc-component/motion/-/motion-1.3.2.tgz", + "integrity": "sha512-itfd+GztzJYAb04Z4RkEub1TbJAfZc2Iuy8p44U44xD1F5+fNYFKI3897ijlbIyfvXkTmMm+KGcjkQQGMHywEQ==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.2.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/mutate-observer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/mutate-observer/-/mutate-observer-2.0.1.tgz", + "integrity": "sha512-AyarjoLU5YlxuValRi+w8JRH2Z84TBbFO2RoGWz9d8bSu0FqT8DtugH3xC3BV7mUwlmROFauyWuXFuq4IFbH+w==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.2.0" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/notification": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@rc-component/notification/-/notification-2.0.7.tgz", + "integrity": "sha512-nqZzpf6BPdaj+3ILx7si79LLmqPKyUmQoXa+/9gg0SkH0v1DbD66oJgRMSBEVnd/zUT3D4gwxWIHUKebYf2ZXQ==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.4", + "@rc-component/util": "^1.11.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/overflow": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/overflow/-/overflow-1.0.1.tgz", + "integrity": "sha512-syfmgAABaHCnCDzPwHZ/2tuvIcpOO3jefYZMmfkN+pmo8HKTzsfhS57vxo4ksPdN0By+uWVJhJWNFozNBxi2eA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@rc-component/resize-observer": "^1.0.1", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/pagination": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rc-component/pagination/-/pagination-1.2.0.tgz", + "integrity": "sha512-YcpUFE8dMLfSo6OARJlK6DbHHvrxz7pMGPGmC/caZSJJz6HRKHC1RPP001PRHCvG9Z/veD039uOQmazVuLJzlw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/picker": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@rc-component/picker/-/picker-1.10.0.tgz", + "integrity": "sha512-vVOXP2RVWozwpERGUFAehVH1Jz6o/uRrAb9qSZm1LC+iJs8rvEwFo1bzz2jlOYV+uWwu0dIuG86tnDui14Ea0w==", + "license": "MIT", + "dependencies": { + "@rc-component/overflow": "^1.0.0", + "@rc-component/resize-observer": "^1.0.0", + "@rc-component/trigger": "^3.6.15", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=12.x" + }, + "peerDependencies": { + "date-fns": ">= 2.x", + "dayjs": ">= 1.x", + "luxon": ">= 3.x", + "moment": ">= 2.x", + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + }, + "peerDependenciesMeta": { + "date-fns": { + "optional": true + }, + "dayjs": { + "optional": true + }, + "luxon": { + "optional": true + }, + "moment": { + "optional": true + } + } + }, + "node_modules/@rc-component/portal": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@rc-component/portal/-/portal-2.2.0.tgz", + "integrity": "sha512-oc6FlA+uXCMiwArHsJyHcIkX4q6uKyndrPol2eWX8YPkAnztHOPsFIRtmWG4BMlGE5h7YIRE3NiaJ5VS8Lb1QQ==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.2.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=12.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/progress": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rc-component/progress/-/progress-1.0.2.tgz", + "integrity": "sha512-WZUnH9eGxH1+xodZKqdrHke59uyGZSWgj5HBM5Kwk5BrTMuAORO7VJ2IP5Qbm9aH3n9x3IcesqHHR0NWPBC7fQ==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.2.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/qrcode": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@rc-component/qrcode/-/qrcode-1.1.1.tgz", + "integrity": "sha512-LfLGNymzKdUPjXUbRP+xOhIWY4jQ+YMj5MmWAcgcAq1Ij8XP7tRmAXqyuv96XvLUBE/5cA8hLFl9eO1JQMujrA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.24.7" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/rate": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/rate/-/rate-1.0.1.tgz", + "integrity": "sha512-bkXxeBqDpl5IOC7yL7GcSYjQx9G8H+6kLYQnNZWeBYq2OYIv1MONd6mqKTjnnJYpV0cQIU2z3atdW0j1kttpTw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/resize-observer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@rc-component/resize-observer/-/resize-observer-1.1.2.tgz", + "integrity": "sha512-t/Bb0W8uvL4PYKAB3YcChC+DlHh0Wt5kM7q/J+0qpVEUMLe7Hk5zuvc9km0hMnTFPSx5Z7Wu/fzCLN6erVLE8Q==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.2.0" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/segmented": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@rc-component/segmented/-/segmented-1.3.0.tgz", + "integrity": "sha512-5J/bJ01mbDnoA6P/FW8SxUvKn+OgUSTZJPzCNnTBntG50tzoP7DydGhqxp7ggZXZls7me3mc2EQDXakU3iTVFg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.11.1", + "@rc-component/motion": "^1.1.4", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.0.0", + "react-dom": ">=16.0.0" + } + }, + "node_modules/@rc-component/select": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/@rc-component/select/-/select-1.6.15.tgz", + "integrity": "sha512-SyVCWnqxCQZZcQvQJ/CxSjx2bGma6ds/HtnpkIfZVnt6RoEgbqUmHgD6vrzNarNXwbLXerwVzWwq8F3d1sst7g==", + "license": "MIT", + "dependencies": { + "@rc-component/overflow": "^1.0.0", + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.3.0", + "@rc-component/virtual-list": "^1.0.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@rc-component/slider": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rc-component/slider/-/slider-1.0.1.tgz", + "integrity": "sha512-uDhEPU1z3WDfCJhaL9jfd2ha/Eqpdfxsn0Zb0Xcq1NGQAman0TWaR37OWp2vVXEOdV2y0njSILTMpTfPV1454g==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/steps": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@rc-component/steps/-/steps-1.2.2.tgz", + "integrity": "sha512-/yVIZ00gDYYPHSY0JP+M+s3ZvuXLu2f9rEjQqiUDs7EcYsUYrpJ/1bLj9aI9R7MBR3fu/NGh6RM9u2qGfqp+Nw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.2.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/switch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rc-component/switch/-/switch-1.0.3.tgz", + "integrity": "sha512-Jgi+EbOBquje/XNdofr7xbJQZPYJP+BlPfR0h+WN4zFkdtB2EWqEfvkXJWeipflwjWip0/17rNbxEAqs8hVHfw==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/table": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/@rc-component/table/-/table-1.10.2.tgz", + "integrity": "sha512-b3PjqB9Gp25p5t/zq+9QrbXbodkptT8/zvLmwgd2FNPUUtaYyDnQqfxeD5a7ao8E8lpinLHsi2u2vdfPhyNvAw==", + "license": "MIT", + "dependencies": { + "@rc-component/context": "^2.0.1", + "@rc-component/resize-observer": "^1.0.0", + "@rc-component/util": "^1.11.1", + "@rc-component/virtual-list": "^1.0.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/tabs": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@rc-component/tabs/-/tabs-1.9.1.tgz", + "integrity": "sha512-6mY08Fce6aNOHuGsxbzT+f2ekgL9mg1cGGHkittMlVGymjGg+kGupu5v90sRxcUd/paRU9jclLLXtF/PkK1FUA==", + "license": "MIT", + "dependencies": { + "@rc-component/dropdown": "~1.0.0", + "@rc-component/menu": "~1.3.0", + "@rc-component/motion": "^1.1.3", + "@rc-component/resize-observer": "^1.0.0", + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/tooltip": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@rc-component/tooltip/-/tooltip-1.4.0.tgz", + "integrity": "sha512-8Rx5DCctIlLI4raR0I0xHjVTf1aF48+gKCNeAAo5bmF5VoR5YED+A/XEqzXv9KKqrJDRcd3Wndpxh2hyzrTtSg==", + "license": "MIT", + "dependencies": { + "@rc-component/trigger": "^3.7.1", + "@rc-component/util": "^1.3.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/tour": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@rc-component/tour/-/tour-2.4.0.tgz", + "integrity": "sha512-aui4r4TqmTzwaBgcQxHYep8kM8PTjZFufjokObpy35KfFeZ0k9ArquWFZqegQlH24P14t+F0qO0mGTgzlav1yg==", + "license": "MIT", + "dependencies": { + "@rc-component/portal": "^2.2.0", + "@rc-component/trigger": "^3.0.0", + "@rc-component/util": "^1.7.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/tree": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@rc-component/tree/-/tree-1.3.2.tgz", + "integrity": "sha512-bJFj46wEkpBPnWyTm18XmgAgNQ/4YvprxMOPPY2a6rmhGJYxLuNKEFiL5Qej4Qctu9wHJm8WW+v2SYskafE0kA==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.0.0", + "@rc-component/util": "^1.11.1", + "@rc-component/virtual-list": "^1.2.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=10.x" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@rc-component/tree-select": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@rc-component/tree-select/-/tree-select-1.9.0.tgz", + "integrity": "sha512-GXcFe15a+trUl1/J3OHWQhsVWFpwFpGFK2cqYWZ1sK22Zs3KZTvMwDpzr75PIo1s6QVioVxpE/pRwRopkeDQ6w==", + "license": "MIT", + "dependencies": { + "@rc-component/select": "~1.6.0", + "@rc-component/tree": "~1.3.0", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/@rc-component/trigger": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/@rc-component/trigger/-/trigger-3.9.1.tgz", + "integrity": "sha512-LNsYvz60mrLJ/kRvKcHE7boUvcQfVMCfRqZ71x3Fo9AOiZ1KKIEqkzMA8DNvz2V3Bcvir/vwQNn7JF1NPODQ7Q==", + "license": "MIT", + "dependencies": { + "@rc-component/motion": "^1.1.4", + "@rc-component/portal": "^2.2.0", + "@rc-component/resize-observer": "^1.1.1", + "@rc-component/util": "^1.2.1", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/upload": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@rc-component/upload/-/upload-1.1.1.tgz", + "integrity": "sha512-GvYWSKeaJTOxxC5p6+nOSadzfvXA1h8C/iHFPFZX+szH3JUXrvs+DLiW8YUTBgvMh8m63mJeHrlYlJzAlg+pDA==", + "license": "MIT", + "dependencies": { + "@rc-component/util": "^1.11.1", + "clsx": "^2.1.1" + }, + "peerDependencies": { + "react": ">=16.9.0", + "react-dom": ">=16.9.0" + } + }, + "node_modules/@rc-component/util": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@rc-component/util/-/util-1.11.1.tgz", + "integrity": "sha512-awVlI3ub2vqfqkYxOBc/uQ0efm3jw0wcrhtO/YWLyZfxiKXczKwNbVuhlnyxytDt7H9pbbVQiqr+O6MLATtRYg==", + "license": "MIT", + "dependencies": { + "is-mobile": "^5.0.0", + "react-is": "^18.2.0" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rc-component/virtual-list": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@rc-component/virtual-list/-/virtual-list-1.2.0.tgz", + "integrity": "sha512-iavRm1Jo4GDbASQwdGa7jFyk93RvSOo9xHyBT4QL1pgFJj/Fdf1G+3RErH7/7BmAMvx2AkF62mjGYxDbXsK9TQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.0", + "@rc-component/resize-observer": "^1.0.1", + "@rc-component/util": "^1.4.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=8.x" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", + "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", + "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", + "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", + "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", + "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", + "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", + "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", + "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", + "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", + "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", + "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", + "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", + "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", + "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", + "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz", + "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.21.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz", + "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-x64": "4.3.0", + "@tailwindcss/oxide-freebsd-x64": "4.3.0", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-x64-musl": "4.3.0", + "@tailwindcss/oxide-wasm32-wasi": "4.3.0", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz", + "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz", + "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz", + "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz", + "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz", + "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz", + "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz", + "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz", + "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz", + "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz", + "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.10.0", + "@emnapi/runtime": "^1.10.0", + "@emnapi/wasi-threads": "^1.2.1", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz", + "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz", + "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.0.tgz", + "integrity": "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.0", + "@tailwindcss/oxide": "4.3.0", + "tailwindcss": "4.3.0" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "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", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "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", + "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@transloadit/prettier-bytes": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz", + "integrity": "sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==", + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "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", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "license": "MIT", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "license": "MIT" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "license": "MIT" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "license": "MIT" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/dompurify": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.2.0.tgz", + "integrity": "sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==", + "deprecated": "This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed.", + "dev": true, + "license": "MIT", + "dependencies": { + "dompurify": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@types/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-zx2/Gg0Eg7gwEiOIIh5w9TrhKKTeQh7CPCOPNc0el4pLSwzebA8SmnHwZs2dWlLONvyulykSwGSQxQHLhjGLvQ==", + "license": "MIT" + }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.12.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.4.tgz", + "integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.16", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.16.tgz", + "integrity": "sha512-esJiCAnl0kfpNdE69f3So4WJUXy95dLZydX0KwK46riIHDzHM7O9Vtf9xCHW0PXIqvgqNrswl522kA/5yx+F4w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "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==", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/set-cookie-parser": { + "version": "2.4.10", + "resolved": "https://registry.npmjs.org/@types/set-cookie-parser/-/set-cookie-parser-2.4.10.tgz", + "integrity": "sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/statuses": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.6.tgz", + "integrity": "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==", + "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", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.60.1.tgz", + "integrity": "sha512-JQ4S5GB0tfjO8BuJ4fcX+HodkzJjYBV+7OJ+wLygaX7OGQ7FudyHL4NSCA6ob+w3Yn+5MkKIozOwQhXeM7opVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.60.1", + "@typescript-eslint/type-utils": "8.60.1", + "@typescript-eslint/utils": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.60.1", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.60.1.tgz", + "integrity": "sha512-A0M6ua6H252bVjPvvtSgl2QA4+ET9S5Mtkb2GDyTxIhH/C4qDItT7RQNO5PhMC6NXGYXOR9dIalcDDgBKT7oFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.60.1", + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.60.1.tgz", + "integrity": "sha512-eXkTH2bxmXlqD1RnOPmLZ9ZM9D3VwSx04JOwBnP9RQ+yUA5a2Mu7SfW8uaV2Aon53NJzZlZYuX7tn91Izf+xaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.60.1", + "@typescript-eslint/types": "^8.60.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.60.1.tgz", + "integrity": "sha512-gvI5OQoptnxQnchOirukCuQ55svJSTuD/4k5+pC267xyBtYry748R9/c3tYUzb/iE6RZfllRz2lVulLCHkTm4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.60.1.tgz", + "integrity": "sha512-nh8w4qAteiKuZu3pSSzG/yGKpw0OlkrKnzFmbVRenKaD4qc+7i1GrmZaLVkr8rk4uipiPGMOW4YsM6WmKZ5CvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.60.1.tgz", + "integrity": "sha512-sdwTrpjosW7ANQYJ39ZBF1ZyEMEGVB2UsikrserVM/30a/F1dTLnu9bGxEdosugyu5caigjLrR2qiD11asjI1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1", + "@typescript-eslint/utils": "8.60.1", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.60.1.tgz", + "integrity": "sha512-4h0tY8ppCkdCzcrl2YM5M3my0xsE1Tf8om3owEu5oPWmXwkKRmk0j0LGDzYBGUcAlesEbxBhazqu/K4cu3Ug7w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.60.1.tgz", + "integrity": "sha512-alpRkfG8hlVE5kdJW2GkfgDgXxold3e8e4l6EnmhRmRLbekgAPCCGDVD++sABy9FcgPFroq+uFcCSM1vR57Cew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.60.1", + "@typescript-eslint/tsconfig-utils": "8.60.1", + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/visitor-keys": "8.60.1", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.60.1.tgz", + "integrity": "sha512-h2MPBLoNtjc3qZWfY3Tl51yPorQ2McHn8pJfcMNTcIvrrZrr90Ykffit0yjrPFWQcRcUxzH20+6OcVdW4yHtUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.60.1", + "@typescript-eslint/types": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.60.1.tgz", + "integrity": "sha512-EbGRQg4FhrmwLodl+t3JNAnXHWVr9Vp+Zl1QBZVPY4ByfkzIT8cX3K6QWODHtkIZqqJVEWvhHSx3v5PDHsaQag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.60.1", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@uppy/companion-client": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@uppy/companion-client/-/companion-client-2.2.2.tgz", + "integrity": "sha512-5mTp2iq97/mYSisMaBtFRry6PTgZA6SIL7LePteOV5x0/DxKfrZW3DEiQERJmYpHzy7k8johpm2gHnEKto56Og==", + "license": "MIT", + "dependencies": { + "@uppy/utils": "^4.1.2", + "namespace-emitter": "^2.0.1" + } + }, + "node_modules/@uppy/core": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@uppy/core/-/core-2.3.4.tgz", + "integrity": "sha512-iWAqppC8FD8mMVqewavCz+TNaet6HPXitmGXpGGREGrakZ4FeuWytVdrelydzTdXx6vVKkOmI2FLztGg73sENQ==", + "license": "MIT", + "dependencies": { + "@transloadit/prettier-bytes": "0.0.7", + "@uppy/store-default": "^2.1.1", + "@uppy/utils": "^4.1.3", + "lodash.throttle": "^4.1.1", + "mime-match": "^1.0.2", + "namespace-emitter": "^2.0.1", + "nanoid": "^3.1.25", + "preact": "^10.5.13" + } + }, + "node_modules/@uppy/store-default": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@uppy/store-default/-/store-default-2.1.1.tgz", + "integrity": "sha512-xnpTxvot2SeAwGwbvmJ899ASk5tYXhmZzD/aCFsXePh/v8rNvR2pKlcQUH7cF/y4baUGq3FHO/daKCok/mpKqQ==", + "license": "MIT" + }, + "node_modules/@uppy/utils": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@uppy/utils/-/utils-4.1.3.tgz", + "integrity": "sha512-nTuMvwWYobnJcytDO3t+D6IkVq/Qs4Xv3vyoEZ+Iaf8gegZP+rEyoaFT2CK5XLRMienPyqRqNbIfRuFaOWSIFw==", + "license": "MIT", + "dependencies": { + "lodash.throttle": "^4.1.1" + } + }, + "node_modules/@uppy/xhr-upload": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@uppy/xhr-upload/-/xhr-upload-2.1.3.tgz", + "integrity": "sha512-YWOQ6myBVPs+mhNjfdWsQyMRWUlrDLMoaG7nvf/G6Y3GKZf8AyjFDjvvJ49XWQ+DaZOftGkHmF1uh/DBeGivJQ==", + "license": "MIT", + "dependencies": { + "@uppy/companion-client": "^2.2.2", + "@uppy/utils": "^4.1.2", + "nanoid": "^3.1.25" + }, + "peerDependencies": { + "@uppy/core": "^2.3.3" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.2.tgz", + "integrity": "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@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": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.8.tgz", + "integrity": "sha512-lt3kovsyHwYe00wq4D1ti0Z974fWj4NLp6siqiyEufUpyFwK9Yhi7rBhac9JL5aA0zoMrJqc4vYPZRUnI7l7nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.1.8", + "ast-v8-to-istanbul": "^1.0.0", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.2", + "obug": "^2.1.1", + "std-env": "^4.0.0-rc.1", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.1.8", + "vitest": "4.1.8" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.8.tgz", + "integrity": "sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.8", + "@vitest/utils": "4.1.8", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.8.tgz", + "integrity": "sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.8", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.8.tgz", + "integrity": "sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.8.tgz", + "integrity": "sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.8", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.8.tgz", + "integrity": "sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.8", + "@vitest/utils": "4.1.8", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.8.tgz", + "integrity": "sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.8.tgz", + "integrity": "sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.8", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@wangeditor/basic-modules": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@wangeditor/basic-modules/-/basic-modules-1.1.7.tgz", + "integrity": "sha512-cY9CPkLJaqF05STqfpZKWG4LpxTMeGSIIF1fHvfm/mz+JXatCagjdkbxdikOuKYlxDdeqvOeBmsUBItufDLXZg==", + "license": "MIT", + "dependencies": { + "is-url": "^1.2.4" + }, + "peerDependencies": { + "@wangeditor/core": "1.x", + "dom7": "^3.0.0", + "lodash.throttle": "^4.1.1", + "nanoid": "^3.2.0", + "slate": "^0.72.0", + "snabbdom": "^3.1.0" + } + }, + "node_modules/@wangeditor/code-highlight": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@wangeditor/code-highlight/-/code-highlight-1.0.3.tgz", + "integrity": "sha512-iazHwO14XpCuIWJNTQTikqUhGKyqj+dUNWJ9288Oym9M2xMVHvnsOmDU2sgUDWVy+pOLojReMPgXCsvvNlOOhw==", + "license": "MIT", + "dependencies": { + "prismjs": "^1.23.0" + }, + "peerDependencies": { + "@wangeditor/core": "1.x", + "dom7": "^3.0.0", + "slate": "^0.72.0", + "snabbdom": "^3.1.0" + } + }, + "node_modules/@wangeditor/core": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@wangeditor/core/-/core-1.1.19.tgz", + "integrity": "sha512-KevkB47+7GhVszyYF2pKGKtCSj/YzmClsD03C3zTt+9SR2XWT5T0e3yQqg8baZpcMvkjs1D8Dv4fk8ok/UaS2Q==", + "license": "MIT", + "dependencies": { + "@types/event-emitter": "^0.3.3", + "event-emitter": "^0.3.5", + "html-void-elements": "^2.0.0", + "i18next": "^20.4.0", + "scroll-into-view-if-needed": "^2.2.28", + "slate-history": "^0.66.0" + }, + "peerDependencies": { + "@uppy/core": "^2.1.1", + "@uppy/xhr-upload": "^2.0.3", + "dom7": "^3.0.0", + "is-hotkey": "^0.2.0", + "lodash.camelcase": "^4.3.0", + "lodash.clonedeep": "^4.5.0", + "lodash.debounce": "^4.0.8", + "lodash.foreach": "^4.5.0", + "lodash.isequal": "^4.5.0", + "lodash.throttle": "^4.1.1", + "lodash.toarray": "^4.4.0", + "nanoid": "^3.2.0", + "slate": "^0.72.0", + "snabbdom": "^3.1.0" + } + }, + "node_modules/@wangeditor/editor": { + "version": "5.1.23", + "resolved": "https://registry.npmjs.org/@wangeditor/editor/-/editor-5.1.23.tgz", + "integrity": "sha512-0RxfeVTuK1tktUaPROnCoFfaHVJpRAIE2zdS0mpP+vq1axVQpLjM8+fCvKzqYIkH0Pg+C+44hJpe3VVroSkEuQ==", + "license": "MIT", + "dependencies": { + "@uppy/core": "^2.1.1", + "@uppy/xhr-upload": "^2.0.3", + "@wangeditor/basic-modules": "^1.1.7", + "@wangeditor/code-highlight": "^1.0.3", + "@wangeditor/core": "^1.1.19", + "@wangeditor/list-module": "^1.0.5", + "@wangeditor/table-module": "^1.1.4", + "@wangeditor/upload-image-module": "^1.0.2", + "@wangeditor/video-module": "^1.1.4", + "dom7": "^3.0.0", + "is-hotkey": "^0.2.0", + "lodash.camelcase": "^4.3.0", + "lodash.clonedeep": "^4.5.0", + "lodash.debounce": "^4.0.8", + "lodash.foreach": "^4.5.0", + "lodash.isequal": "^4.5.0", + "lodash.throttle": "^4.1.1", + "lodash.toarray": "^4.4.0", + "nanoid": "^3.2.0", + "slate": "^0.72.0", + "snabbdom": "^3.1.0" + } + }, + "node_modules/@wangeditor/editor-for-react": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@wangeditor/editor-for-react/-/editor-for-react-1.0.6.tgz", + "integrity": "sha512-KJNSfgMr5Blzae3oyaiz20flMKHZHnvsz4bCYQKDCUs/qkvC+xNTnwedlCmhGP187oPWPEypCIYI8Zg6sz0psQ==", + "license": "MIT", + "peerDependencies": { + "@wangeditor/core": ">=1.1.0", + "@wangeditor/editor": ">=5.1.0", + "react": ">=17.0.2", + "react-dom": ">=17.0.2" + } + }, + "node_modules/@wangeditor/list-module": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@wangeditor/list-module/-/list-module-1.0.5.tgz", + "integrity": "sha512-uDuYTP6DVhcYf7mF1pTlmNn5jOb4QtcVhYwSSAkyg09zqxI1qBqsfUnveeDeDqIuptSJhkh81cyxi+MF8sEPOQ==", + "license": "MIT", + "peerDependencies": { + "@wangeditor/core": "1.x", + "dom7": "^3.0.0", + "slate": "^0.72.0", + "snabbdom": "^3.1.0" + } + }, + "node_modules/@wangeditor/table-module": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@wangeditor/table-module/-/table-module-1.1.4.tgz", + "integrity": "sha512-5saanU9xuEocxaemGdNi9t8MCDSucnykEC6jtuiT72kt+/Hhh4nERYx1J20OPsTCCdVr7hIyQenFD1iSRkIQ6w==", + "license": "MIT", + "peerDependencies": { + "@wangeditor/core": "1.x", + "dom7": "^3.0.0", + "lodash.isequal": "^4.5.0", + "lodash.throttle": "^4.1.1", + "nanoid": "^3.2.0", + "slate": "^0.72.0", + "snabbdom": "^3.1.0" + } + }, + "node_modules/@wangeditor/upload-image-module": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@wangeditor/upload-image-module/-/upload-image-module-1.0.2.tgz", + "integrity": "sha512-z81lk/v71OwPDYeQDxj6cVr81aDP90aFuywb8nPD6eQeECtOymrqRODjpO6VGvCVxVck8nUxBHtbxKtjgcwyiA==", + "license": "MIT", + "peerDependencies": { + "@uppy/core": "^2.0.3", + "@uppy/xhr-upload": "^2.0.3", + "@wangeditor/basic-modules": "1.x", + "@wangeditor/core": "1.x", + "dom7": "^3.0.0", + "lodash.foreach": "^4.5.0", + "slate": "^0.72.0", + "snabbdom": "^3.1.0" + } + }, + "node_modules/@wangeditor/video-module": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@wangeditor/video-module/-/video-module-1.1.4.tgz", + "integrity": "sha512-ZdodDPqKQrgx3IwWu4ZiQmXI8EXZ3hm2/fM6E3t5dB8tCaIGWQZhmqd6P5knfkRAd3z2+YRSRbxOGfoRSp/rLg==", + "license": "MIT", + "peerDependencies": { + "@uppy/core": "^2.1.4", + "@uppy/xhr-upload": "^2.0.7", + "@wangeditor/core": "1.x", + "dom7": "^3.0.0", + "nanoid": "^3.2.0", + "slate": "^0.72.0", + "snabbdom": "^3.1.0" + } + }, + "node_modules/@xyflow/react": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@xyflow/react/-/react-12.11.0.tgz", + "integrity": "sha512-na4IO33FSs2OS72hASgZDmTYwFAkef7Z74uBUVrong3ARmQQHfnRUVaCFn1kTt5LbS6pK03TbYjCPGLjLFfziA==", + "license": "MIT", + "dependencies": { + "@xyflow/system": "0.0.77", + "classcat": "^5.0.3", + "zustand": "^4.4.0" + }, + "peerDependencies": { + "@types/react": ">=17", + "@types/react-dom": ">=17", + "react": ">=17", + "react-dom": ">=17" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@xyflow/react/node_modules/zustand": { + "version": "4.5.7", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.5.7.tgz", + "integrity": "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.2.2" + }, + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "immer": ">=9.0.6", + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/@xyflow/system": { + "version": "0.0.77", + "resolved": "https://registry.npmjs.org/@xyflow/system/-/system-0.0.77.tgz", + "integrity": "sha512-qCDCMCQAAgUu8yHnhloHG9F5mwPX5E+Wl8McpYIOPSSXfzFJJoZcwOcsDiAjitVKIg2de1WmJbCHfpcvxprsgg==", + "license": "MIT", + "dependencies": { + "@types/d3-drag": "^3.0.7", + "@types/d3-interpolate": "^3.0.4", + "@types/d3-selection": "^3.0.10", + "@types/d3-transition": "^3.0.8", + "@types/d3-zoom": "^3.0.8", + "d3-drag": "^3.0.0", + "d3-interpolate": "^3.0.1", + "d3-selection": "^3.0.0", + "d3-zoom": "^3.0.0" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/antd": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/antd/-/antd-6.4.3.tgz", + "integrity": "sha512-6H2avkxCGfxcF67r3J2mwm9Ck50el1pks/73vfM1wDsPL/tPtj5vHuauMgJFnrqmq7CH3g8aoZ0VBQbt+jpAsw==", + "license": "MIT", + "dependencies": { + "@ant-design/colors": "^8.0.1", + "@ant-design/cssinjs": "^2.1.2", + "@ant-design/cssinjs-utils": "^2.1.2", + "@ant-design/fast-color": "^3.0.1", + "@ant-design/icons": "^6.2.3", + "@ant-design/react-slick": "~2.0.0", + "@babel/runtime": "^7.29.2", + "@rc-component/cascader": "~1.15.0", + "@rc-component/checkbox": "~2.0.0", + "@rc-component/collapse": "~1.2.0", + "@rc-component/color-picker": "~3.1.1", + "@rc-component/dialog": "~1.9.0", + "@rc-component/drawer": "~1.4.2", + "@rc-component/dropdown": "~1.0.2", + "@rc-component/form": "~1.8.1", + "@rc-component/image": "~1.9.0", + "@rc-component/input": "~1.3.0", + "@rc-component/input-number": "~1.6.2", + "@rc-component/mentions": "~1.9.0", + "@rc-component/menu": "~1.3.0", + "@rc-component/motion": "^1.3.2", + "@rc-component/mutate-observer": "^2.0.1", + "@rc-component/notification": "~2.0.7", + "@rc-component/pagination": "~1.2.0", + "@rc-component/picker": "~1.10.0", + "@rc-component/progress": "~1.0.2", + "@rc-component/qrcode": "~1.1.1", + "@rc-component/rate": "~1.0.1", + "@rc-component/resize-observer": "^1.1.2", + "@rc-component/segmented": "~1.3.0", + "@rc-component/select": "~1.6.15", + "@rc-component/slider": "~1.0.1", + "@rc-component/steps": "~1.2.2", + "@rc-component/switch": "~1.0.3", + "@rc-component/table": "~1.10.0", + "@rc-component/tabs": "~1.9.0", + "@rc-component/tooltip": "~1.4.0", + "@rc-component/tour": "~2.4.0", + "@rc-component/tree": "~1.3.1", + "@rc-component/tree-select": "~1.9.0", + "@rc-component/trigger": "^3.9.0", + "@rc-component/upload": "~1.1.0", + "@rc-component/util": "^1.11.0", + "clsx": "^2.1.1", + "dayjs": "^1.11.11", + "scroll-into-view-if-needed": "^3.1.0", + "throttle-debounce": "^5.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ant-design" + }, + "peerDependencies": { + "react": ">=18.0.0", + "react-dom": ">=18.0.0" + } + }, + "node_modules/antd/node_modules/compute-scroll-into-view": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz", + "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==", + "license": "MIT" + }, + "node_modules/antd/node_modules/scroll-into-view-if-needed": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", + "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", + "license": "MIT", + "dependencies": { + "compute-scroll-into-view": "^3.0.2" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "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/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-v8-to-istanbul": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.3.tgz", + "integrity": "sha512-jCMQ6ZylLPudp0CDfBmQBZUsrh1/8psbmu9ibeVWKuHWD0YrH9YABwlKu5kVEFoT0GCQQW9Z/SxfuEbbkGQCRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.1.tgz", + "integrity": "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.33", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.33.tgz", + "integrity": "sha512-bA6+tcSLpz2tIEdDXZPpPTIuxBcC4+w6SieaYyfigIa4h8GlFxbA17v22Vx3JUtuZQj9SgOsnbK+aTBzyDyEuw==", + "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", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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/bubblesets-js": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/bubblesets-js/-/bubblesets-js-2.3.4.tgz", + "integrity": "sha512-DyMjHmpkS2+xcFNtyN00apJYL3ESdp9fTrkDr5+9Qg/GPqFmcWgGsK1akZnttE1XFxJ/VMy4DNNGMGYtmFp1Sg==", + "license": "MIT" + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001793", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", + "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", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/classcat": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz", + "integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==", + "license": "MIT" + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/comlink": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/comlink/-/comlink-4.4.2.tgz", + "integrity": "sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==", + "license": "Apache-2.0" + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/compute-scroll-into-view": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.20.tgz", + "integrity": "sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==", + "license": "MIT" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "license": "MIT", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/d": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz", + "integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==", + "license": "ISC", + "dependencies": { + "es5-ext": "^0.10.64", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-binarytree": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/d3-binarytree/-/d3-binarytree-1.0.2.tgz", + "integrity": "sha512-cElUNH+sHu95L04m92pG73t2MEJXKu+GeKUN1TJkFsu93E5W8E9Sc3kHEGJKgenGvj19m6upSn2EunvMgMD2Yw==", + "license": "MIT" + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "license": "ISC", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force-3d": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/d3-force-3d/-/d3-force-3d-3.0.6.tgz", + "integrity": "sha512-4tsKHUPLOVkyfEffZo1v6sFHvGFwAIIjt/W8IThbp08DYAsXZck+2pSHEG5W1+gQgEvFLdZkYvmJAbRM2EzMnA==", + "license": "MIT", + "dependencies": { + "d3-binarytree": "1", + "d3-dispatch": "1 - 3", + "d3-octree": "1", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo-projection": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/d3-geo-projection/-/d3-geo-projection-4.0.0.tgz", + "integrity": "sha512-p0bK60CEzph1iqmnxut7d/1kyTmm3UWtPlwdkM31AU+LW+BXazd5zJdoCn7VFxNCHXRngPHRnsNn5uGjLRGndg==", + "license": "ISC", + "dependencies": { + "commander": "7", + "d3-array": "1 - 3", + "d3-geo": "1.12.0 - 3" + }, + "bin": { + "geo2svg": "bin/geo2svg.js", + "geograticule": "bin/geograticule.js", + "geoproject": "bin/geoproject.js", + "geoquantize": "bin/geoquantize.js", + "geostitch": "bin/geostitch.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-octree": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-octree/-/d3-octree-1.1.0.tgz", + "integrity": "sha512-F8gPlqpP+HwRPMO/8uOu5wjH110+6q4cgJvgJT6vlpy3BEaDIKlTZrgHKZSp/i1InRpVfh4puY/kvL6MxK930A==", + "license": "MIT" + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-regression": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/d3-regression/-/d3-regression-1.3.10.tgz", + "integrity": "sha512-PF8GWEL70cHHWpx2jUQXc68r1pyPHIA+St16muk/XRokETzlegj5LriNKg7o4LR0TySug4nHYPJNNRz/W+/Niw==", + "license": "BSD-3-Clause" + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/dagre/-/dagre-0.8.5.tgz", + "integrity": "sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==", + "license": "MIT", + "dependencies": { + "graphlib": "^2.1.8", + "lodash": "^4.17.15" + } + }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/dayjs": { + "version": "1.11.21", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.21.tgz", + "integrity": "sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "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/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", + "peer": true + }, + "node_modules/dom7": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/dom7/-/dom7-3.0.0.tgz", + "integrity": "sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==", + "license": "MIT", + "dependencies": { + "ssr-window": "^3.0.0-alpha.1" + } + }, + "node_modules/dompurify": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.7.tgz", + "integrity": "sha512-2jBxDJY4RR06tQNy4w5FlFH7kfxsQZlufd0sbv+chfHCxeJwrFw2baUDsSwvBISD4K4RDbd0PTfy3uNXsR6siA==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.364", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.364.tgz", + "integrity": "sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.22.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.1.tgz", + "integrity": "sha512-6QEuw3zoX1SJQc7b87aBXke/no+mG2bTBgw29gWMQonLmpEkWoCAVkl+M49e48AZlWzxiDzDZzYdp6kobcyLww==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es5-ext": { + "version": "0.10.64", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz", + "integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==", + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "esniff": "^2.0.1", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "node_modules/es6-symbol": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz", + "integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==", + "license": "ISC", + "dependencies": { + "d": "^1.0.2", + "ext": "^1.7.0" + }, + "engines": { + "node": ">=0.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", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz", + "integrity": "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esniff": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", + "integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==", + "license": "ISC", + "dependencies": { + "d": "^1.0.1", + "es5-ext": "^0.10.62", + "event-emitter": "^0.3.5", + "type": "^2.7.2" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/event-emitter": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", + "integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==", + "license": "MIT", + "dependencies": { + "d": "1", + "es5-ext": "~0.10.14" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "license": "ISC", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/flru": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/flru/-/flru-1.0.2.tgz", + "integrity": "sha512-kWyh8ADvHBFz6ua5xYOPnUroZTT/bwWfrCeL0Wj1dzG4/YOmOcfJ99W8dOVyyynJN35rZ9aCOtHChqQovV7yog==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "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/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gl-matrix": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.4.tgz", + "integrity": "sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==", + "license": "MIT" + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.6.0.tgz", + "integrity": "sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/graphlib": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz", + "integrity": "sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==", + "license": "MIT", + "dependencies": { + "lodash": "^4.17.15" + } + }, + "node_modules/graphql": { + "version": "16.14.1", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.14.1.tgz", + "integrity": "sha512-cQOsSMS/IrDz82PVyRDvf/Q1F/bRbBVjJlh+xYOkI1qw2bWRvWGiWc+m2O0d6l4Bt1fyY+8kzJ8JFWGJqNeDBg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/headers-polyfill": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-5.0.1.tgz", + "integrity": "sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/set-cookie-parser": "^2.4.10", + "set-cookie-parser": "^3.0.1" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/html-void-elements": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-2.0.1.tgz", + "integrity": "sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "license": "MIT", + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/i18next": { + "version": "20.6.1", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-20.6.1.tgz", + "integrity": "sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immer": { + "version": "9.0.21", + "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", + "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/immer" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-any-array": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-any-array/-/is-any-array-3.0.0.tgz", + "integrity": "sha512-o4h+tylWykC4BD1vaejp6gDxoM13bwW8FGuNs4yIKpj8xbBJcRxJx8vZpq0dCr7ZDEfeKjmsi/euolKhX6f/ww==", + "license": "MIT" + }, + "node_modules/is-arrayish": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hotkey": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/is-hotkey/-/is-hotkey-0.2.0.tgz", + "integrity": "sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==", + "license": "MIT" + }, + "node_modules/is-mobile": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-mobile/-/is-mobile-5.0.0.tgz", + "integrity": "sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ==", + "license": "MIT" + }, + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-url": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/is-url/-/is-url-1.2.4.tgz", + "integrity": "sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==", + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", + "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^5.1.11", + "@asamuzakjp/dom-selector": "^7.1.1", + "@bramus/specificity": "^2.4.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.3", + "@exodus/bytes": "^1.15.0", + "css-tree": "^3.2.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.3.5", + "parse5": "^8.0.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.1", + "undici": "^7.25.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.1", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/lru-cache": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "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", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json2mq": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/json2mq/-/json2mq-0.2.0.tgz", + "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", + "license": "MIT", + "dependencies": { + "string-convert": "^0.2.0" + } + }, + "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/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "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/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "license": "MIT" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "license": "MIT" + }, + "node_modules/lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==", + "license": "MIT" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "license": "MIT" + }, + "node_modules/lodash.toarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", + "integrity": "sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==", + "license": "MIT" + }, + "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==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.3.tgz", + "integrity": "sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/mime-match/-/mime-match-1.0.2.tgz", + "integrity": "sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==", + "license": "ISC", + "dependencies": { + "wildcard": "^1.1.0" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ml-array-max": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ml-array-max/-/ml-array-max-2.0.0.tgz", + "integrity": "sha512-QQZ4kENwpWmyNb98UXRDFXrmtIXuXtt1+bSbda/2KA85+F+rrJP8hZk6QOkCQXM2Th9mUDYdq/PNByPdT9ID4A==", + "license": "MIT", + "dependencies": { + "is-any-array": "^3.0.0" + } + }, + "node_modules/ml-array-min": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ml-array-min/-/ml-array-min-2.0.0.tgz", + "integrity": "sha512-GRj6Ky6sW9vGL6yIjgsHmXZ9YgrdmcQ8nCxPqEGeKc6dkfYg1XDYxGFxADUjNuZyoCd5PUscWAS4N+cFaX6hFg==", + "license": "MIT", + "dependencies": { + "is-any-array": "^3.0.0" + } + }, + "node_modules/ml-array-rescale": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ml-array-rescale/-/ml-array-rescale-2.0.0.tgz", + "integrity": "sha512-2GGtKfSno94/kIloWGvpp/U5Q5vLvLrza+SAaGsLeo6Xj4mEbA6Gqx+oTfZFkxnd1grT2X007HfJNs3T5BsiVg==", + "license": "MIT", + "dependencies": { + "is-any-array": "^3.0.0", + "ml-array-max": "^2.0.0", + "ml-array-min": "^2.0.0" + } + }, + "node_modules/ml-matrix": { + "version": "6.12.2", + "resolved": "https://registry.npmjs.org/ml-matrix/-/ml-matrix-6.12.2.tgz", + "integrity": "sha512-GC+BnW+pBh8Auap8goAxY0senAmF0IEoc3HNVSfnfbvGw0buuDIYb9kAKMS1l+GiwJ1rfK2bzJ8IHhwjzATSFA==", + "license": "MIT", + "dependencies": { + "is-any-array": "^3.0.0", + "ml-array-rescale": "^2.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/msw": { + "version": "2.14.6", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.14.6.tgz", + "integrity": "sha512-ALe+N10S72cyx94cMcy3Zs4HhXCj35sgeAL4c+WTvKi0zWnbd8/h0lcFqv0mb2P+aSgAdD7p9HzvA0DiUPxsyg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@inquirer/confirm": "^6.0.11", + "@mswjs/interceptors": "^0.41.3", + "@open-draft/deferred-promise": "^3.0.0", + "@types/statuses": "^2.0.6", + "cookie": "^1.1.1", + "graphql": "^16.13.2", + "headers-polyfill": "^5.0.1", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.3", + "path-to-regexp": "^6.3.0", + "picocolors": "^1.1.1", + "rettime": "^0.11.11", + "statuses": "^2.0.2", + "strict-event-emitter": "^0.5.1", + "tough-cookie": "^6.0.1", + "type-fest": "^5.5.0", + "until-async": "^3.0.2", + "yargs": "^17.7.2" + }, + "bin": { + "msw": "cli/index.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mswjs" + }, + "peerDependencies": { + "typescript": ">= 4.8.x" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/mute-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-3.0.0.tgz", + "integrity": "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/namespace-emitter": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/namespace-emitter/-/namespace-emitter-2.0.1.tgz", + "integrity": "sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==", + "license": "ISC" + }, + "node_modules/node-releases": { + "version": "2.0.46", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz", + "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/outvariant": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", + "dev": true, + "license": "MIT" + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pdfast": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/pdfast/-/pdfast-0.2.0.tgz", + "integrity": "sha512-cq6TTu6qKSFUHwEahi68k/kqN2mfepjkGrG9Un70cgdRRKLKY6Rf8P8uvP2NvZktaQZNF3YE7agEkLj0vGK9bA==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "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/playwright": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz", + "integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.60.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz", + "integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "node_modules/preact": { + "version": "10.29.2", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.2.tgz", + "integrity": "sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/prismjs": { + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/proxy-from-env": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", + "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", + "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.7" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/react-router": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.16.0.tgz", + "integrity": "sha512-wArC8lVyJb3+jM9OpDyW6hLCizACWkvQR/sSGqSs+o5uEXEtGlqdZ4v8hENR3Jad6i+LRkK93q/+bQAcvl6V1A==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.16.0.tgz", + "integrity": "sha512-kMUAbimWB5FVbF4Bce4bJsiKJWLIUHq/mEG8+CFDnCSgltptBiG5nguducmsJeGKytlCvQud9Qhzpn49iduTlA==", + "license": "MIT", + "dependencies": { + "react-router": "7.16.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-router/node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.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", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rettime": { + "version": "0.11.11", + "resolved": "https://registry.npmjs.org/rettime/-/rettime-0.11.11.tgz", + "integrity": "sha512-ILJRqVWBCTlg9r42fFgwVZx1gnFAcQF8mRoMkbgQfIrjEDf9nbBFDFx00oloOa+Q869FUtaYDXZvEfnecQSCoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/rolldown": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", + "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.133.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.3", + "@rolldown/binding-darwin-arm64": "1.0.3", + "@rolldown/binding-darwin-x64": "1.0.3", + "@rolldown/binding-freebsd-x64": "1.0.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", + "@rolldown/binding-linux-arm64-gnu": "1.0.3", + "@rolldown/binding-linux-arm64-musl": "1.0.3", + "@rolldown/binding-linux-ppc64-gnu": "1.0.3", + "@rolldown/binding-linux-s390x-gnu": "1.0.3", + "@rolldown/binding-linux-x64-gnu": "1.0.3", + "@rolldown/binding-linux-x64-musl": "1.0.3", + "@rolldown/binding-openharmony-arm64": "1.0.3", + "@rolldown/binding-wasm32-wasi": "1.0.3", + "@rolldown/binding-win32-arm64-msvc": "1.0.3", + "@rolldown/binding-win32-x64-msvc": "1.0.3" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", + "license": "BSD-3-Clause" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/scheduler": { + "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/scroll-into-view-if-needed": { + "version": "2.2.31", + "resolved": "https://registry.npmjs.org/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.31.tgz", + "integrity": "sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==", + "license": "MIT", + "dependencies": { + "compute-scroll-into-view": "^1.0.20" + } + }, + "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/set-cookie-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.0.tgz", + "integrity": "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/slate": { + "version": "0.72.8", + "resolved": "https://registry.npmjs.org/slate/-/slate-0.72.8.tgz", + "integrity": "sha512-/nJwTswQgnRurpK+bGJFH1oM7naD5qDmHd89JyiKNT2oOKD8marW0QSBtuFnwEbL5aGCS8AmrhXQgNOsn4osAw==", + "license": "MIT", + "dependencies": { + "immer": "^9.0.6", + "is-plain-object": "^5.0.0", + "tiny-warning": "^1.0.3" + } + }, + "node_modules/slate-history": { + "version": "0.66.0", + "resolved": "https://registry.npmjs.org/slate-history/-/slate-history-0.66.0.tgz", + "integrity": "sha512-6MWpxGQZiMvSINlCbMW43E2YBSVMCMCIwQfBzGssjWw4kb0qfvj0pIdblWNRQZD0hR6WHP+dHHgGSeVdMWzfng==", + "license": "MIT", + "dependencies": { + "is-plain-object": "^5.0.0" + }, + "peerDependencies": { + "slate": ">=0.65.3" + } + }, + "node_modules/snabbdom": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/snabbdom/-/snabbdom-3.6.3.tgz", + "integrity": "sha512-W2lHLLw2qR2Vv0DcMmcxXqcfdBaIcoN+y/86SmHv8fn4DazEQSH6KN3TjZcWvwujW56OHiiirsbHWZb4vx/0fg==", + "license": "MIT", + "engines": { + "node": ">=12.17.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssr-window": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ssr-window/-/ssr-window-3.0.0.tgz", + "integrity": "sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==", + "license": "MIT" + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz", + "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==", + "license": "MIT" + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/styled-components": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.4.2.tgz", + "integrity": "sha512-xZBhBJsMtGqb+aKcwKgaT+BtuFums9VynX2JRvXJGTx5UfZzN12rk5r4nVdhXYvRw+hE7yiYxVrOqJZaK2+Txg==", + "license": "MIT", + "dependencies": { + "@emotion/is-prop-valid": "1.4.0", + "css-to-react-native": "3.2.0", + "csstype": "3.2.3", + "stylis": "4.3.6" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "css-to-react-native": ">= 3.2.0", + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0", + "react-native": ">= 0.68.0" + }, + "peerDependenciesMeta": { + "css-to-react-native": { + "optional": true + }, + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/styled-components/node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "license": "MIT" + }, + "node_modules/stylis": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.4.0.tgz", + "integrity": "sha512-5Z9ZpRzfuH6l/UAvCPAPUo3665Nk2wLaZU3x+TLHKVzIz33+sbJqbtrYoC3KD4/uVOr2Zp+L0LySezP9OHV9yA==", + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/svg-path-parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/svg-path-parser/-/svg-path-parser-1.1.0.tgz", + "integrity": "sha512-jGCUqcQyXpfe38R7RFfhrMyfXcBmpMNJI/B+4CE9/Unkh98UporAc461GTthv+TVDuZXsBx7/WiwJb1Oh4tt4A==", + "license": "MIT" + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tagged-tag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", + "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", + "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "license": "MIT", + "dependencies": { + "utrie": "^1.0.2" + } + }, + "node_modules/throttle-debounce": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-5.0.2.tgz", + "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==", + "license": "MIT", + "engines": { + "node": ">=12.22" + } + }, + "node_modules/tiny-warning": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.2.tgz", + "integrity": "sha512-kCwffuaH8ntKtygnWe1b4BJKWiCUH30n5KfoTr6IchcXOwR7chAOFJxFrH3vjANafUYrIA4a7SDL+nn7SiR4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.4.2" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.2.tgz", + "integrity": "sha512-nwEyF4vl4RSJjwSjBUmOSxc3BFPoIFdlRthJ6e+5v9P3bHNsoD06UjuqMUspqp7vsEZ1beaHi1km+optiE17yA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", + "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz", + "integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==", + "license": "ISC" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.7.0.tgz", + "integrity": "sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "dependencies": { + "tagged-tag": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.60.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.60.1.tgz", + "integrity": "sha512-6m5hkkRAp8lKvhVpcprAIn5KkehQEh+47oHH2VGnExEh7dhNxXlg6GPAOIu6TxbVQxhebrJDvjl3020ooiWCMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.60.1", + "@typescript-eslint/parser": "8.60.1", + "@typescript-eslint/typescript-estree": "8.60.1", + "@typescript-eslint/utils": "8.60.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.27.0.tgz", + "integrity": "sha512-+t2Z/GwkZQDtu00813aP66ygViGtPHKhhoFZpQKpKrE+9jIgES+Zw+mFNaDWOVRKiuJjuqKHzD3B1sfGg8+ZOQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/until-async": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/until-async/-/until-async-3.0.2.tgz", + "integrity": "sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/kettanaito" + } + }, + "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", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "license": "MIT", + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, + "node_modules/vite": { + "version": "8.0.16", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", + "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.15", + "rolldown": "1.0.3", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.18", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/vitest": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.8.tgz", + "integrity": "sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.8", + "@vitest/mocker": "4.1.8", + "@vitest/pretty-format": "4.1.8", + "@vitest/runner": "4.1.8", + "@vitest/snapshot": "4.1.8", + "@vitest/spy": "4.1.8", + "@vitest/utils": "4.1.8", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.8", + "@vitest/browser-preview": "4.1.8", + "@vitest/browser-webdriverio": "4.1.8", + "@vitest/coverage-istanbul": "4.1.8", + "@vitest/coverage-v8": "4.1.8", + "@vitest/ui": "4.1.8", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-url": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wildcard": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-1.1.2.tgz", + "integrity": "sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng==", + "license": "MIT" + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "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/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, + "node_modules/zustand": { + "version": "5.0.14", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.14.tgz", + "integrity": "sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + } + } +} diff --git a/apps/web/package.json b/apps/web/package.json new file mode 100644 index 0000000..51e17b4 --- /dev/null +++ b/apps/web/package.json @@ -0,0 +1,63 @@ +{ + "name": "web", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview", + "test": "vitest", + "test:coverage": "vitest run --coverage", + "test:e2e": "playwright test", + "test:e2e:ui": "playwright test --ui" + }, + "dependencies": { + "@ant-design/charts": "^2.6.7", + "@ant-design/icons": "^6.1.1", + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-react": "^1.0.6", + "@xyflow/react": "^12.10.2", + "antd": "^6.3.5", + "axios": "^1.15.0", + "dayjs": "^1.11.20", + "dompurify": "^3.4.5", + "react": "^19.2.4", + "react-dom": "^19.2.4", + "react-router-dom": "^7.14.0", + "snabbdom": "^3.6.3", + "zustand": "^5.0.12" + }, + "devDependencies": { + "@eslint/js": "^9.39.4", + "@playwright/test": "^1.52.0", + "@tailwindcss/vite": "^4.2.2", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.2", + "@types/dompurify": "^3.2.0", + "@types/node": "^24.12.2", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "@vitest/coverage-v8": "^4.1.5", + "eslint": "^9.39.4", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.4.0", + "jsdom": "^29.0.2", + "msw": "^2.13.6", + "tailwindcss": "^4.2.2", + "typescript": "~6.0.2", + "typescript-eslint": "^8.58.0", + "vite": "^8.0.4", + "vitest": "^4.1.5" + }, + "msw": { + "workerDirectory": [ + "public" + ] + } +} \ No newline at end of file diff --git a/apps/web/playwright.config.ts b/apps/web/playwright.config.ts new file mode 100644 index 0000000..4604271 --- /dev/null +++ b/apps/web/playwright.config.ts @@ -0,0 +1,32 @@ +import { defineConfig, devices } from '@playwright/test'; + +export default defineConfig({ + testDir: './e2e', + testMatch: ['smoke/**/*.spec.ts', 'flows/**/*.spec.ts'], + timeout: 60_000, + retries: 1, + fullyParallel: false, + workers: 1, + forbidOnly: !!process.env.CI, + reporter: [['html', { open: 'never' }], ['list']], + use: { + baseURL: process.env.E2E_BASE_URL || 'http://localhost:5174', + headless: true, + screenshot: 'only-on-failure', + trace: 'on-first-retry', + video: 'retain-on-failure', + }, + projects: [ + { + name: 'chromium', + use: { ...devices['Desktop Chrome'] }, + }, + ], + globalSetup: './e2e/check-readiness', + webServer: { + command: 'pnpm dev', + port: 5174, + reuseExistingServer: true, + timeout: 30_000, + }, +}); diff --git a/apps/web/pnpm-lock.yaml b/apps/web/pnpm-lock.yaml new file mode 100644 index 0000000..b510873 --- /dev/null +++ b/apps/web/pnpm-lock.yaml @@ -0,0 +1,5980 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@ant-design/charts': + specifier: ^2.6.7 + version: 2.6.7(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@ant-design/icons': + specifier: ^6.1.1 + version: 6.1.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@dnd-kit/core': + specifier: ^6.3.1 + version: 6.3.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@dnd-kit/sortable': + specifier: ^10.0.0 + version: 10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5) + '@wangeditor/editor': + specifier: ^5.1.23 + version: 5.1.23 + '@wangeditor/editor-for-react': + specifier: ^1.0.6 + version: 1.0.6(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(@wangeditor/editor@5.1.23)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@xyflow/react': + specifier: ^12.10.2 + version: 12.10.2(@types/react@19.2.14)(immer@9.0.21)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + antd: + specifier: ^6.3.5 + version: 6.3.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + axios: + specifier: ^1.15.0 + version: 1.15.0 + dayjs: + specifier: ^1.11.20 + version: 1.11.20 + dompurify: + specifier: ^3.4.5 + version: 3.4.5 + react: + specifier: ^19.2.4 + version: 19.2.5 + react-dom: + specifier: ^19.2.4 + version: 19.2.5(react@19.2.5) + react-router-dom: + specifier: ^7.14.0 + version: 7.14.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + snabbdom: + specifier: ^3.6.3 + version: 3.6.3 + zustand: + specifier: ^5.0.12 + version: 5.0.12(@types/react@19.2.14)(immer@9.0.21)(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5)) + devDependencies: + '@eslint/js': + specifier: ^9.39.4 + version: 9.39.4 + '@playwright/test': + specifier: ^1.52.0 + version: 1.59.1 + '@tailwindcss/vite': + specifier: ^4.2.2 + version: 4.2.2(vite@8.0.8(@types/node@24.12.2)(jiti@2.6.1)) + '@testing-library/jest-dom': + specifier: ^6.9.1 + version: 6.9.1 + '@testing-library/react': + specifier: ^16.3.2 + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@types/dompurify': + specifier: ^3.2.0 + version: 3.2.0 + '@types/node': + specifier: ^24.12.2 + version: 24.12.2 + '@types/react': + specifier: ^19.2.14 + version: 19.2.14 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.14) + '@vitejs/plugin-react': + specifier: ^6.0.1 + version: 6.0.1(vite@8.0.8(@types/node@24.12.2)(jiti@2.6.1)) + '@vitest/coverage-v8': + specifier: ^4.1.5 + version: 4.1.5(vitest@4.1.5) + eslint: + specifier: ^9.39.4 + version: 9.39.4(jiti@2.6.1) + eslint-plugin-react-hooks: + specifier: ^7.0.1 + version: 7.0.1(eslint@9.39.4(jiti@2.6.1)) + eslint-plugin-react-refresh: + specifier: ^0.5.2 + version: 0.5.2(eslint@9.39.4(jiti@2.6.1)) + globals: + specifier: ^17.4.0 + version: 17.4.0 + jsdom: + specifier: ^29.0.2 + version: 29.0.2 + msw: + specifier: ^2.13.6 + version: 2.13.6(@types/node@24.12.2)(typescript@6.0.2) + tailwindcss: + specifier: ^4.2.2 + version: 4.2.2 + typescript: + specifier: ~6.0.2 + version: 6.0.2 + typescript-eslint: + specifier: ^8.58.0 + version: 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2) + vite: + specifier: ^8.0.4 + version: 8.0.8(@types/node@24.12.2)(jiti@2.6.1) + vitest: + specifier: ^4.1.5 + version: 4.1.5(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(jsdom@29.0.2)(msw@2.13.6(@types/node@24.12.2)(typescript@6.0.2))(vite@8.0.8(@types/node@24.12.2)(jiti@2.6.1)) + +packages: + + '@adobe/css-tools@4.4.4': + resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} + + '@ant-design/charts-util@0.0.1-alpha.7': + resolution: {integrity: sha512-Yh0o6EdO6SvdSnStFZMbnUzjyymkVzV+TQ9ymVW9hlVgO/fUkUII3JYSdV+UVcFnYwUF0YiDKuSTLCZNAzg2bQ==} + peerDependencies: + react: '>=16.8.4' + react-dom: '>=16.8.4' + + '@ant-design/charts-util@0.0.3': + resolution: {integrity: sha512-x1H7UT6t4dXAyGRoHqlOnEsEqBSTANFGTZEAMI0CWYhYUpp13n0o9grl9oPtoL6FEQMjUBTY+zGJKlHkz8smMw==} + peerDependencies: + react: '>=16.8.4' + react-dom: '>=16.8.4' + + '@ant-design/charts@2.6.7': + resolution: {integrity: sha512-XfmsnspUpfrMlRFGTwmHJ2TPKcosq5a5nSxAfIOpEXAvmJBT2N16oejGTZhUFTzba8W3XtBOziwRAXmDmLUqvA==} + peerDependencies: + react: '>=16.8.4' + react-dom: '>=16.8.4' + + '@ant-design/colors@8.0.1': + resolution: {integrity: sha512-foPVl0+SWIslGUtD/xBr1p9U4AKzPhNYEseXYRRo5QSzGACYZrQbe11AYJbYfAWnWSpGBx6JjBmSeugUsD9vqQ==} + + '@ant-design/cssinjs-utils@2.1.2': + resolution: {integrity: sha512-5fTHQ158jJJ5dC/ECeyIdZUzKxE/mpEMRZxthyG1sw/AKRHKgJBg00Yi6ACVXgycdje7KahRNvNET/uBccwCnA==} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + '@ant-design/cssinjs@2.1.2': + resolution: {integrity: sha512-2Hy8BnCEH31xPeSLbhhB2ctCPXE2ZnASdi+KbSeS79BNbUhL9hAEe20SkUk+BR8aKTmqb6+FKFruk7w8z0VoRQ==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@ant-design/fast-color@3.0.1': + resolution: {integrity: sha512-esKJegpW4nckh0o6kV3Tkb7NPIZYbPnnFxmQDUmL08ukXZAvV85TZBr70eGuke/CIArLaP6aw8lt9KILjnWuOw==} + engines: {node: '>=8.x'} + + '@ant-design/graphs@2.1.1': + resolution: {integrity: sha512-qT3Oo8BWeoAmZEy9gfR6uIk+rczbNJ3sWXKonoOD5koATWv7dY0kgvS1JnhdM1QW4FkfPPJTeQVSlRRUtvWDwA==} + peerDependencies: + react: '>=16.8.4' + react-dom: '>=16.8.4' + + '@ant-design/icons-svg@4.4.2': + resolution: {integrity: sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==} + + '@ant-design/icons@6.1.1': + resolution: {integrity: sha512-AMT4N2y++TZETNHiM77fs4a0uPVCJGuL5MTonk13Pvv7UN7sID1cNEZOc1qNqx6zLKAOilTEFAdAoAFKa0U//Q==} + engines: {node: '>=8'} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@ant-design/plots@2.6.8': + resolution: {integrity: sha512-QsunUs2d5rbq/1BwVhga/siA5H50OaG23YopMYwPD4sPsza6NQzPQ8FM3elNIsD/BIk298tihqX1cJ/MmvVJbQ==} + peerDependencies: + react: '>=16.8.4' + react-dom: '>=16.8.4' + + '@ant-design/react-slick@2.0.0': + resolution: {integrity: sha512-HMS9sRoEmZey8LsE/Yo6+klhlzU12PisjrVcydW3So7RdklyEd2qehyU6a7Yp+OYN72mgsYs3NFCyP2lCPFVqg==} + peerDependencies: + react: ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^0.14.0 || ^15.0.1 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + '@antv/algorithm@0.1.26': + resolution: {integrity: sha512-DVhcFSQ8YQnMNW34Mk8BSsfc61iC1sAnmcfYoXTAshYHuU50p/6b7x3QYaGctDNKWGvi1ub7mPcSY0bK+aN0qg==} + + '@antv/component@2.1.11': + resolution: {integrity: sha512-dTdz8VAd3rpjOaGEZTluz82mtzrP4XCtNlNQyrxY7VNRNcjtvpTLDn57bUL2lRu1T+iklKvgbE2llMriWkq9vQ==} + + '@antv/coord@0.4.7': + resolution: {integrity: sha512-UTbrMLhwJUkKzqJx5KFnSRpU3BqrdLORJbwUbHK2zHSCT3q3bjcFA//ZYLVfIlwqFDXp/hzfMyRtp0c77A9ZVA==} + + '@antv/event-emitter@0.1.3': + resolution: {integrity: sha512-4ddpsiHN9Pd4UIlWuKVK1C4IiZIdbwQvy9i7DUSI3xNJ89FPUFt8lxDYj8GzzfdllV0NkJTRxnG+FvLk0llidg==} + + '@antv/expr@1.0.2': + resolution: {integrity: sha512-vrfdmPHkTuiS5voVutKl2l06w1ihBh9A8SFdQPEE+2KMVpkymzGOF1eWpfkbGZ7tiFE15GodVdhhHomD/hdIwg==} + + '@antv/g-canvas@2.2.0': + resolution: {integrity: sha512-h7zVBBo2aO64DuGKvq9sG+yTU3sCUb9DALCVm7nz8qGPs8hhLuFOkKPEzUDNfNYZGJUGzY8UDtJ3QRGRFcvEQg==} + + '@antv/g-lite@2.7.0': + resolution: {integrity: sha512-uSzgHYa5bwR5L2Au7/5tsOhFmXKZKLPBH90+Q9bP9teVs5VT4kOAi0isPSpDI8uhdDC2/VrfTWu5K9HhWI6FWw==} + + '@antv/g-math@3.1.0': + resolution: {integrity: sha512-DtN1Gj/yI0UiK18nSBsZX8RK0LszGwqfb+cBYWgE+ddyTm8dZnW4tPUhV7QXePsS6/A5hHC+JFpAAK7OEGo5ZQ==} + + '@antv/g-plugin-dragndrop@2.1.1': + resolution: {integrity: sha512-+aesDUJVQDs6UJ2bOBbDlaGAPCfHmU0MbrMTlQlfpwNplWueqtgVAZ3L57oZ2ZGHRWUHiRwZGPjXMBM3O2LELw==} + + '@antv/g-svg@2.1.1': + resolution: {integrity: sha512-gVzBkjqA8FzDTbkuIxj6L0Omz/X/hFbYLzK6alWr0sHTfywqP6czcjDUJU8DF2MRIY1Twy55uZYW4dqqLXOXXg==} + + '@antv/g2-extension-plot@0.2.2': + resolution: {integrity: sha512-KJXCXO7as+h0hDqirGXf1omrNuYzQmY3VmBmp7lIvkepbQ7sz3pPwy895r1FWETGF3vTk5UeFcAF5yzzBHWgbw==} + + '@antv/g2@5.4.8': + resolution: {integrity: sha512-IvgIpwmT4M5/QAd3Mn2WiHIDeBqFJ4WA2gcZhRRSZuZ2KmgCqZWZwwIT0hc+kIGxwYeDoCQqf//t6FMVu3ryBg==} + + '@antv/g6-extension-react@0.2.7': + resolution: {integrity: sha512-X/zxGiL/kyJ+5xteX1+P2mI07oLw+zfvKcIHxfynL7IGCQCwQ6q91LkJaOlSDTuWhNRXwnwJ4Cf2Nt/9Dhq5Dg==} + peerDependencies: + '@antv/g6': ^5.1.0 + react: '>=16.8' + react-dom: '>=16.8' + + '@antv/g6@5.1.0': + resolution: {integrity: sha512-tvoBDKypL/zWEG99pgwGJLWr2CKA+6zVixYxaVzDOp0+TrPY2cxB1jevxFGPjbTOLBIMYt/vKCh1jnmDtfvtpg==} + + '@antv/g@6.3.1': + resolution: {integrity: sha512-WYEKqy86LHB2PzTmrZXrIsIe+3Epeds2f68zceQ+BJtRoGki7Sy4IhlC8LrUMztgfT1t3d/0L745NWZwITroKA==} + + '@antv/graphin@3.0.5': + resolution: {integrity: sha512-V/j8R8Ty44wUqxVIYLdpPuIO8WWCTIVq1eBJg5YRunL5t5o5qAFpC/qkQxslbBMWyKdIH0oWBnvHA74riGi7cw==} + peerDependencies: + react: ^18.0.0 || ^19.1.0 + react-dom: ^18.0.0 || ^19.1.0 + + '@antv/graphlib@2.0.4': + resolution: {integrity: sha512-zc/5oQlsdk42Z0ib1mGklwzhJ5vczLFiPa1v7DgJkTbgJ2YxRh9xdarf86zI49sKVJmgbweRpJs7Nu5bIiwv4w==} + + '@antv/hierarchy@0.7.1': + resolution: {integrity: sha512-7r22r+HxfcRZp79ZjGmsn97zgC1Iajrv0Mm9DIgx3lPfk+Kme2MG/+EKdZj1iEBsN0rJRzjWVPGL5YrBdVHchw==} + + '@antv/layout@2.0.0': + resolution: {integrity: sha512-aCZ3UdNc40SfT7meFV7QTADY2HCnc0DShVw56CJNTI6oExUIVU736grPuL5Dhb8/JrVaU4Y83QPN/P7KafBzlw==} + + '@antv/scale@0.4.16': + resolution: {integrity: sha512-5wg/zB5kXHxpTV5OYwJD3ja6R8yTiqIOkjOhmpEJiowkzRlbEC/BOyMvNUq5fqFIHnMCE9woO7+c3zxEQCKPjw==} + + '@antv/scale@0.5.2': + resolution: {integrity: sha512-rTHRAwvpHWC5PGZF/mJ2ZuTDqwwvVBDRph0Uu5PV9BXwzV7K8+9lsqGJ+XHVLxe8c6bKog5nlzvV/dcYb0d5Ow==} + + '@antv/util@2.0.17': + resolution: {integrity: sha512-o6I9hi5CIUvLGDhth0RxNSFDRwXeywmt6ExR4+RmVAzIi48ps6HUy+svxOCayvrPBN37uE6TAc2KDofRo0nK9Q==} + + '@antv/util@3.3.11': + resolution: {integrity: sha512-FII08DFM4ABh2q5rPYdr0hMtKXRgeZazvXaFYCs7J7uTcWDHUhczab2qOCJLNDugoj8jFag1djb7wS9ehaRYBg==} + + '@antv/vendor@1.0.11': + resolution: {integrity: sha512-LmhPEQ+aapk3barntaiIxJ5VHno/Tyab2JnfdcPzp5xONh/8VSfed4bo/9xKo5HcUAEydko38vYLfj6lJliLiw==} + + '@asamuzakjp/css-color@5.1.11': + resolution: {integrity: sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/dom-selector@7.1.1': + resolution: {integrity: sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/generational-cache@1.0.1': + resolution: {integrity: sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + '@asamuzakjp/nwsapi@2.3.9': + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.0': + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.2': + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + + '@bramus/specificity@2.4.2': + resolution: {integrity: sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==} + hasBin: true + + '@csstools/color-helpers@6.0.2': + resolution: {integrity: sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==} + engines: {node: '>=20.19.0'} + + '@csstools/css-calc@3.2.0': + resolution: {integrity: sha512-bR9e6o2BDB12jzN/gIbjHa5wLJ4UjD1CB9pM7ehlc0ddk6EBz+yYS1EV2MF55/HUxrHcB/hehAyt5vhsA3hx7w==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-color-parser@4.1.0': + resolution: {integrity: sha512-U0KhLYmy2GVj6q4T3WaAe6NPuFYCPQoE3b0dRGxejWDgcPp8TP7S5rVdM5ZrFaqu4N67X8YaPBw14dQSYx3IyQ==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-parser-algorithms': ^4.0.0 + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-parser-algorithms@4.0.0': + resolution: {integrity: sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==} + engines: {node: '>=20.19.0'} + peerDependencies: + '@csstools/css-tokenizer': ^4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.1.3': + resolution: {integrity: sha512-SH60bMfrRCJF3morcdk57WklujF4Jr/EsQUzqkarfHXEFcAR1gg7fS/chAE922Sehgzc1/+Tz5H3Ypa1HiEKrg==} + peerDependencies: + css-tree: ^3.2.1 + peerDependenciesMeta: + css-tree: + optional: true + + '@csstools/css-tokenizer@4.0.0': + resolution: {integrity: sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==} + engines: {node: '>=20.19.0'} + + '@dnd-kit/accessibility@3.1.1': + resolution: {integrity: sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw==} + peerDependencies: + react: '>=16.8.0' + + '@dnd-kit/core@6.3.1': + resolution: {integrity: sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@dnd-kit/sortable@10.0.0': + resolution: {integrity: sha512-+xqhmIIzvAYMGfBYYnbKuNicfSsk4RksY2XdmJhT+HAC01nix6fHCztU68jooFiMUB01Ky3F0FyOvhG/BZrWkg==} + peerDependencies: + '@dnd-kit/core': ^6.3.0 + react: '>=16.8.0' + + '@dnd-kit/utilities@3.2.2': + resolution: {integrity: sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg==} + peerDependencies: + react: '>=16.8.0' + + '@emnapi/core@1.9.2': + resolution: {integrity: sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==} + + '@emnapi/runtime@1.9.2': + resolution: {integrity: sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + + '@emotion/hash@0.8.0': + resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==} + + '@emotion/is-prop-valid@1.4.0': + resolution: {integrity: sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/unitless@0.7.5': + resolution: {integrity: sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==} + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.5': + resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.4': + resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@exodus/bytes@1.15.0': + resolution: {integrity: sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + '@noble/hashes': ^1.8.0 || ^2.0.0 + peerDependenciesMeta: + '@noble/hashes': + optional: true + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@inquirer/ansi@2.0.5': + resolution: {integrity: sha512-doc2sWgJpbFQ64UflSVd17ibMGDuxO1yKgOgLMwavzESnXjFWJqUeG8saYosqKpHp4kWiM5x1nXvEjbpx90gzw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + + '@inquirer/confirm@6.0.12': + resolution: {integrity: sha512-h9FgGun3QwVYNj5TWIZZ+slii73bMoBFjPfVIGtnFuL4t8gBiNDV9PcSfIzkuxvgquJKt9nr1QzszpBzTbH8Og==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/core@11.1.9': + resolution: {integrity: sha512-BDE4fG22uYh1bGSifcj7JSx119TVYNViMhMu85usp4Fswrzh6M0DV3yld64jA98uOAa2GSQ4Bg4bZRm2d2cwSg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/figures@2.0.5': + resolution: {integrity: sha512-NsSs4kzfm12lNetHwAn3GEuH317IzpwrMCbOuMIVytpjnJ90YYHNwdRgYGuKmVxwuIqSgqk3M5qqQt1cDk0tGQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + + '@inquirer/type@4.0.5': + resolution: {integrity: sha512-aetVUNeKNc/VriqXlw1NRSW0zhMBB0W4bNbWRJgzRl/3d0QNDQFfk0GO5SDdtjMZVg6o8ZKEiadd7SCCzoOn5Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@mswjs/interceptors@0.41.6': + resolution: {integrity: sha512-qmDvJIjcNsZ6tXWy2G9yuCgMPTTn35GMA3dPpSLm7QJVpbQzYdw0ALy1bKoivXnEM3U93/OrK+/M719b+fg84Q==} + engines: {node: '>=18'} + + '@napi-rs/wasm-runtime@1.1.3': + resolution: {integrity: sha512-xK9sGVbJWYb08+mTJt3/YV24WxvxpXcXtP6B172paPZ+Ts69Re9dAr7lKwJoeIx8OoeuimEiRZ7umkiUVClmmQ==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 + + '@open-draft/deferred-promise@2.2.0': + resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} + + '@open-draft/deferred-promise@3.0.0': + resolution: {integrity: sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA==} + + '@open-draft/logger@0.3.0': + resolution: {integrity: sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==} + + '@open-draft/until@2.1.0': + resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} + + '@oxc-project/types@0.124.0': + resolution: {integrity: sha512-VBFWMTBvHxS11Z5Lvlr3IWgrwhMTXV+Md+EQF0Xf60+wAdsGFTBx7X7K/hP4pi8N7dcm1RvcHwDxZ16Qx8keUg==} + + '@playwright/test@1.59.1': + resolution: {integrity: sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==} + engines: {node: '>=18'} + hasBin: true + + '@rc-component/async-validator@5.1.0': + resolution: {integrity: sha512-n4HcR5siNUXRX23nDizbZBQPO0ZM/5oTtmKZ6/eqL0L2bo747cklFdZGRN2f+c9qWGICwDzrhW0H7tE9PptdcA==} + engines: {node: '>=14.x'} + + '@rc-component/cascader@1.14.0': + resolution: {integrity: sha512-Ip9356xwZUR2nbW5PRVGif4B/bDve4pLa/N+PGbvBaTnjbvmN4PFMBGQSmlDlzKP1ovxaYMvwF/dI9lXNLT4iQ==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/checkbox@2.0.0': + resolution: {integrity: sha512-3CXGPpAR9gsPKeO2N78HAPOzU30UdemD6HGJoWVJOpa6WleaGB5kzZj3v6bdTZab31YuWgY/RxV3VKPctn0DwQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/collapse@1.2.0': + resolution: {integrity: sha512-ZRYSKSS39qsFx93p26bde7JUZJshsUBEQRlRXPuJYlAiNX0vyYlF5TsAm8JZN3LcF8XvKikdzPbgAtXSbkLUkw==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/color-picker@3.1.1': + resolution: {integrity: sha512-OHaCHLHszCegdXmIq2ZRIZBN/EtpT6Wm8SG/gpzLATHbVKc/avvuKi+zlOuk05FTWvgaMmpxAko44uRJ3M+2pg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/context@2.0.1': + resolution: {integrity: sha512-HyZbYm47s/YqtP6pKXNMjPEMaukyg7P0qVfgMLzr7YiFNMHbK2fKTAGzms9ykfGHSfyf75nBbgWw+hHkp+VImw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/dialog@1.8.4': + resolution: {integrity: sha512-Ay6PM7phkTkquplG8fWfUGFZ2GTLx9diTl4f0d8Eqxd7W1u1KjE9AQooFQHOHnhZf0Ya3z51+5EKCWHmt/dNEw==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/drawer@1.4.2': + resolution: {integrity: sha512-1ib+fZEp6FBu+YvcIktm+nCQ+Q+qIpwpoaJH6opGr4ofh2QMq+qdr5DLC4oCf5qf3pcWX9lUWPYX652k4ini8Q==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/dropdown@1.0.2': + resolution: {integrity: sha512-6PY2ecUSYhDPhkNHHb4wfeAya04WhpmUSKzdR60G+kMNVUCX2vjT/AgTS0Lz0I/K6xrPMJ3enQbwVpeN3sHCgg==} + peerDependencies: + react: '>=16.11.0' + react-dom: '>=16.11.0' + + '@rc-component/form@1.8.0': + resolution: {integrity: sha512-eUD5KKYnIZWmJwRA0vnyO/ovYUfHGU1svydY1OrqU5fw8Oz9Tdqvxvrlh0wl6xI/EW69dT7II49xpgOWzK3T5A==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/image@1.8.1': + resolution: {integrity: sha512-JfPCijmMl+EaMvbftsEs/4VHmTyJKsZBh5ujFowSA45i9NTVYS1vuHtgpVV/QrGa27kXwbVOZriffCe/PNKuMw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/input-number@1.6.2': + resolution: {integrity: sha512-Gjcq7meZlCOiWN1t1xCC+7/s85humHVokTBI7PJgTfoyw5OWF74y3e6P8PHX104g9+b54jsodFIzyaj6p8LI9w==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/input@1.1.2': + resolution: {integrity: sha512-Q61IMR47piUBudgixJ30CciKIy9b1H95qe7GgEKOmSJVJXvFRWJllJfQry9tif+MX2cWFXWJf/RXz4kaCeq/Fg==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@rc-component/mentions@1.6.0': + resolution: {integrity: sha512-KIkQNP6habNuTsLhUv0UGEOwG67tlmE7KNIJoQZZNggEZl5lQJTytFDb69sl5CK3TDdISCTjKP3nGEBKgT61CQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/menu@1.2.0': + resolution: {integrity: sha512-VWwDuhvYHSnTGj4n6bV3ISrLACcPAzdPOq3d0BzkeiM5cve8BEYfvkEhNoM0PLzv51jpcejeyrLXeMVIJ+QJlg==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/mini-decimal@1.1.3': + resolution: {integrity: sha512-bk/FJ09fLf+NLODMAFll6CfYrHPBioTedhW6lxDBuuWucJEqFUd4l/D/5JgIi3dina6sYahB8iuPAZTNz2pMxw==} + engines: {node: '>=8.x'} + + '@rc-component/motion@1.3.2': + resolution: {integrity: sha512-itfd+GztzJYAb04Z4RkEub1TbJAfZc2Iuy8p44U44xD1F5+fNYFKI3897ijlbIyfvXkTmMm+KGcjkQQGMHywEQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/mutate-observer@2.0.1': + resolution: {integrity: sha512-AyarjoLU5YlxuValRi+w8JRH2Z84TBbFO2RoGWz9d8bSu0FqT8DtugH3xC3BV7mUwlmROFauyWuXFuq4IFbH+w==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/notification@1.2.0': + resolution: {integrity: sha512-OX3J+zVU7rvoJCikjrfW7qOUp7zlDeFBK2eA3SFbGSkDqo63Sl4Ss8A04kFP+fxHSxMDIS9jYVEZtU1FNCFuBA==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/overflow@1.0.0': + resolution: {integrity: sha512-GSlBeoE0XTBi5cf3zl8Qh7Uqhn7v8RrlJ8ajeVpEkNe94HWy5l5BQ0Mwn2TVUq9gdgbfEMUmTX7tJFAg7mz0Rw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/pagination@1.2.0': + resolution: {integrity: sha512-YcpUFE8dMLfSo6OARJlK6DbHHvrxz7pMGPGmC/caZSJJz6HRKHC1RPP001PRHCvG9Z/veD039uOQmazVuLJzlw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/picker@1.9.1': + resolution: {integrity: sha512-9FBYYsvH3HMLICaPDA/1Th5FLaDkFa7qAtangIdlhKb3ZALaR745e9PsOhheJb6asS4QXc12ffiAcjdkZ4C5/g==} + engines: {node: '>=12.x'} + peerDependencies: + date-fns: '>= 2.x' + dayjs: '>= 1.x' + luxon: '>= 3.x' + moment: '>= 2.x' + react: '>=16.9.0' + react-dom: '>=16.9.0' + peerDependenciesMeta: + date-fns: + optional: true + dayjs: + optional: true + luxon: + optional: true + moment: + optional: true + + '@rc-component/portal@2.2.0': + resolution: {integrity: sha512-oc6FlA+uXCMiwArHsJyHcIkX4q6uKyndrPol2eWX8YPkAnztHOPsFIRtmWG4BMlGE5h7YIRE3NiaJ5VS8Lb1QQ==} + engines: {node: '>=12.x'} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/progress@1.0.2': + resolution: {integrity: sha512-WZUnH9eGxH1+xodZKqdrHke59uyGZSWgj5HBM5Kwk5BrTMuAORO7VJ2IP5Qbm9aH3n9x3IcesqHHR0NWPBC7fQ==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/qrcode@1.1.1': + resolution: {integrity: sha512-LfLGNymzKdUPjXUbRP+xOhIWY4jQ+YMj5MmWAcgcAq1Ij8XP7tRmAXqyuv96XvLUBE/5cA8hLFl9eO1JQMujrA==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/rate@1.0.1': + resolution: {integrity: sha512-bkXxeBqDpl5IOC7yL7GcSYjQx9G8H+6kLYQnNZWeBYq2OYIv1MONd6mqKTjnnJYpV0cQIU2z3atdW0j1kttpTw==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/resize-observer@1.1.2': + resolution: {integrity: sha512-t/Bb0W8uvL4PYKAB3YcChC+DlHh0Wt5kM7q/J+0qpVEUMLe7Hk5zuvc9km0hMnTFPSx5Z7Wu/fzCLN6erVLE8Q==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/segmented@1.3.0': + resolution: {integrity: sha512-5J/bJ01mbDnoA6P/FW8SxUvKn+OgUSTZJPzCNnTBntG50tzoP7DydGhqxp7ggZXZls7me3mc2EQDXakU3iTVFg==} + peerDependencies: + react: '>=16.0.0' + react-dom: '>=16.0.0' + + '@rc-component/select@1.6.15': + resolution: {integrity: sha512-SyVCWnqxCQZZcQvQJ/CxSjx2bGma6ds/HtnpkIfZVnt6RoEgbqUmHgD6vrzNarNXwbLXerwVzWwq8F3d1sst7g==} + engines: {node: '>=8.x'} + peerDependencies: + react: '*' + react-dom: '*' + + '@rc-component/slider@1.0.1': + resolution: {integrity: sha512-uDhEPU1z3WDfCJhaL9jfd2ha/Eqpdfxsn0Zb0Xcq1NGQAman0TWaR37OWp2vVXEOdV2y0njSILTMpTfPV1454g==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/steps@1.2.2': + resolution: {integrity: sha512-/yVIZ00gDYYPHSY0JP+M+s3ZvuXLu2f9rEjQqiUDs7EcYsUYrpJ/1bLj9aI9R7MBR3fu/NGh6RM9u2qGfqp+Nw==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/switch@1.0.3': + resolution: {integrity: sha512-Jgi+EbOBquje/XNdofr7xbJQZPYJP+BlPfR0h+WN4zFkdtB2EWqEfvkXJWeipflwjWip0/17rNbxEAqs8hVHfw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/table@1.9.1': + resolution: {integrity: sha512-FVI5ZS/GdB3BcgexfCYKi3iHhZS3Fr59EtsxORszYGrfpH1eWr33eDNSYkVfLI6tfJ7vftJDd9D5apfFWqkdJg==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/tabs@1.7.0': + resolution: {integrity: sha512-J48cs2iBi7Ho3nptBxxIqizEliUC+ExE23faspUQKGQ550vaBlv3aGF8Epv/UB1vFWeoJDTW/dNzgIU0Qj5i/w==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/textarea@1.1.2': + resolution: {integrity: sha512-9rMUEODWZDMovfScIEHXWlVZuPljZ2pd1LKNjslJVitn4SldEzq5vO1CL3yy3Dnib6zZal2r2DPtjy84VVpF6A==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/tooltip@1.4.0': + resolution: {integrity: sha512-8Rx5DCctIlLI4raR0I0xHjVTf1aF48+gKCNeAAo5bmF5VoR5YED+A/XEqzXv9KKqrJDRcd3Wndpxh2hyzrTtSg==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/tour@2.3.0': + resolution: {integrity: sha512-K04K9r32kUC+auBSQfr+Fss4SpSIS9JGe56oq/ALAX0p+i2ylYOI1MgR83yBY7v96eO6ZFXcM/igCQmubps0Ow==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/tree-select@1.8.0': + resolution: {integrity: sha512-iYsPq3nuLYvGqdvFAW+l+I9ASRIOVbMXyA8FGZg2lGym/GwkaWeJGzI4eJ7c9IOEhRj0oyfIN4S92Fl3J05mjQ==} + peerDependencies: + react: '*' + react-dom: '*' + + '@rc-component/tree@1.2.4': + resolution: {integrity: sha512-5Gli43+m4R7NhpYYz3Z61I6LOw9yI6CNChxgVtvrO6xB1qML7iE6QMLVMB3+FTjo2yF6uFdAHtqWPECz/zbX5w==} + engines: {node: '>=10.x'} + peerDependencies: + react: '*' + react-dom: '*' + + '@rc-component/trigger@3.9.0': + resolution: {integrity: sha512-X8btpwfrT27AgrZVOz4swclhEHTZcqaHeQMXXBgveagOiakTa36uObXbdwerXffgV8G9dH1fAAE0DHtVQs8EHg==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/upload@1.1.0': + resolution: {integrity: sha512-LIBV90mAnUE6VK5N4QvForoxZc4XqEYZimcp7fk+lkE4XwHHyJWxpIXQQwMU8hJM+YwBbsoZkGksL1sISWHQxw==} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rc-component/util@1.10.1': + resolution: {integrity: sha512-q++9S6rUa5Idb/xIBNz6jtvumw5+O5YV5V0g4iK9mn9jWs4oGJheE3ZN1kAnE723AXyaD8v95yeOASmdk8Jnng==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + '@rc-component/virtual-list@1.0.2': + resolution: {integrity: sha512-uvTol/mH74FYsn5loDGJxo+7kjkO4i+y4j87Re1pxJBs0FaeuMuLRzQRGaXwnMcV1CxpZLi2Z56Rerj2M00fjQ==} + engines: {node: '>=8.x'} + peerDependencies: + react: '>=16.9.0' + react-dom: '>=16.9.0' + + '@rolldown/binding-android-arm64@1.0.0-rc.15': + resolution: {integrity: sha512-YYe6aWruPZDtHNpwu7+qAHEMbQ/yRl6atqb/AhznLTnD3UY99Q1jE7ihLSahNWkF4EqRPVC4SiR4O0UkLK02tA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.0.0-rc.15': + resolution: {integrity: sha512-oArR/ig8wNTPYsXL+Mzhs0oxhxfuHRfG7Ikw7jXsw8mYOtk71W0OkF2VEVh699pdmzjPQsTjlD1JIOoHkLP1Fg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.0.0-rc.15': + resolution: {integrity: sha512-YzeVqOqjPYvUbJSWJ4EDL8ahbmsIXQpgL3JVipmN+MX0XnXMeWomLN3Fb+nwCmP/jfyqte5I3XRSm7OfQrbyxw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.0.0-rc.15': + resolution: {integrity: sha512-9Erhx956jeQ0nNTyif1+QWAXDRD38ZNjr//bSHrt6wDwB+QkAfl2q6Mn1k6OBPerznjRmbM10lgRb1Pli4xZPw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.15': + resolution: {integrity: sha512-cVwk0w8QbZJGTnP/AHQBs5yNwmpgGYStL88t4UIaqcvYJWBfS0s3oqVLZPwsPU6M0zlW4GqjP0Zq5MnAGwFeGA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.15': + resolution: {integrity: sha512-eBZ/u8iAK9SoHGanqe/jrPnY0JvBN6iXbVOsbO38mbz+ZJsaobExAm1Iu+rxa4S1l2FjG0qEZn4Rc6X8n+9M+w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.15': + resolution: {integrity: sha512-ZvRYMGrAklV9PEkgt4LQM6MjQX2P58HPAuecwYObY2DhS2t35R0I810bKi0wmaYORt6m/2Sm+Z+nFgb0WhXNcQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.15': + resolution: {integrity: sha512-VDpgGBzgfg5hLg+uBpCLoFG5kVvEyafmfxGUV0UHLcL5irxAK7PKNeC2MwClgk6ZAiNhmo9FLhRYgvMmedLtnQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.15': + resolution: {integrity: sha512-y1uXY3qQWCzcPgRJATPSOUP4tCemh4uBdY7e3EZbVwCJTY3gLJWnQABgeUetvED+bt1FQ01OeZwvhLS2bpNrAQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.15': + resolution: {integrity: sha512-023bTPBod7J3Y/4fzAN6QtpkSABR0rigtrwaP+qSEabUh5zf6ELr9Nc7GujaROuPY3uwdSIXWrvhn1KxOvurWA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rolldown/binding-linux-x64-musl@1.0.0-rc.15': + resolution: {integrity: sha512-witB2O0/hU4CgfOOKUoeFgQ4GktPi1eEbAhaLAIpgD6+ZnhcPkUtPsoKKHRzmOoWPZue46IThdSgdo4XneOLYw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rolldown/binding-openharmony-arm64@1.0.0-rc.15': + resolution: {integrity: sha512-UCL68NJ0Ud5zRipXZE9dF5PmirzJE4E4BCIOOssEnM7wLDsxjc6Qb0sGDxTNRTP53I6MZpygyCpY8Aa8sPfKPg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-wasm32-wasi@1.0.0-rc.15': + resolution: {integrity: sha512-ApLruZq/ig+nhaE7OJm4lDjayUnOHVUa77zGeqnqZ9pn0ovdVbbNPerVibLXDmWeUZXjIYIT8V3xkT58Rm9u5Q==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.15': + resolution: {integrity: sha512-KmoUoU7HnN+Si5YWJigfTws1jz1bKBYDQKdbLspz0UaqjjFkddHsqorgiW1mxcAj88lYUE6NC/zJNwT+SloqtA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.15': + resolution: {integrity: sha512-3P2A8L+x75qavWLe/Dll3EYBJLQmtkJN8rfh+U/eR3MqMgL/h98PhYI+JFfXuDPgPeCB7iZAKiqii5vqOvnA0g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.0-rc.15': + resolution: {integrity: sha512-UromN0peaE53IaBRe9W7CjrZgXl90fqGpK+mIZbA3qSTeYqg3pqpROBdIPvOG3F5ereDHNwoHBI2e50n1BDr1g==} + + '@rolldown/pluginutils@1.0.0-rc.7': + resolution: {integrity: sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==} + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@tailwindcss/node@4.2.2': + resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==} + + '@tailwindcss/oxide-android-arm64@4.2.2': + resolution: {integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.2.2': + resolution: {integrity: sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.2.2': + resolution: {integrity: sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.2.2': + resolution: {integrity: sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': + resolution: {integrity: sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': + resolution: {integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.2.2': + resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.2.2': + resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.2.2': + resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.2.2': + resolution: {integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': + resolution: {integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.2.2': + resolution: {integrity: sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.2.2': + resolution: {integrity: sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==} + engines: {node: '>= 20'} + + '@tailwindcss/vite@4.2.2': + resolution: {integrity: sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 || ^8 + + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} + + '@testing-library/jest-dom@6.9.1': + resolution: {integrity: sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==} + engines: {node: '>=14', npm: '>=6', yarn: '>=1'} + + '@testing-library/react@16.3.2': + resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@transloadit/prettier-bytes@0.0.7': + resolution: {integrity: sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==} + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-dispatch@3.0.7': + resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==} + + '@types/d3-drag@3.0.7': + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + + '@types/d3-dsv@3.0.7': + resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-fetch@3.0.7': + resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} + + '@types/d3-force@3.0.10': + resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} + + '@types/d3-format@3.0.4': + resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} + + '@types/d3-geo@3.1.0': + resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} + + '@types/d3-hierarchy@3.1.7': + resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-quadtree@3.0.6': + resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} + + '@types/d3-random@3.0.3': + resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==} + + '@types/d3-scale-chromatic@3.1.0': + resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-selection@3.0.11': + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + + '@types/d3-shape@3.1.8': + resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + + '@types/d3-transition@3.0.9': + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} + + '@types/d3-zoom@3.0.8': + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/dompurify@3.2.0': + resolution: {integrity: sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==} + deprecated: This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed. + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/event-emitter@0.3.5': + resolution: {integrity: sha512-zx2/Gg0Eg7gwEiOIIh5w9TrhKKTeQh7CPCOPNc0el4pLSwzebA8SmnHwZs2dWlLONvyulykSwGSQxQHLhjGLvQ==} + + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@24.12.2': + resolution: {integrity: sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.14': + resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + + '@types/set-cookie-parser@2.4.10': + resolution: {integrity: sha512-GGmQVGpQWUe5qglJozEjZV/5dyxbOOZ0LHe/lqyWssB88Y4svNfst0uqBVscdDeIKl5Jy5+aPSvy7mI9tYRguw==} + + '@types/statuses@2.0.6': + resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + + '@typescript-eslint/eslint-plugin@8.58.1': + resolution: {integrity: sha512-eSkwoemjo76bdXl2MYqtxg51HNwUSkWfODUOQ3PaTLZGh9uIWWFZIjyjaJnex7wXDu+TRx+ATsnSxdN9YWfRTQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.58.1 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.58.1': + resolution: {integrity: sha512-gGkiNMPqerb2cJSVcruigx9eHBlLG14fSdPdqMoOcBfh+vvn4iCq2C8MzUB89PrxOXk0y3GZ1yIWb9aOzL93bw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.58.1': + resolution: {integrity: sha512-gfQ8fk6cxhtptek+/8ZIqw8YrRW5048Gug8Ts5IYcMLCw18iUgrZAEY/D7s4hkI0FxEfGakKuPK/XUMPzPxi5g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.58.1': + resolution: {integrity: sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.58.1': + resolution: {integrity: sha512-JAr2hOIct2Q+qk3G+8YFfqkqi7sC86uNryT+2i5HzMa2MPjw4qNFvtjnw1IiA1rP7QhNKVe21mSSLaSjwA1Olw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.58.1': + resolution: {integrity: sha512-HUFxvTJVroT+0rXVJC7eD5zol6ID+Sn5npVPWoFuHGg9Ncq5Q4EYstqR+UOqaNRFXi5TYkpXXkLhoCHe3G0+7w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.58.1': + resolution: {integrity: sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.58.1': + resolution: {integrity: sha512-w4w7WR7GHOjqqPnvAYbazq+Y5oS68b9CzasGtnd6jIeOIeKUzYzupGTB2T4LTPSv4d+WPeccbxuneTFHYgAAWg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.58.1': + resolution: {integrity: sha512-Ln8R0tmWC7pTtLOzgJzYTXSCjJ9rDNHAqTaVONF4FEi2qwce8mD9iSOxOpLFFvWp/wBFlew0mjM1L1ihYWfBdQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.58.1': + resolution: {integrity: sha512-y+vH7QE8ycjoa0bWciFg7OpFcipUuem1ujhrdLtq1gByKwfbC7bPeKsiny9e0urg93DqwGcHey+bGRKCnF1nZQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@uppy/companion-client@2.2.2': + resolution: {integrity: sha512-5mTp2iq97/mYSisMaBtFRry6PTgZA6SIL7LePteOV5x0/DxKfrZW3DEiQERJmYpHzy7k8johpm2gHnEKto56Og==} + + '@uppy/core@2.3.4': + resolution: {integrity: sha512-iWAqppC8FD8mMVqewavCz+TNaet6HPXitmGXpGGREGrakZ4FeuWytVdrelydzTdXx6vVKkOmI2FLztGg73sENQ==} + + '@uppy/store-default@2.1.1': + resolution: {integrity: sha512-xnpTxvot2SeAwGwbvmJ899ASk5tYXhmZzD/aCFsXePh/v8rNvR2pKlcQUH7cF/y4baUGq3FHO/daKCok/mpKqQ==} + + '@uppy/utils@4.1.3': + resolution: {integrity: sha512-nTuMvwWYobnJcytDO3t+D6IkVq/Qs4Xv3vyoEZ+Iaf8gegZP+rEyoaFT2CK5XLRMienPyqRqNbIfRuFaOWSIFw==} + + '@uppy/xhr-upload@2.1.3': + resolution: {integrity: sha512-YWOQ6myBVPs+mhNjfdWsQyMRWUlrDLMoaG7nvf/G6Y3GKZf8AyjFDjvvJ49XWQ+DaZOftGkHmF1uh/DBeGivJQ==} + peerDependencies: + '@uppy/core': ^2.3.3 + + '@vitejs/plugin-react@6.0.1': + resolution: {integrity: sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + '@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 + + '@vitest/coverage-v8@4.1.5': + resolution: {integrity: sha512-38C0/Ddb7HcRG0Z4/DUem8x57d2p9jYgp18mkaYswEOQBGsI1CG4f/hjm0ZCeaJfWhSZ4k7jgs29V1Zom7Ki9A==} + peerDependencies: + '@vitest/browser': 4.1.5 + vitest: 4.1.5 + peerDependenciesMeta: + '@vitest/browser': + optional: true + + '@vitest/expect@4.1.5': + resolution: {integrity: sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==} + + '@vitest/mocker@4.1.5': + resolution: {integrity: sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.5': + resolution: {integrity: sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==} + + '@vitest/runner@4.1.5': + resolution: {integrity: sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==} + + '@vitest/snapshot@4.1.5': + resolution: {integrity: sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==} + + '@vitest/spy@4.1.5': + resolution: {integrity: sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==} + + '@vitest/utils@4.1.5': + resolution: {integrity: sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==} + + '@wangeditor/basic-modules@1.1.7': + resolution: {integrity: sha512-cY9CPkLJaqF05STqfpZKWG4LpxTMeGSIIF1fHvfm/mz+JXatCagjdkbxdikOuKYlxDdeqvOeBmsUBItufDLXZg==} + peerDependencies: + '@wangeditor/core': 1.x + dom7: ^3.0.0 + lodash.throttle: ^4.1.1 + nanoid: ^3.2.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/code-highlight@1.0.3': + resolution: {integrity: sha512-iazHwO14XpCuIWJNTQTikqUhGKyqj+dUNWJ9288Oym9M2xMVHvnsOmDU2sgUDWVy+pOLojReMPgXCsvvNlOOhw==} + peerDependencies: + '@wangeditor/core': 1.x + dom7: ^3.0.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/core@1.1.19': + resolution: {integrity: sha512-KevkB47+7GhVszyYF2pKGKtCSj/YzmClsD03C3zTt+9SR2XWT5T0e3yQqg8baZpcMvkjs1D8Dv4fk8ok/UaS2Q==} + peerDependencies: + '@uppy/core': ^2.1.1 + '@uppy/xhr-upload': ^2.0.3 + dom7: ^3.0.0 + is-hotkey: ^0.2.0 + lodash.camelcase: ^4.3.0 + lodash.clonedeep: ^4.5.0 + lodash.debounce: ^4.0.8 + lodash.foreach: ^4.5.0 + lodash.isequal: ^4.5.0 + lodash.throttle: ^4.1.1 + lodash.toarray: ^4.4.0 + nanoid: ^3.2.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/editor-for-react@1.0.6': + resolution: {integrity: sha512-KJNSfgMr5Blzae3oyaiz20flMKHZHnvsz4bCYQKDCUs/qkvC+xNTnwedlCmhGP187oPWPEypCIYI8Zg6sz0psQ==} + peerDependencies: + '@wangeditor/core': '>=1.1.0' + '@wangeditor/editor': '>=5.1.0' + react: '>=17.0.2' + react-dom: '>=17.0.2' + + '@wangeditor/editor@5.1.23': + resolution: {integrity: sha512-0RxfeVTuK1tktUaPROnCoFfaHVJpRAIE2zdS0mpP+vq1axVQpLjM8+fCvKzqYIkH0Pg+C+44hJpe3VVroSkEuQ==} + + '@wangeditor/list-module@1.0.5': + resolution: {integrity: sha512-uDuYTP6DVhcYf7mF1pTlmNn5jOb4QtcVhYwSSAkyg09zqxI1qBqsfUnveeDeDqIuptSJhkh81cyxi+MF8sEPOQ==} + peerDependencies: + '@wangeditor/core': 1.x + dom7: ^3.0.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/table-module@1.1.4': + resolution: {integrity: sha512-5saanU9xuEocxaemGdNi9t8MCDSucnykEC6jtuiT72kt+/Hhh4nERYx1J20OPsTCCdVr7hIyQenFD1iSRkIQ6w==} + peerDependencies: + '@wangeditor/core': 1.x + dom7: ^3.0.0 + lodash.isequal: ^4.5.0 + lodash.throttle: ^4.1.1 + nanoid: ^3.2.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/upload-image-module@1.0.2': + resolution: {integrity: sha512-z81lk/v71OwPDYeQDxj6cVr81aDP90aFuywb8nPD6eQeECtOymrqRODjpO6VGvCVxVck8nUxBHtbxKtjgcwyiA==} + peerDependencies: + '@uppy/core': ^2.0.3 + '@uppy/xhr-upload': ^2.0.3 + '@wangeditor/basic-modules': 1.x + '@wangeditor/core': 1.x + dom7: ^3.0.0 + lodash.foreach: ^4.5.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/video-module@1.1.4': + resolution: {integrity: sha512-ZdodDPqKQrgx3IwWu4ZiQmXI8EXZ3hm2/fM6E3t5dB8tCaIGWQZhmqd6P5knfkRAd3z2+YRSRbxOGfoRSp/rLg==} + peerDependencies: + '@uppy/core': ^2.1.4 + '@uppy/xhr-upload': ^2.0.7 + '@wangeditor/core': 1.x + dom7: ^3.0.0 + nanoid: ^3.2.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@xyflow/react@12.10.2': + resolution: {integrity: sha512-CgIi6HwlcHXwlkTpr0fxLv/0sRVNZ8IdwKLzzeCscaYBwpvfcH1QFOCeaTCuEn1FQEs/B8CjnTSjhs8udgmBgQ==} + peerDependencies: + react: '>=17' + react-dom: '>=17' + + '@xyflow/system@0.0.76': + resolution: {integrity: sha512-hvwvnRS1B3REwVDlWexsq7YQaPZeG3/mKo1jv38UmnpWmxihp14bW6VtEOuHEwJX2FvzFw8k77LyKSk/wiZVNA==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.14.0: + resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + antd@6.3.5: + resolution: {integrity: sha512-8BPz9lpZWQm42PTx7yL4KxWAotVuqINiKcoYRcLtdd5BFmAcAZicVyFTnBJyRDlzGZFZeRW3foGu6jXYFnej6Q==} + peerDependencies: + react: '>=18.0.0' + react-dom: '>=18.0.0' + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + ast-v8-to-istanbul@1.0.0: + resolution: {integrity: sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.15.0: + resolution: {integrity: sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + base64-arraybuffer@1.0.2: + resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==} + engines: {node: '>= 0.6.0'} + + baseline-browser-mapping@2.10.17: + resolution: {integrity: sha512-HdrkN8eVG2CXxeifv/VdJ4A4RSra1DTW8dc/hdxzhGHN8QePs6gKaWM9pHPcpCoxYZJuOZ8drHmbdpLHjCYjLA==} + engines: {node: '>=6.0.0'} + hasBin: true + + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + + brace-expansion@1.1.13: + resolution: {integrity: sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==} + + brace-expansion@5.0.5: + resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} + engines: {node: 18 || 20 || >=22} + + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bubblesets-js@2.3.4: + resolution: {integrity: sha512-DyMjHmpkS2+xcFNtyN00apJYL3ESdp9fTrkDr5+9Qg/GPqFmcWgGsK1akZnttE1XFxJ/VMy4DNNGMGYtmFp1Sg==} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001787: + resolution: {integrity: sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + classcat@5.0.5: + resolution: {integrity: sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + comlink@4.4.2: + resolution: {integrity: sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + compute-scroll-into-view@1.0.20: + resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==} + + compute-scroll-into-view@3.1.1: + resolution: {integrity: sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie@1.1.1: + resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} + engines: {node: '>=18'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + css-line-break@2.1.0: + resolution: {integrity: sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==} + + css-tree@3.2.1: + resolution: {integrity: sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css.escape@1.5.1: + resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-binarytree@1.0.2: + resolution: {integrity: sha512-cElUNH+sHu95L04m92pG73t2MEJXKu+GeKUN1TJkFsu93E5W8E9Sc3kHEGJKgenGvj19m6upSn2EunvMgMD2Yw==} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-dsv@3.0.1: + resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} + engines: {node: '>=12'} + hasBin: true + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-fetch@3.0.1: + resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} + engines: {node: '>=12'} + + d3-force-3d@3.0.6: + resolution: {integrity: sha512-4tsKHUPLOVkyfEffZo1v6sFHvGFwAIIjt/W8IThbp08DYAsXZck+2pSHEG5W1+gQgEvFLdZkYvmJAbRM2EzMnA==} + engines: {node: '>=12'} + + d3-force@3.0.0: + resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} + engines: {node: '>=12'} + + d3-format@3.1.2: + resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} + engines: {node: '>=12'} + + d3-geo-projection@4.0.0: + resolution: {integrity: sha512-p0bK60CEzph1iqmnxut7d/1kyTmm3UWtPlwdkM31AU+LW+BXazd5zJdoCn7VFxNCHXRngPHRnsNn5uGjLRGndg==} + engines: {node: '>=12'} + hasBin: true + + d3-geo@3.1.1: + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} + engines: {node: '>=12'} + + d3-hierarchy@3.1.2: + resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-octree@1.1.0: + resolution: {integrity: sha512-F8gPlqpP+HwRPMO/8uOu5wjH110+6q4cgJvgJT6vlpy3BEaDIKlTZrgHKZSp/i1InRpVfh4puY/kvL6MxK930A==} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-quadtree@3.0.1: + resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} + engines: {node: '>=12'} + + d3-random@3.0.1: + resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} + engines: {node: '>=12'} + + d3-regression@1.3.10: + resolution: {integrity: sha512-PF8GWEL70cHHWpx2jUQXc68r1pyPHIA+St16muk/XRokETzlegj5LriNKg7o4LR0TySug4nHYPJNNRz/W+/Niw==} + + d3-scale-chromatic@3.1.0: + resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + d@1.0.2: + resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} + engines: {node: '>=0.12'} + + dagre@0.8.5: + resolution: {integrity: sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==} + + data-urls@7.0.0: + resolution: {integrity: sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + dayjs@1.11.20: + resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + dom-accessibility-api@0.6.3: + resolution: {integrity: sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==} + + dom7@3.0.0: + resolution: {integrity: sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==} + + dompurify@3.4.5: + resolution: {integrity: sha512-OrwIBKsdNSVEeubdJ1HBv/wNENRM9ytAVCv7YXt//A3vPdVMNuACRqK9mXCGCBW2ln7BT/A4X0jXHo2Gu89miA==} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + electron-to-chromium@1.5.334: + resolution: {integrity: sha512-mgjZAz7Jyx1SRCwEpy9wefDS7GvNPazLthHg8eQMJ76wBdGQQDW33TCrUTvQ4wzpmOrv2zrFoD3oNufMdyMpog==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + enhanced-resolve@5.20.1: + resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} + engines: {node: '>=10.13.0'} + + entities@8.0.0: + resolution: {integrity: sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==} + engines: {node: '>=20.19.0'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@2.0.0: + resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es5-ext@0.10.64: + resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} + engines: {node: '>=0.10'} + + es6-iterator@2.0.3: + resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} + + es6-symbol@3.1.4: + resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} + engines: {node: '>=0.12'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-plugin-react-hooks@7.0.1: + resolution: {integrity: sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react-refresh@0.5.2: + resolution: {integrity: sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==} + peerDependencies: + eslint: ^9 || ^10 + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.4: + resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + esniff@2.0.1: + resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} + engines: {node: '>=0.10'} + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + event-emitter@0.3.5: + resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} + + eventemitter3@5.0.4: + resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} + + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + + ext@1.7.0: + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + + fast-wrap-ansi@0.2.0: + resolution: {integrity: sha512-rLV8JHxTyhVmFYhBJuMujcrHqOT2cnO5Zxj37qROj23CP39GXubJRBUFF0z8KFK77Uc0SukZUf7JZhsVEQ6n8w==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fecha@4.2.3: + resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + + flru@1.0.2: + resolution: {integrity: sha512-kWyh8ADvHBFz6ua5xYOPnUroZTT/bwWfrCeL0Wj1dzG4/YOmOcfJ99W8dOVyyynJN35rZ9aCOtHChqQovV7yog==} + engines: {node: '>=6'} + + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + gl-matrix@3.4.4: + resolution: {integrity: sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@17.4.0: + resolution: {integrity: sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==} + engines: {node: '>=18'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphlib@2.1.8: + resolution: {integrity: sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==} + + graphql@16.13.2: + resolution: {integrity: sha512-5bJ+nf/UCpAjHM8i06fl7eLyVC9iuNAjm9qzkiu2ZGhM0VscSvS6WDPfAwkdkBuoXGM9FJSbKl6wylMwP9Ktig==} + engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + headers-polyfill@5.0.1: + resolution: {integrity: sha512-1TJ6Fih/b8h5TIcv+1+Hw0PDQWJTKDKzFZzcKOiW1wJza3XoAQlkCuXLbymPYB8+ZQyw8mHvdw560e8zVFIWyA==} + + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + + html-encoding-sniffer@6.0.0: + resolution: {integrity: sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + html-void-elements@2.0.1: + resolution: {integrity: sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==} + + html2canvas@1.4.1: + resolution: {integrity: sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==} + engines: {node: '>=8.0.0'} + + i18next@20.6.1: + resolution: {integrity: sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A==} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + immer@9.0.21: + resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + is-any-array@3.0.0: + resolution: {integrity: sha512-o4h+tylWykC4BD1vaejp6gDxoM13bwW8FGuNs4yIKpj8xbBJcRxJx8vZpq0dCr7ZDEfeKjmsi/euolKhX6f/ww==} + + is-arrayish@0.3.4: + resolution: {integrity: sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-hotkey@0.2.0: + resolution: {integrity: sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==} + + is-mobile@5.0.0: + resolution: {integrity: sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ==} + + is-node-process@1.2.0: + resolution: {integrity: sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + is-url@1.2.4: + resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsdom@29.0.2: + resolution: {integrity: sha512-9VnGEBosc/ZpwyOsJBCQ/3I5p7Q5ngOY14a9bf5btenAORmZfDse1ZEheMiWcJ3h81+Fv7HmJFdS0szo/waF2w==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json2mq@0.2.0: + resolution: {integrity: sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.foreach@4.5.0: + resolution: {integrity: sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==} + + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead. + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.throttle@4.1.1: + resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} + + lodash.toarray@4.4.0: + resolution: {integrity: sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==} + + lodash@4.18.1: + resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} + + lru-cache@11.3.5: + resolution: {integrity: sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==} + engines: {node: 20 || >=22} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magicast@0.5.2: + resolution: {integrity: sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdn-data@2.27.1: + resolution: {integrity: sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-match@1.0.2: + resolution: {integrity: sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + ml-array-max@2.0.0: + resolution: {integrity: sha512-QQZ4kENwpWmyNb98UXRDFXrmtIXuXtt1+bSbda/2KA85+F+rrJP8hZk6QOkCQXM2Th9mUDYdq/PNByPdT9ID4A==} + + ml-array-min@2.0.0: + resolution: {integrity: sha512-GRj6Ky6sW9vGL6yIjgsHmXZ9YgrdmcQ8nCxPqEGeKc6dkfYg1XDYxGFxADUjNuZyoCd5PUscWAS4N+cFaX6hFg==} + + ml-array-rescale@2.0.0: + resolution: {integrity: sha512-2GGtKfSno94/kIloWGvpp/U5Q5vLvLrza+SAaGsLeo6Xj4mEbA6Gqx+oTfZFkxnd1grT2X007HfJNs3T5BsiVg==} + + ml-matrix@6.12.2: + resolution: {integrity: sha512-GC+BnW+pBh8Auap8goAxY0senAmF0IEoc3HNVSfnfbvGw0buuDIYb9kAKMS1l+GiwJ1rfK2bzJ8IHhwjzATSFA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + msw@2.13.6: + resolution: {integrity: sha512-GAJbQy8Ra/Ydjt0Hb2MGT2qhzd83J3+QZMHdH85uW7r/XkKc846+Ma2PLif5hGvTm5Yqa+wkcstpim0WeLZU9g==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + typescript: '>= 4.8.x' + peerDependenciesMeta: + typescript: + optional: true + + mute-stream@3.0.0: + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + engines: {node: ^20.17.0 || >=22.9.0} + + namespace-emitter@2.0.1: + resolution: {integrity: sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + next-tick@1.1.0: + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + + node-releases@2.0.37: + resolution: {integrity: sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==} + + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + outvariant@1.4.3: + resolution: {integrity: sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse5@8.0.1: + resolution: {integrity: sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pdfast@0.2.0: + resolution: {integrity: sha512-cq6TTu6qKSFUHwEahi68k/kqN2mfepjkGrG9Un70cgdRRKLKY6Rf8P8uvP2NvZktaQZNF3YE7agEkLj0vGK9bA==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + playwright-core@1.59.1: + resolution: {integrity: sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.59.1: + resolution: {integrity: sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==} + engines: {node: '>=18'} + hasBin: true + + postcss@8.5.9: + resolution: {integrity: sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==} + engines: {node: ^10 || ^12 || >=14} + + preact@10.29.1: + resolution: {integrity: sha512-gQCLc/vWroE8lIpleXtdJhTFDogTdZG9AjMUpVkDf2iTCNwYNWA+u16dL41TqUDJO4gm2IgrcMv3uTpjd4Pwmg==} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + react-dom@19.2.5: + resolution: {integrity: sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==} + peerDependencies: + react: ^19.2.5 + + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + react-router-dom@7.14.0: + resolution: {integrity: sha512-2G3ajSVSZMEtmTjIklRWlNvo8wICEpLihfD/0YMDxbWK2UyP5EGfnoIn9AIQGnF3G/FX0MRbHXdFcD+rL1ZreQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + react-router@7.14.0: + resolution: {integrity: sha512-m/xR9N4LQLmAS0ZhkY2nkPA1N7gQ5TUVa5n8TgANuDTARbn1gt+zLPXEm7W0XDTbrQ2AJSJKhoa6yx1D8BcpxQ==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + + react@19.2.5: + resolution: {integrity: sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==} + engines: {node: '>=0.10.0'} + + redent@3.0.0: + resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} + engines: {node: '>=8'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + rettime@0.11.8: + resolution: {integrity: sha512-0fERGXktJTyJ+h8fBEiPxHPEFOu0h15JY7JtwrOVqR5K+vb99ho6IyOo7ekLS3h4sJCzIDy4VWKIbZUfe9njmg==} + + rolldown@1.0.0-rc.15: + resolution: {integrity: sha512-Ff31guA5zT6WjnGp0SXw76X6hzGRk/OQq2hE+1lcDe+lJdHSgnSX6nK3erbONHyCbpSj9a9E+uX/OvytZoWp2g==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rw@1.3.3: + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + scroll-into-view-if-needed@2.2.31: + resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==} + + scroll-into-view-if-needed@3.1.0: + resolution: {integrity: sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.4: + resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + engines: {node: '>=10'} + hasBin: true + + set-cookie-parser@2.7.2: + resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} + + set-cookie-parser@3.1.0: + resolution: {integrity: sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + simple-swizzle@0.2.4: + resolution: {integrity: sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==} + + slate-history@0.66.0: + resolution: {integrity: sha512-6MWpxGQZiMvSINlCbMW43E2YBSVMCMCIwQfBzGssjWw4kb0qfvj0pIdblWNRQZD0hR6WHP+dHHgGSeVdMWzfng==} + peerDependencies: + slate: '>=0.65.3' + + slate@0.72.8: + resolution: {integrity: sha512-/nJwTswQgnRurpK+bGJFH1oM7naD5qDmHd89JyiKNT2oOKD8marW0QSBtuFnwEbL5aGCS8AmrhXQgNOsn4osAw==} + + snabbdom@3.6.3: + resolution: {integrity: sha512-W2lHLLw2qR2Vv0DcMmcxXqcfdBaIcoN+y/86SmHv8fn4DazEQSH6KN3TjZcWvwujW56OHiiirsbHWZb4vx/0fg==} + engines: {node: '>=12.17.0'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + ssr-window@3.0.0: + resolution: {integrity: sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + + std-env@4.1.0: + resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + + strict-event-emitter@0.5.1: + resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} + + string-convert@0.2.1: + resolution: {integrity: sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + styled-components@6.4.0: + resolution: {integrity: sha512-BL1EDFpt+q10eAeZB0q9ps6pSlPejaBQWBkiuM16pyoVTG4NhZrPrZK0cqNbrozxSsYwUsJ9SQYN6NyeKJYX9A==} + engines: {node: '>= 16'} + peerDependencies: + css-to-react-native: '>= 3.2.0' + react: '>= 16.8.0' + react-dom: '>= 16.8.0' + react-native: '>= 0.68.0' + peerDependenciesMeta: + css-to-react-native: + optional: true + react-dom: + optional: true + react-native: + optional: true + + stylis@4.3.6: + resolution: {integrity: sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + svg-path-parser@1.1.0: + resolution: {integrity: sha512-jGCUqcQyXpfe38R7RFfhrMyfXcBmpMNJI/B+4CE9/Unkh98UporAc461GTthv+TVDuZXsBx7/WiwJb1Oh4tt4A==} + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + tagged-tag@1.0.0: + resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} + engines: {node: '>=20'} + + tailwindcss@4.2.2: + resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==} + + tapable@2.3.2: + resolution: {integrity: sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==} + engines: {node: '>=6'} + + text-segmentation@1.0.3: + resolution: {integrity: sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==} + + throttle-debounce@5.0.2: + resolution: {integrity: sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==} + engines: {node: '>=12.22'} + + tiny-warning@1.0.3: + resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@1.1.1: + resolution: {integrity: sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==} + engines: {node: '>=18'} + + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} + engines: {node: '>=14.0.0'} + + tldts-core@7.0.28: + resolution: {integrity: sha512-7W5Efjhsc3chVdFhqtaU0KtK32J37Zcr9RKtID54nG+tIpcY79CQK/veYPODxtD/LJ4Lue66jvrQzIX2Z2/pUQ==} + + tldts@7.0.28: + resolution: {integrity: sha512-+Zg3vWhRUv8B1maGSTFdev9mjoo8Etn2Ayfs4cnjlD3CsGkxXX4QyW3j2WJ0wdjYcYmy7Lx2RDsZMhgCWafKIw==} + hasBin: true + + tough-cookie@6.0.1: + resolution: {integrity: sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==} + engines: {node: '>=16'} + + tr46@6.0.0: + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-fest@5.6.0: + resolution: {integrity: sha512-8ZiHFm91orbSAe2PSAiSVBVko18pbhbiB3U9GglSzF/zCGkR+rxpHx6sEMCUm4kxY4LjDIUGgCfUMtwfZfjfUA==} + engines: {node: '>=20'} + + type@2.7.3: + resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} + + typescript-eslint@8.58.1: + resolution: {integrity: sha512-gf6/oHChByg9HJvhMO1iBexJh12AqqTfnuxscMDOVqfJW3htsdRJI/GfPpHTTcyeB8cSTUY2JcZmVgoyPqcrDg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + typescript@6.0.2: + resolution: {integrity: sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + undici@7.25.0: + resolution: {integrity: sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==} + engines: {node: '>=20.18.1'} + + until-async@3.0.2: + resolution: {integrity: sha512-IiSk4HlzAMqTUseHHe3VhIGyuFmN90zMTpD3Z3y8jeQbzLIq500MVM7Jq2vUAnTKAFPJrqwkzr6PoTcPhGcOiw==} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + utrie@1.0.2: + resolution: {integrity: sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==} + + vite@8.0.8: + resolution: {integrity: sha512-dbU7/iLVa8KZALJyLOBOQ88nOXtNG8vxKuOT4I2mD+Ya70KPceF4IAmDsmU0h1Qsn5bPrvsY9HJstCRh3hG6Uw==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.1.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.1.5: + resolution: {integrity: sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.5 + '@vitest/browser-preview': 4.1.5 + '@vitest/browser-webdriverio': 4.1.5 + '@vitest/coverage-istanbul': 4.1.5 + '@vitest/coverage-v8': 4.1.5 + '@vitest/ui': 4.1.5 + happy-dom: '*' + jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + webidl-conversions@8.0.1: + resolution: {integrity: sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==} + engines: {node: '>=20'} + + whatwg-mimetype@5.0.0: + resolution: {integrity: sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==} + engines: {node: '>=20'} + + whatwg-url@16.0.1: + resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + wildcard@1.1.2: + resolution: {integrity: sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng==} + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + zod@4.3.6: + resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + + zustand@4.5.7: + resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==} + engines: {node: '>=12.7.0'} + peerDependencies: + '@types/react': '>=16.8' + immer: '>=9.0.6' + react: '>=16.8' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + + zustand@5.0.12: + resolution: {integrity: sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + +snapshots: + + '@adobe/css-tools@4.4.4': {} + + '@ant-design/charts-util@0.0.1-alpha.7(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + lodash: 4.18.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@ant-design/charts-util@0.0.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + lodash: 4.18.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@ant-design/charts@2.6.7(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@ant-design/graphs': 2.1.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@ant-design/plots': 2.6.8(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + lodash: 4.18.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + transitivePeerDependencies: + - css-to-react-native + - react-native + + '@ant-design/colors@8.0.1': + dependencies: + '@ant-design/fast-color': 3.0.1 + + '@ant-design/cssinjs-utils@2.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@ant-design/cssinjs': 2.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@babel/runtime': 7.29.2 + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@ant-design/cssinjs@2.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@babel/runtime': 7.29.2 + '@emotion/hash': 0.8.0 + '@emotion/unitless': 0.7.5 + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + csstype: 3.2.3 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + stylis: 4.3.6 + + '@ant-design/fast-color@3.0.1': {} + + '@ant-design/graphs@2.1.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@ant-design/charts-util': 0.0.1-alpha.7(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@antv/g6': 5.1.0 + '@antv/g6-extension-react': 0.2.7(@antv/g6@5.1.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@antv/graphin': 3.0.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + lodash: 4.18.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + styled-components: 6.4.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + transitivePeerDependencies: + - css-to-react-native + - react-native + + '@ant-design/icons-svg@4.4.2': {} + + '@ant-design/icons@6.1.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@ant-design/colors': 8.0.1 + '@ant-design/icons-svg': 4.4.2 + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@ant-design/plots@2.6.8(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@ant-design/charts-util': 0.0.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@antv/event-emitter': 0.1.3 + '@antv/g': 6.3.1 + '@antv/g2': 5.4.8 + '@antv/g2-extension-plot': 0.2.2 + lodash: 4.18.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@ant-design/react-slick@2.0.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@babel/runtime': 7.29.2 + clsx: 2.1.1 + json2mq: 0.2.0 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + throttle-debounce: 5.0.2 + + '@antv/algorithm@0.1.26': + dependencies: + '@antv/util': 2.0.17 + tslib: 2.8.1 + + '@antv/component@2.1.11': + dependencies: + '@antv/g': 6.3.1 + '@antv/scale': 0.4.16 + '@antv/util': 3.3.11 + svg-path-parser: 1.1.0 + + '@antv/coord@0.4.7': + dependencies: + '@antv/scale': 0.4.16 + '@antv/util': 2.0.17 + gl-matrix: 3.4.4 + + '@antv/event-emitter@0.1.3': {} + + '@antv/expr@1.0.2': {} + + '@antv/g-canvas@2.2.0': + dependencies: + '@antv/g-lite': 2.7.0 + '@antv/g-math': 3.1.0 + '@antv/util': 3.3.11 + '@babel/runtime': 7.29.2 + gl-matrix: 3.4.4 + tslib: 2.8.1 + + '@antv/g-lite@2.7.0': + dependencies: + '@antv/g-math': 3.1.0 + '@antv/util': 3.3.11 + '@antv/vendor': 1.0.11 + '@babel/runtime': 7.29.2 + eventemitter3: 5.0.4 + gl-matrix: 3.4.4 + tslib: 2.8.1 + + '@antv/g-math@3.1.0': + dependencies: + '@antv/util': 3.3.11 + '@babel/runtime': 7.29.2 + gl-matrix: 3.4.4 + tslib: 2.8.1 + + '@antv/g-plugin-dragndrop@2.1.1': + dependencies: + '@antv/g-lite': 2.7.0 + '@antv/util': 3.3.11 + '@babel/runtime': 7.29.2 + tslib: 2.8.1 + + '@antv/g-svg@2.1.1': + dependencies: + '@antv/g-lite': 2.7.0 + '@antv/util': 3.3.11 + '@babel/runtime': 7.29.2 + gl-matrix: 3.4.4 + tslib: 2.8.1 + + '@antv/g2-extension-plot@0.2.2': + dependencies: + '@antv/g2': 5.4.8 + '@antv/util': 3.3.11 + '@antv/vendor': 1.0.11 + + '@antv/g2@5.4.8': + dependencies: + '@antv/component': 2.1.11 + '@antv/coord': 0.4.7 + '@antv/event-emitter': 0.1.3 + '@antv/expr': 1.0.2 + '@antv/g': 6.3.1 + '@antv/g-canvas': 2.2.0 + '@antv/g-plugin-dragndrop': 2.1.1 + '@antv/scale': 0.5.2 + '@antv/util': 3.3.11 + '@antv/vendor': 1.0.11 + flru: 1.0.2 + pdfast: 0.2.0 + + '@antv/g6-extension-react@0.2.7(@antv/g6@5.1.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@antv/g': 6.3.1 + '@antv/g-svg': 2.1.1 + '@antv/g6': 5.1.0 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@antv/g6@5.1.0': + dependencies: + '@antv/algorithm': 0.1.26 + '@antv/component': 2.1.11 + '@antv/event-emitter': 0.1.3 + '@antv/g': 6.3.1 + '@antv/g-canvas': 2.2.0 + '@antv/g-plugin-dragndrop': 2.1.1 + '@antv/graphlib': 2.0.4 + '@antv/hierarchy': 0.7.1 + '@antv/layout': 2.0.0 + '@antv/util': 3.3.11 + bubblesets-js: 2.3.4 + + '@antv/g@6.3.1': + dependencies: + '@antv/g-lite': 2.7.0 + '@antv/util': 3.3.11 + '@babel/runtime': 7.29.2 + gl-matrix: 3.4.4 + html2canvas: 1.4.1 + + '@antv/graphin@3.0.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@antv/g6': 5.1.0 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@antv/graphlib@2.0.4': + dependencies: + '@antv/event-emitter': 0.1.3 + + '@antv/hierarchy@0.7.1': {} + + '@antv/layout@2.0.0': + dependencies: + '@antv/event-emitter': 0.1.3 + '@antv/expr': 1.0.2 + '@antv/graphlib': 2.0.4 + '@antv/util': 3.3.11 + comlink: 4.4.2 + d3-force: 3.0.0 + d3-force-3d: 3.0.6 + d3-octree: 1.1.0 + d3-quadtree: 3.0.1 + dagre: 0.8.5 + ml-matrix: 6.12.2 + tslib: 2.8.1 + + '@antv/scale@0.4.16': + dependencies: + '@antv/util': 3.3.11 + color-string: 1.9.1 + fecha: 4.2.3 + + '@antv/scale@0.5.2': + dependencies: + '@antv/util': 3.3.11 + color-string: 1.9.1 + fecha: 4.2.3 + + '@antv/util@2.0.17': + dependencies: + csstype: 3.2.3 + tslib: 2.8.1 + + '@antv/util@3.3.11': + dependencies: + fast-deep-equal: 3.1.3 + gl-matrix: 3.4.4 + tslib: 2.8.1 + + '@antv/vendor@1.0.11': + dependencies: + '@types/d3-array': 3.2.2 + '@types/d3-color': 3.1.3 + '@types/d3-dispatch': 3.0.7 + '@types/d3-dsv': 3.0.7 + '@types/d3-ease': 3.0.2 + '@types/d3-fetch': 3.0.7 + '@types/d3-force': 3.0.10 + '@types/d3-format': 3.0.4 + '@types/d3-geo': 3.1.0 + '@types/d3-hierarchy': 3.1.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-path': 3.1.1 + '@types/d3-quadtree': 3.0.6 + '@types/d3-random': 3.0.3 + '@types/d3-scale': 4.0.9 + '@types/d3-scale-chromatic': 3.1.0 + '@types/d3-shape': 3.1.8 + '@types/d3-time': 3.0.4 + '@types/d3-timer': 3.0.2 + d3-array: 3.2.4 + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-dsv: 3.0.1 + d3-ease: 3.0.1 + d3-fetch: 3.0.1 + d3-force: 3.0.0 + d3-force-3d: 3.0.6 + d3-format: 3.1.2 + d3-geo: 3.1.1 + d3-geo-projection: 4.0.0 + d3-hierarchy: 3.1.2 + d3-interpolate: 3.0.1 + d3-path: 3.1.0 + d3-quadtree: 3.0.1 + d3-random: 3.0.1 + d3-regression: 1.3.10 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-timer: 3.0.1 + + '@asamuzakjp/css-color@5.1.11': + dependencies: + '@asamuzakjp/generational-cache': 1.0.1 + '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-color-parser': 4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@asamuzakjp/dom-selector@7.1.1': + dependencies: + '@asamuzakjp/generational-cache': 1.0.1 + '@asamuzakjp/nwsapi': 2.3.9 + bidi-js: 1.0.3 + css-tree: 3.2.1 + is-potential-custom-element-name: 1.0.1 + + '@asamuzakjp/generational-cache@1.0.1': {} + + '@asamuzakjp/nwsapi@2.3.9': {} + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.0': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.2 + '@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.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.29.2': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.2': + dependencies: + '@babel/types': 7.29.0 + + '@babel/runtime@7.29.2': {} + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.2 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@bcoe/v8-coverage@1.0.2': {} + + '@bramus/specificity@2.4.2': + dependencies: + css-tree: 3.2.1 + + '@csstools/color-helpers@6.0.2': {} + + '@csstools/css-calc@3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-color-parser@4.1.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/color-helpers': 6.0.2 + '@csstools/css-calc': 3.2.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0) + '@csstools/css-parser-algorithms': 4.0.0(@csstools/css-tokenizer@4.0.0) + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0)': + dependencies: + '@csstools/css-tokenizer': 4.0.0 + + '@csstools/css-syntax-patches-for-csstree@1.1.3(css-tree@3.2.1)': + optionalDependencies: + css-tree: 3.2.1 + + '@csstools/css-tokenizer@4.0.0': {} + + '@dnd-kit/accessibility@3.1.1(react@19.2.5)': + dependencies: + react: 19.2.5 + tslib: 2.8.1 + + '@dnd-kit/core@6.3.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@dnd-kit/accessibility': 3.1.1(react@19.2.5) + '@dnd-kit/utilities': 3.2.2(react@19.2.5) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + tslib: 2.8.1 + + '@dnd-kit/sortable@10.0.0(@dnd-kit/core@6.3.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(react@19.2.5)': + dependencies: + '@dnd-kit/core': 6.3.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@dnd-kit/utilities': 3.2.2(react@19.2.5) + react: 19.2.5 + tslib: 2.8.1 + + '@dnd-kit/utilities@3.2.2(react@19.2.5)': + dependencies: + react: 19.2.5 + tslib: 2.8.1 + + '@emnapi/core@1.9.2': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.9.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emotion/hash@0.8.0': {} + + '@emotion/is-prop-valid@1.4.0': + dependencies: + '@emotion/memoize': 0.9.0 + + '@emotion/memoize@0.9.0': {} + + '@emotion/unitless@0.7.5': {} + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.6.1))': + dependencies: + eslint: 9.39.4(jiti@2.6.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.2': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.5': + dependencies: + ajv: 6.14.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.4': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@exodus/bytes@1.15.0': {} + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@inquirer/ansi@2.0.5': {} + + '@inquirer/confirm@6.0.12(@types/node@24.12.2)': + dependencies: + '@inquirer/core': 11.1.9(@types/node@24.12.2) + '@inquirer/type': 4.0.5(@types/node@24.12.2) + optionalDependencies: + '@types/node': 24.12.2 + + '@inquirer/core@11.1.9(@types/node@24.12.2)': + dependencies: + '@inquirer/ansi': 2.0.5 + '@inquirer/figures': 2.0.5 + '@inquirer/type': 4.0.5(@types/node@24.12.2) + cli-width: 4.1.0 + fast-wrap-ansi: 0.2.0 + mute-stream: 3.0.0 + signal-exit: 4.1.0 + optionalDependencies: + '@types/node': 24.12.2 + + '@inquirer/figures@2.0.5': {} + + '@inquirer/type@4.0.5(@types/node@24.12.2)': + optionalDependencies: + '@types/node': 24.12.2 + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@mswjs/interceptors@0.41.6': + dependencies: + '@open-draft/deferred-promise': 2.2.0 + '@open-draft/logger': 0.3.0 + '@open-draft/until': 2.1.0 + is-node-process: 1.2.0 + outvariant: 1.4.3 + strict-event-emitter: 0.5.1 + + '@napi-rs/wasm-runtime@1.1.3(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2)': + dependencies: + '@emnapi/core': 1.9.2 + '@emnapi/runtime': 1.9.2 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@open-draft/deferred-promise@2.2.0': {} + + '@open-draft/deferred-promise@3.0.0': {} + + '@open-draft/logger@0.3.0': + dependencies: + is-node-process: 1.2.0 + outvariant: 1.4.3 + + '@open-draft/until@2.1.0': {} + + '@oxc-project/types@0.124.0': {} + + '@playwright/test@1.59.1': + dependencies: + playwright: 1.59.1 + + '@rc-component/async-validator@5.1.0': + dependencies: + '@babel/runtime': 7.29.2 + + '@rc-component/cascader@1.14.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/select': 1.6.15(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/tree': 1.2.4(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/checkbox@2.0.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/collapse@1.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@babel/runtime': 7.29.2 + '@rc-component/motion': 1.3.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/color-picker@3.1.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@ant-design/fast-color': 3.0.1 + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/context@2.0.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/dialog@1.8.4(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/motion': 1.3.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/portal': 2.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/drawer@1.4.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/motion': 1.3.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/portal': 2.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/dropdown@1.0.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/trigger': 3.9.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/form@1.8.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/async-validator': 5.1.0 + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/image@1.8.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/motion': 1.3.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/portal': 2.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/input-number@1.6.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/mini-decimal': 1.1.3 + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/input@1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/mentions@1.6.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/input': 1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/menu': 1.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/textarea': 1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/trigger': 3.9.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/menu@1.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/motion': 1.3.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/overflow': 1.0.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/trigger': 3.9.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/mini-decimal@1.1.3': + dependencies: + '@babel/runtime': 7.29.2 + + '@rc-component/motion@1.3.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/mutate-observer@2.0.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/notification@1.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/motion': 1.3.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/overflow@1.0.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@babel/runtime': 7.29.2 + '@rc-component/resize-observer': 1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/pagination@1.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/picker@1.9.1(dayjs@1.11.20)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/overflow': 1.0.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/resize-observer': 1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/trigger': 3.9.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + optionalDependencies: + dayjs: 1.11.20 + + '@rc-component/portal@2.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/progress@1.0.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/qrcode@1.1.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@babel/runtime': 7.29.2 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/rate@1.0.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/resize-observer@1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/segmented@1.3.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@babel/runtime': 7.29.2 + '@rc-component/motion': 1.3.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/select@1.6.15(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/overflow': 1.0.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/trigger': 3.9.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/virtual-list': 1.0.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/slider@1.0.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/steps@1.2.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/switch@1.0.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/table@1.9.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/context': 2.0.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/resize-observer': 1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/virtual-list': 1.0.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/tabs@1.7.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/dropdown': 1.0.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/menu': 1.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/motion': 1.3.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/resize-observer': 1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/textarea@1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/input': 1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/resize-observer': 1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/tooltip@1.4.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/trigger': 3.9.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/tour@2.3.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/portal': 2.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/trigger': 3.9.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/tree-select@1.8.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/select': 1.6.15(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/tree': 1.2.4(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/tree@1.2.4(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/motion': 1.3.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/virtual-list': 1.0.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/trigger@3.9.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/motion': 1.3.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/portal': 2.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/resize-observer': 1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/upload@1.1.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rc-component/util@1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + is-mobile: 5.0.0 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + react-is: 18.3.1 + + '@rc-component/virtual-list@1.0.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@babel/runtime': 7.29.2 + '@rc-component/resize-observer': 1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@rolldown/binding-android-arm64@1.0.0-rc.15': + optional: true + + '@rolldown/binding-darwin-arm64@1.0.0-rc.15': + optional: true + + '@rolldown/binding-darwin-x64@1.0.0-rc.15': + optional: true + + '@rolldown/binding-freebsd-x64@1.0.0-rc.15': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.15': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.15': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.0.0-rc.15': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.15': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.0.0-rc.15': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.0.0-rc.15': + optional: true + + '@rolldown/binding-linux-x64-musl@1.0.0-rc.15': + optional: true + + '@rolldown/binding-openharmony-arm64@1.0.0-rc.15': + optional: true + + '@rolldown/binding-wasm32-wasi@1.0.0-rc.15': + dependencies: + '@emnapi/core': 1.9.2 + '@emnapi/runtime': 1.9.2 + '@napi-rs/wasm-runtime': 1.1.3(@emnapi/core@1.9.2)(@emnapi/runtime@1.9.2) + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.15': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.0.0-rc.15': + optional: true + + '@rolldown/pluginutils@1.0.0-rc.15': {} + + '@rolldown/pluginutils@1.0.0-rc.7': {} + + '@standard-schema/spec@1.1.0': {} + + '@tailwindcss/node@4.2.2': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.20.1 + jiti: 2.6.1 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.2.2 + + '@tailwindcss/oxide-android-arm64@4.2.2': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.2.2': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.2.2': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.2.2': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.2.2': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.2.2': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.2.2': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.2.2': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.2.2': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.2.2': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.2.2': + optional: true + + '@tailwindcss/oxide@4.2.2': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.2.2 + '@tailwindcss/oxide-darwin-arm64': 4.2.2 + '@tailwindcss/oxide-darwin-x64': 4.2.2 + '@tailwindcss/oxide-freebsd-x64': 4.2.2 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.2 + '@tailwindcss/oxide-linux-arm64-gnu': 4.2.2 + '@tailwindcss/oxide-linux-arm64-musl': 4.2.2 + '@tailwindcss/oxide-linux-x64-gnu': 4.2.2 + '@tailwindcss/oxide-linux-x64-musl': 4.2.2 + '@tailwindcss/oxide-wasm32-wasi': 4.2.2 + '@tailwindcss/oxide-win32-arm64-msvc': 4.2.2 + '@tailwindcss/oxide-win32-x64-msvc': 4.2.2 + + '@tailwindcss/vite@4.2.2(vite@8.0.8(@types/node@24.12.2)(jiti@2.6.1))': + dependencies: + '@tailwindcss/node': 4.2.2 + '@tailwindcss/oxide': 4.2.2 + tailwindcss: 4.2.2 + vite: 8.0.8(@types/node@24.12.2)(jiti@2.6.1) + + '@testing-library/dom@10.4.1': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/runtime': 7.29.2 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + '@testing-library/jest-dom@6.9.1': + dependencies: + '@adobe/css-tools': 4.4.4 + aria-query: 5.3.2 + css.escape: 1.5.1 + dom-accessibility-api: 0.6.3 + picocolors: 1.1.1 + redent: 3.0.0 + + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@babel/runtime': 7.29.2 + '@testing-library/dom': 10.4.1 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + optionalDependencies: + '@types/react': 19.2.14 + '@types/react-dom': 19.2.3(@types/react@19.2.14) + + '@transloadit/prettier-bytes@0.0.7': {} + + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/aria-query@5.0.4': {} + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/d3-array@3.2.2': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-dispatch@3.0.7': {} + + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-dsv@3.0.7': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-fetch@3.0.7': + dependencies: + '@types/d3-dsv': 3.0.7 + + '@types/d3-force@3.0.10': {} + + '@types/d3-format@3.0.4': {} + + '@types/d3-geo@3.1.0': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/d3-hierarchy@3.1.7': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-quadtree@3.0.6': {} + + '@types/d3-random@3.0.3': {} + + '@types/d3-scale-chromatic@3.1.0': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-selection@3.0.11': {} + + '@types/d3-shape@3.1.8': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + + '@types/d3-transition@3.0.9': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + + '@types/deep-eql@4.0.2': {} + + '@types/dompurify@3.2.0': + dependencies: + dompurify: 3.4.5 + + '@types/estree@1.0.8': {} + + '@types/event-emitter@0.3.5': {} + + '@types/geojson@7946.0.16': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@24.12.2': + dependencies: + undici-types: 7.16.0 + + '@types/react-dom@19.2.3(@types/react@19.2.14)': + dependencies: + '@types/react': 19.2.14 + + '@types/react@19.2.14': + dependencies: + csstype: 3.2.3 + + '@types/set-cookie-parser@2.4.10': + dependencies: + '@types/node': 24.12.2 + + '@types/statuses@2.0.6': {} + + '@types/trusted-types@2.0.7': + optional: true + + '@typescript-eslint/eslint-plugin@8.58.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2) + '@typescript-eslint/scope-manager': 8.58.1 + '@typescript-eslint/type-utils': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2) + '@typescript-eslint/utils': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2) + '@typescript-eslint/visitor-keys': 8.58.1 + eslint: 9.39.4(jiti@2.6.1) + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@6.0.2) + typescript: 6.0.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)': + dependencies: + '@typescript-eslint/scope-manager': 8.58.1 + '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/typescript-estree': 8.58.1(typescript@6.0.2) + '@typescript-eslint/visitor-keys': 8.58.1 + debug: 4.4.3 + eslint: 9.39.4(jiti@2.6.1) + typescript: 6.0.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.58.1(typescript@6.0.2)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.58.1(typescript@6.0.2) + '@typescript-eslint/types': 8.58.1 + debug: 4.4.3 + typescript: 6.0.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.58.1': + dependencies: + '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/visitor-keys': 8.58.1 + + '@typescript-eslint/tsconfig-utils@8.58.1(typescript@6.0.2)': + dependencies: + typescript: 6.0.2 + + '@typescript-eslint/type-utils@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)': + dependencies: + '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/typescript-estree': 8.58.1(typescript@6.0.2) + '@typescript-eslint/utils': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2) + debug: 4.4.3 + eslint: 9.39.4(jiti@2.6.1) + ts-api-utils: 2.5.0(typescript@6.0.2) + typescript: 6.0.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.58.1': {} + + '@typescript-eslint/typescript-estree@8.58.1(typescript@6.0.2)': + dependencies: + '@typescript-eslint/project-service': 8.58.1(typescript@6.0.2) + '@typescript-eslint/tsconfig-utils': 8.58.1(typescript@6.0.2) + '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/visitor-keys': 8.58.1 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.7.4 + tinyglobby: 0.2.16 + ts-api-utils: 2.5.0(typescript@6.0.2) + typescript: 6.0.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.58.1 + '@typescript-eslint/types': 8.58.1 + '@typescript-eslint/typescript-estree': 8.58.1(typescript@6.0.2) + eslint: 9.39.4(jiti@2.6.1) + typescript: 6.0.2 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.58.1': + dependencies: + '@typescript-eslint/types': 8.58.1 + eslint-visitor-keys: 5.0.1 + + '@uppy/companion-client@2.2.2': + dependencies: + '@uppy/utils': 4.1.3 + namespace-emitter: 2.0.1 + + '@uppy/core@2.3.4': + dependencies: + '@transloadit/prettier-bytes': 0.0.7 + '@uppy/store-default': 2.1.1 + '@uppy/utils': 4.1.3 + lodash.throttle: 4.1.1 + mime-match: 1.0.2 + namespace-emitter: 2.0.1 + nanoid: 3.3.11 + preact: 10.29.1 + + '@uppy/store-default@2.1.1': {} + + '@uppy/utils@4.1.3': + dependencies: + lodash.throttle: 4.1.1 + + '@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4)': + dependencies: + '@uppy/companion-client': 2.2.2 + '@uppy/core': 2.3.4 + '@uppy/utils': 4.1.3 + nanoid: 3.3.11 + + '@vitejs/plugin-react@6.0.1(vite@8.0.8(@types/node@24.12.2)(jiti@2.6.1))': + dependencies: + '@rolldown/pluginutils': 1.0.0-rc.7 + vite: 8.0.8(@types/node@24.12.2)(jiti@2.6.1) + + '@vitest/coverage-v8@4.1.5(vitest@4.1.5)': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.1.5 + ast-v8-to-istanbul: 1.0.0 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.2.0 + magicast: 0.5.2 + obug: 2.1.1 + std-env: 4.1.0 + tinyrainbow: 3.1.0 + vitest: 4.1.5(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(jsdom@29.0.2)(msw@2.13.6(@types/node@24.12.2)(typescript@6.0.2))(vite@8.0.8(@types/node@24.12.2)(jiti@2.6.1)) + + '@vitest/expect@4.1.5': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.5 + '@vitest/utils': 4.1.5 + chai: 6.2.2 + tinyrainbow: 3.1.0 + + '@vitest/mocker@4.1.5(msw@2.13.6(@types/node@24.12.2)(typescript@6.0.2))(vite@8.0.8(@types/node@24.12.2)(jiti@2.6.1))': + dependencies: + '@vitest/spy': 4.1.5 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + msw: 2.13.6(@types/node@24.12.2)(typescript@6.0.2) + vite: 8.0.8(@types/node@24.12.2)(jiti@2.6.1) + + '@vitest/pretty-format@4.1.5': + dependencies: + tinyrainbow: 3.1.0 + + '@vitest/runner@4.1.5': + dependencies: + '@vitest/utils': 4.1.5 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.5': + dependencies: + '@vitest/pretty-format': 4.1.5 + '@vitest/utils': 4.1.5 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.5': {} + + '@vitest/utils@4.1.5': + dependencies: + '@vitest/pretty-format': 4.1.5 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 + + '@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + is-url: 1.2.4 + lodash.throttle: 4.1.1 + nanoid: 3.3.11 + slate: 0.72.8 + snabbdom: 3.6.3 + + '@wangeditor/code-highlight@1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + prismjs: 1.30.0 + slate: 0.72.8 + snabbdom: 3.6.3 + + '@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@types/event-emitter': 0.3.5 + '@uppy/core': 2.3.4 + '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) + dom7: 3.0.0 + event-emitter: 0.3.5 + html-void-elements: 2.0.1 + i18next: 20.6.1 + is-hotkey: 0.2.0 + lodash.camelcase: 4.3.0 + lodash.clonedeep: 4.5.0 + lodash.debounce: 4.0.8 + lodash.foreach: 4.5.0 + lodash.isequal: 4.5.0 + lodash.throttle: 4.1.1 + lodash.toarray: 4.4.0 + nanoid: 3.3.11 + scroll-into-view-if-needed: 2.2.31 + slate: 0.72.8 + slate-history: 0.66.0(slate@0.72.8) + snabbdom: 3.6.3 + + '@wangeditor/editor-for-react@1.0.6(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(@wangeditor/editor@5.1.23)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/editor': 5.1.23 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + + '@wangeditor/editor@5.1.23': + dependencies: + '@uppy/core': 2.3.4 + '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) + '@wangeditor/basic-modules': 1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/code-highlight': 1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/list-module': 1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/table-module': 1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/upload-image-module': 1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/video-module': 1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + is-hotkey: 0.2.0 + lodash.camelcase: 4.3.0 + lodash.clonedeep: 4.5.0 + lodash.debounce: 4.0.8 + lodash.foreach: 4.5.0 + lodash.isequal: 4.5.0 + lodash.throttle: 4.1.1 + lodash.toarray: 4.4.0 + nanoid: 3.3.11 + slate: 0.72.8 + snabbdom: 3.6.3 + + '@wangeditor/list-module@1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + slate: 0.72.8 + snabbdom: 3.6.3 + + '@wangeditor/table-module@1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + lodash.isequal: 4.5.0 + lodash.throttle: 4.1.1 + nanoid: 3.3.11 + slate: 0.72.8 + snabbdom: 3.6.3 + + '@wangeditor/upload-image-module@1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@uppy/core': 2.3.4 + '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) + '@wangeditor/basic-modules': 1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + lodash.foreach: 4.5.0 + slate: 0.72.8 + snabbdom: 3.6.3 + + '@wangeditor/video-module@1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3))(dom7@3.0.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3)': + dependencies: + '@uppy/core': 2.3.4 + '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.11)(slate@0.72.8)(snabbdom@3.6.3) + dom7: 3.0.0 + nanoid: 3.3.11 + slate: 0.72.8 + snabbdom: 3.6.3 + + '@xyflow/react@12.10.2(@types/react@19.2.14)(immer@9.0.21)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + dependencies: + '@xyflow/system': 0.0.76 + classcat: 5.0.5 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + zustand: 4.5.7(@types/react@19.2.14)(immer@9.0.21)(react@19.2.5) + transitivePeerDependencies: + - '@types/react' + - immer + + '@xyflow/system@0.0.76': + dependencies: + '@types/d3-drag': 3.0.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-zoom: 3.0.0 + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + ajv@6.14.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@5.2.0: {} + + antd@6.3.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + dependencies: + '@ant-design/colors': 8.0.1 + '@ant-design/cssinjs': 2.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@ant-design/cssinjs-utils': 2.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@ant-design/fast-color': 3.0.1 + '@ant-design/icons': 6.1.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@ant-design/react-slick': 2.0.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@babel/runtime': 7.29.2 + '@rc-component/cascader': 1.14.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/checkbox': 2.0.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/collapse': 1.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/color-picker': 3.1.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/dialog': 1.8.4(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/drawer': 1.4.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/dropdown': 1.0.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/form': 1.8.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/image': 1.8.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/input': 1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/input-number': 1.6.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/mentions': 1.6.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/menu': 1.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/motion': 1.3.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/mutate-observer': 2.0.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/notification': 1.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/pagination': 1.2.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/picker': 1.9.1(dayjs@1.11.20)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/progress': 1.0.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/qrcode': 1.1.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/rate': 1.0.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/resize-observer': 1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/segmented': 1.3.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/select': 1.6.15(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/slider': 1.0.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/steps': 1.2.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/switch': 1.0.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/table': 1.9.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/tabs': 1.7.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/textarea': 1.1.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/tooltip': 1.4.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/tour': 2.3.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/tree': 1.2.4(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/tree-select': 1.8.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/trigger': 3.9.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/upload': 1.1.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@rc-component/util': 1.10.1(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + clsx: 2.1.1 + dayjs: 1.11.20 + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + scroll-into-view-if-needed: 3.1.0 + throttle-debounce: 5.0.2 + transitivePeerDependencies: + - date-fns + - luxon + - moment + + argparse@2.0.1: {} + + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + aria-query@5.3.2: {} + + assertion-error@2.0.1: {} + + ast-v8-to-istanbul@1.0.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 10.0.0 + + asynckit@0.4.0: {} + + axios@1.15.0: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.5 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + base64-arraybuffer@1.0.2: {} + + baseline-browser-mapping@2.10.17: {} + + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + + brace-expansion@1.1.13: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@5.0.5: + dependencies: + balanced-match: 4.0.4 + + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.17 + caniuse-lite: 1.0.30001787 + electron-to-chromium: 1.5.334 + node-releases: 2.0.37 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + + bubblesets-js@2.3.4: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001787: {} + + chai@6.2.2: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + classcat@5.0.5: {} + + cli-width@4.1.0: {} + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clsx@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.4 + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + comlink@4.4.2: {} + + commander@7.2.0: {} + + compute-scroll-into-view@1.0.20: {} + + compute-scroll-into-view@3.1.1: {} + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + cookie@1.1.1: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + css-line-break@2.1.0: + dependencies: + utrie: 1.0.2 + + css-tree@3.2.1: + dependencies: + mdn-data: 2.27.1 + source-map-js: 1.2.1 + + css.escape@1.5.1: {} + + csstype@3.2.3: {} + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-binarytree@1.0.2: {} + + d3-color@3.1.0: {} + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-dsv@3.0.1: + dependencies: + commander: 7.2.0 + iconv-lite: 0.6.3 + rw: 1.3.3 + + d3-ease@3.0.1: {} + + d3-fetch@3.0.1: + dependencies: + d3-dsv: 3.0.1 + + d3-force-3d@3.0.6: + dependencies: + d3-binarytree: 1.0.2 + d3-dispatch: 3.0.1 + d3-octree: 1.1.0 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + + d3-force@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + + d3-format@3.1.2: {} + + d3-geo-projection@4.0.0: + dependencies: + commander: 7.2.0 + d3-array: 3.2.4 + d3-geo: 3.1.1 + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + + d3-hierarchy@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-octree@1.1.0: {} + + d3-path@3.1.0: {} + + d3-quadtree@3.0.1: {} + + d3-random@3.0.1: {} + + d3-regression@1.3.10: {} + + d3-scale-chromatic@3.1.0: + dependencies: + d3-color: 3.1.0 + d3-interpolate: 3.0.1 + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.2 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-selection@3.0.0: {} + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d@1.0.2: + dependencies: + es5-ext: 0.10.64 + type: 2.7.3 + + dagre@0.8.5: + dependencies: + graphlib: 2.1.8 + lodash: 4.18.1 + + data-urls@7.0.0: + dependencies: + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.1 + transitivePeerDependencies: + - '@noble/hashes' + + dayjs@1.11.20: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decimal.js@10.6.0: {} + + deep-is@0.1.4: {} + + delayed-stream@1.0.0: {} + + dequal@2.0.3: {} + + detect-libc@2.1.2: {} + + dom-accessibility-api@0.5.16: {} + + dom-accessibility-api@0.6.3: {} + + dom7@3.0.0: + dependencies: + ssr-window: 3.0.0 + + dompurify@3.4.5: + optionalDependencies: + '@types/trusted-types': 2.0.7 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + electron-to-chromium@1.5.334: {} + + emoji-regex@8.0.0: {} + + enhanced-resolve@5.20.1: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.2 + + entities@8.0.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@2.0.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es5-ext@0.10.64: + dependencies: + es6-iterator: 2.0.3 + es6-symbol: 3.1.4 + esniff: 2.0.1 + next-tick: 1.1.0 + + es6-iterator@2.0.3: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-symbol: 3.1.4 + + es6-symbol@3.1.4: + dependencies: + d: 1.0.2 + ext: 1.7.0 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-plugin-react-hooks@7.0.1(eslint@9.39.4(jiti@2.6.1)): + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.2 + eslint: 9.39.4(jiti@2.6.1) + hermes-parser: 0.25.1 + zod: 4.3.6 + zod-validation-error: 4.0.2(zod@4.3.6) + transitivePeerDependencies: + - supports-color + + eslint-plugin-react-refresh@0.5.2(eslint@9.39.4(jiti@2.6.1)): + dependencies: + eslint: 9.39.4(jiti@2.6.1) + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.4(jiti@2.6.1): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.6.1)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.5 + '@eslint/js': 9.39.4 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.6.1 + transitivePeerDependencies: + - supports-color + + esniff@2.0.1: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + event-emitter: 0.3.5 + type: 2.7.3 + + espree@10.4.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + esutils@2.0.3: {} + + event-emitter@0.3.5: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + + eventemitter3@5.0.4: {} + + expect-type@1.3.0: {} + + ext@1.7.0: + dependencies: + type: 2.7.3 + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + + fast-wrap-ansi@0.2.0: + dependencies: + fast-string-width: 3.0.2 + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + fecha@4.2.3: {} + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flatted@3.4.2: {} + + flru@1.0.2: {} + + follow-redirects@1.15.11: {} + + form-data@4.0.5: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + fsevents@2.3.2: + optional: true + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + gl-matrix@3.4.4: {} + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@17.4.0: {} + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graphlib@2.1.8: + dependencies: + lodash: 4.18.1 + + graphql@16.13.2: {} + + has-flag@4.0.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + headers-polyfill@5.0.1: + dependencies: + '@types/set-cookie-parser': 2.4.10 + set-cookie-parser: 3.1.0 + + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + + html-encoding-sniffer@6.0.0: + dependencies: + '@exodus/bytes': 1.15.0 + transitivePeerDependencies: + - '@noble/hashes' + + html-escaper@2.0.2: {} + + html-void-elements@2.0.1: {} + + html2canvas@1.4.1: + dependencies: + css-line-break: 2.1.0 + text-segmentation: 1.0.3 + + i18next@20.6.1: + dependencies: + '@babel/runtime': 7.29.2 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + immer@9.0.21: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + indent-string@4.0.0: {} + + internmap@2.0.3: {} + + is-any-array@3.0.0: {} + + is-arrayish@0.3.4: {} + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-hotkey@0.2.0: {} + + is-mobile@5.0.0: {} + + is-node-process@1.2.0: {} + + is-plain-object@5.0.0: {} + + is-potential-custom-element-name@1.0.1: {} + + is-url@1.2.4: {} + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + jiti@2.6.1: {} + + js-tokens@10.0.0: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsdom@29.0.2: + dependencies: + '@asamuzakjp/css-color': 5.1.11 + '@asamuzakjp/dom-selector': 7.1.1 + '@bramus/specificity': 2.4.2 + '@csstools/css-syntax-patches-for-csstree': 1.1.3(css-tree@3.2.1) + '@exodus/bytes': 1.15.0 + css-tree: 3.2.1 + data-urls: 7.0.0 + decimal.js: 10.6.0 + html-encoding-sniffer: 6.0.0 + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.3.5 + parse5: 8.0.1 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 6.0.1 + undici: 7.25.0 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 8.0.1 + whatwg-mimetype: 5.0.0 + whatwg-url: 16.0.1 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - '@noble/hashes' + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json2mq@0.2.0: + dependencies: + string-convert: 0.2.1 + + json5@2.2.3: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + 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 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.camelcase@4.3.0: {} + + lodash.clonedeep@4.5.0: {} + + lodash.debounce@4.0.8: {} + + lodash.foreach@4.5.0: {} + + lodash.isequal@4.5.0: {} + + lodash.merge@4.6.2: {} + + lodash.throttle@4.1.1: {} + + lodash.toarray@4.4.0: {} + + lodash@4.18.1: {} + + lru-cache@11.3.5: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lz-string@1.5.0: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magicast@0.5.2: + dependencies: + '@babel/parser': 7.29.2 + '@babel/types': 7.29.0 + source-map-js: 1.2.1 + + make-dir@4.0.0: + dependencies: + semver: 7.7.4 + + math-intrinsics@1.1.0: {} + + mdn-data@2.27.1: {} + + mime-db@1.52.0: {} + + mime-match@1.0.2: + dependencies: + wildcard: 1.1.2 + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + min-indent@1.0.1: {} + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.5 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.13 + + ml-array-max@2.0.0: + dependencies: + is-any-array: 3.0.0 + + ml-array-min@2.0.0: + dependencies: + is-any-array: 3.0.0 + + ml-array-rescale@2.0.0: + dependencies: + is-any-array: 3.0.0 + ml-array-max: 2.0.0 + ml-array-min: 2.0.0 + + ml-matrix@6.12.2: + dependencies: + is-any-array: 3.0.0 + ml-array-rescale: 2.0.0 + + ms@2.1.3: {} + + msw@2.13.6(@types/node@24.12.2)(typescript@6.0.2): + dependencies: + '@inquirer/confirm': 6.0.12(@types/node@24.12.2) + '@mswjs/interceptors': 0.41.6 + '@open-draft/deferred-promise': 3.0.0 + '@types/statuses': 2.0.6 + cookie: 1.1.1 + graphql: 16.13.2 + headers-polyfill: 5.0.1 + is-node-process: 1.2.0 + outvariant: 1.4.3 + path-to-regexp: 6.3.0 + picocolors: 1.1.1 + rettime: 0.11.8 + statuses: 2.0.2 + strict-event-emitter: 0.5.1 + tough-cookie: 6.0.1 + type-fest: 5.6.0 + until-async: 3.0.2 + yargs: 17.7.2 + optionalDependencies: + typescript: 6.0.2 + transitivePeerDependencies: + - '@types/node' + + mute-stream@3.0.0: {} + + namespace-emitter@2.0.1: {} + + nanoid@3.3.11: {} + + natural-compare@1.4.0: {} + + next-tick@1.1.0: {} + + node-releases@2.0.37: {} + + obug@2.1.1: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + outvariant@1.4.3: {} + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse5@8.0.1: + dependencies: + entities: 8.0.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-to-regexp@6.3.0: {} + + pathe@2.0.3: {} + + pdfast@0.2.0: {} + + picocolors@1.1.1: {} + + picomatch@4.0.4: {} + + playwright-core@1.59.1: {} + + playwright@1.59.1: + dependencies: + playwright-core: 1.59.1 + optionalDependencies: + fsevents: 2.3.2 + + postcss@8.5.9: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + preact@10.29.1: {} + + prelude-ls@1.2.1: {} + + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + + prismjs@1.30.0: {} + + proxy-from-env@2.1.0: {} + + punycode@2.3.1: {} + + react-dom@19.2.5(react@19.2.5): + dependencies: + react: 19.2.5 + scheduler: 0.27.0 + + react-is@17.0.2: {} + + react-is@18.3.1: {} + + react-router-dom@7.14.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + dependencies: + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + react-router: 7.14.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + + react-router@7.14.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + dependencies: + cookie: 1.1.1 + react: 19.2.5 + set-cookie-parser: 2.7.2 + optionalDependencies: + react-dom: 19.2.5(react@19.2.5) + + react@19.2.5: {} + + redent@3.0.0: + dependencies: + indent-string: 4.0.0 + strip-indent: 3.0.0 + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + resolve-from@4.0.0: {} + + rettime@0.11.8: {} + + rolldown@1.0.0-rc.15: + dependencies: + '@oxc-project/types': 0.124.0 + '@rolldown/pluginutils': 1.0.0-rc.15 + optionalDependencies: + '@rolldown/binding-android-arm64': 1.0.0-rc.15 + '@rolldown/binding-darwin-arm64': 1.0.0-rc.15 + '@rolldown/binding-darwin-x64': 1.0.0-rc.15 + '@rolldown/binding-freebsd-x64': 1.0.0-rc.15 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.15 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.15 + '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.15 + '@rolldown/binding-linux-ppc64-gnu': 1.0.0-rc.15 + '@rolldown/binding-linux-s390x-gnu': 1.0.0-rc.15 + '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.15 + '@rolldown/binding-linux-x64-musl': 1.0.0-rc.15 + '@rolldown/binding-openharmony-arm64': 1.0.0-rc.15 + '@rolldown/binding-wasm32-wasi': 1.0.0-rc.15 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.15 + '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.15 + + rw@1.3.3: {} + + safer-buffer@2.1.2: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + + scheduler@0.27.0: {} + + scroll-into-view-if-needed@2.2.31: + dependencies: + compute-scroll-into-view: 1.0.20 + + scroll-into-view-if-needed@3.1.0: + dependencies: + compute-scroll-into-view: 3.1.1 + + semver@6.3.1: {} + + semver@7.7.4: {} + + set-cookie-parser@2.7.2: {} + + set-cookie-parser@3.1.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + siginfo@2.0.0: {} + + signal-exit@4.1.0: {} + + simple-swizzle@0.2.4: + dependencies: + is-arrayish: 0.3.4 + + slate-history@0.66.0(slate@0.72.8): + dependencies: + is-plain-object: 5.0.0 + slate: 0.72.8 + + slate@0.72.8: + dependencies: + immer: 9.0.21 + is-plain-object: 5.0.0 + tiny-warning: 1.0.3 + + snabbdom@3.6.3: {} + + source-map-js@1.2.1: {} + + ssr-window@3.0.0: {} + + stackback@0.0.2: {} + + statuses@2.0.2: {} + + std-env@4.1.0: {} + + strict-event-emitter@0.5.1: {} + + string-convert@0.2.1: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-indent@3.0.0: + dependencies: + min-indent: 1.0.1 + + strip-json-comments@3.1.1: {} + + styled-components@6.4.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + dependencies: + '@emotion/is-prop-valid': 1.4.0 + csstype: 3.2.3 + react: 19.2.5 + stylis: 4.3.6 + optionalDependencies: + react-dom: 19.2.5(react@19.2.5) + + stylis@4.3.6: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + svg-path-parser@1.1.0: {} + + symbol-tree@3.2.4: {} + + tagged-tag@1.0.0: {} + + tailwindcss@4.2.2: {} + + tapable@2.3.2: {} + + text-segmentation@1.0.3: + dependencies: + utrie: 1.0.2 + + throttle-debounce@5.0.2: {} + + tiny-warning@1.0.3: {} + + tinybench@2.9.0: {} + + tinyexec@1.1.1: {} + + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + tinyrainbow@3.1.0: {} + + tldts-core@7.0.28: {} + + tldts@7.0.28: + dependencies: + tldts-core: 7.0.28 + + tough-cookie@6.0.1: + dependencies: + tldts: 7.0.28 + + tr46@6.0.0: + dependencies: + punycode: 2.3.1 + + ts-api-utils@2.5.0(typescript@6.0.2): + dependencies: + typescript: 6.0.2 + + tslib@2.8.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + type-fest@5.6.0: + dependencies: + tagged-tag: 1.0.0 + + type@2.7.3: {} + + typescript-eslint@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2): + dependencies: + '@typescript-eslint/eslint-plugin': 8.58.1(@typescript-eslint/parser@8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2))(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2) + '@typescript-eslint/parser': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2) + '@typescript-eslint/typescript-estree': 8.58.1(typescript@6.0.2) + '@typescript-eslint/utils': 8.58.1(eslint@9.39.4(jiti@2.6.1))(typescript@6.0.2) + eslint: 9.39.4(jiti@2.6.1) + typescript: 6.0.2 + transitivePeerDependencies: + - supports-color + + typescript@6.0.2: {} + + undici-types@7.16.0: {} + + undici@7.25.0: {} + + until-async@3.0.2: {} + + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + use-sync-external-store@1.6.0(react@19.2.5): + dependencies: + react: 19.2.5 + + utrie@1.0.2: + dependencies: + base64-arraybuffer: 1.0.2 + + vite@8.0.8(@types/node@24.12.2)(jiti@2.6.1): + dependencies: + lightningcss: 1.32.0 + picomatch: 4.0.4 + postcss: 8.5.9 + rolldown: 1.0.0-rc.15 + tinyglobby: 0.2.16 + optionalDependencies: + '@types/node': 24.12.2 + fsevents: 2.3.3 + jiti: 2.6.1 + + vitest@4.1.5(@types/node@24.12.2)(@vitest/coverage-v8@4.1.5)(jsdom@29.0.2)(msw@2.13.6(@types/node@24.12.2)(typescript@6.0.2))(vite@8.0.8(@types/node@24.12.2)(jiti@2.6.1)): + dependencies: + '@vitest/expect': 4.1.5 + '@vitest/mocker': 4.1.5(msw@2.13.6(@types/node@24.12.2)(typescript@6.0.2))(vite@8.0.8(@types/node@24.12.2)(jiti@2.6.1)) + '@vitest/pretty-format': 4.1.5 + '@vitest/runner': 4.1.5 + '@vitest/snapshot': 4.1.5 + '@vitest/spy': 4.1.5 + '@vitest/utils': 4.1.5 + es-module-lexer: 2.0.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.4 + std-env: 4.1.0 + tinybench: 2.9.0 + tinyexec: 1.1.1 + tinyglobby: 0.2.16 + tinyrainbow: 3.1.0 + vite: 8.0.8(@types/node@24.12.2)(jiti@2.6.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 24.12.2 + '@vitest/coverage-v8': 4.1.5(vitest@4.1.5) + jsdom: 29.0.2 + transitivePeerDependencies: + - msw + + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + webidl-conversions@8.0.1: {} + + whatwg-mimetype@5.0.0: {} + + whatwg-url@16.0.1: + dependencies: + '@exodus/bytes': 1.15.0 + tr46: 6.0.0 + webidl-conversions: 8.0.1 + transitivePeerDependencies: + - '@noble/hashes' + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + wildcard@1.1.2: {} + + word-wrap@1.2.5: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + + y18n@5.0.8: {} + + yallist@3.1.1: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} + + zod-validation-error@4.0.2(zod@4.3.6): + dependencies: + zod: 4.3.6 + + zod@4.3.6: {} + + zustand@4.5.7(@types/react@19.2.14)(immer@9.0.21)(react@19.2.5): + dependencies: + use-sync-external-store: 1.6.0(react@19.2.5) + optionalDependencies: + '@types/react': 19.2.14 + immer: 9.0.21 + react: 19.2.5 + + zustand@5.0.12(@types/react@19.2.14)(immer@9.0.21)(react@19.2.5)(use-sync-external-store@1.6.0(react@19.2.5)): + optionalDependencies: + '@types/react': 19.2.14 + immer: 9.0.21 + react: 19.2.5 + use-sync-external-store: 1.6.0(react@19.2.5) diff --git a/apps/web/public/crm.wasm b/apps/web/public/crm.wasm new file mode 100644 index 0000000..94339aa Binary files /dev/null and b/apps/web/public/crm.wasm differ diff --git a/apps/web/public/favicon.svg b/apps/web/public/favicon.svg new file mode 100644 index 0000000..6893eb1 --- /dev/null +++ b/apps/web/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/web/public/icons.svg b/apps/web/public/icons.svg new file mode 100644 index 0000000..e952219 --- /dev/null +++ b/apps/web/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/web/public/inventory.wasm b/apps/web/public/inventory.wasm new file mode 100644 index 0000000..f402120 Binary files /dev/null and b/apps/web/public/inventory.wasm differ diff --git a/apps/web/public/mockServiceWorker.js b/apps/web/public/mockServiceWorker.js new file mode 100644 index 0000000..80f1930 --- /dev/null +++ b/apps/web/public/mockServiceWorker.js @@ -0,0 +1,349 @@ +/* eslint-disable */ +/* tslint:disable */ + +/** + * Mock Service Worker. + * @see https://github.com/mswjs/msw + * - Please do NOT modify this file. + */ + +const PACKAGE_VERSION = '2.13.6' +const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82' +const IS_MOCKED_RESPONSE = Symbol('isMockedResponse') +const activeClientIds = new Set() + +addEventListener('install', function () { + self.skipWaiting() +}) + +addEventListener('activate', function (event) { + event.waitUntil(self.clients.claim()) +}) + +addEventListener('message', async function (event) { + const clientId = Reflect.get(event.source || {}, 'id') + + if (!clientId || !self.clients) { + return + } + + const client = await self.clients.get(clientId) + + if (!client) { + return + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }) + break + } + + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: { + packageVersion: PACKAGE_VERSION, + checksum: INTEGRITY_CHECKSUM, + }, + }) + break + } + + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId) + + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: { + client: { + id: client.id, + frameType: client.frameType, + }, + }, + }) + break + } + + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId) + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId + }) + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister() + } + + break + } + } +}) + +addEventListener('fetch', function (event) { + const requestInterceptedAt = Date.now() + + // Bypass navigation requests. + if (event.request.mode === 'navigate') { + return + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if ( + event.request.cache === 'only-if-cached' && + event.request.mode !== 'same-origin' + ) { + return + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been terminated (still remains active until the next reload). + if (activeClientIds.size === 0) { + return + } + + const requestId = crypto.randomUUID() + event.respondWith(handleRequest(event, requestId, requestInterceptedAt)) +}) + +/** + * @param {FetchEvent} event + * @param {string} requestId + * @param {number} requestInterceptedAt + */ +async function handleRequest(event, requestId, requestInterceptedAt) { + const client = await resolveMainClient(event) + const requestCloneForEvents = event.request.clone() + const response = await getResponse( + event, + client, + requestId, + requestInterceptedAt, + ) + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + const serializedRequest = await serializeRequest(requestCloneForEvents) + + // Clone the response so both the client and the library could consume it. + const responseClone = response.clone() + + sendToClient( + client, + { + type: 'RESPONSE', + payload: { + isMockedResponse: IS_MOCKED_RESPONSE in response, + request: { + id: requestId, + ...serializedRequest, + }, + response: { + type: responseClone.type, + status: responseClone.status, + statusText: responseClone.statusText, + headers: Object.fromEntries(responseClone.headers.entries()), + body: responseClone.body, + }, + }, + }, + responseClone.body ? [serializedRequest.body, responseClone.body] : [], + ) + } + + return response +} + +/** + * Resolve the main client for the given event. + * Client that issues a request doesn't necessarily equal the client + * that registered the worker. It's with the latter the worker should + * communicate with during the response resolving phase. + * @param {FetchEvent} event + * @returns {Promise} + */ +async function resolveMainClient(event) { + const client = await self.clients.get(event.clientId) + + if (activeClientIds.has(event.clientId)) { + return client + } + + if (client?.frameType === 'top-level') { + return client + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible' + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id) + }) +} + +/** + * @param {FetchEvent} event + * @param {Client | undefined} client + * @param {string} requestId + * @param {number} requestInterceptedAt + * @returns {Promise} + */ +async function getResponse(event, client, requestId, requestInterceptedAt) { + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const requestClone = event.request.clone() + + function passthrough() { + // Cast the request headers to a new Headers instance + // so the headers can be manipulated with. + const headers = new Headers(requestClone.headers) + + // Remove the "accept" header value that marked this request as passthrough. + // This prevents request alteration and also keeps it compliant with the + // user-defined CORS policies. + const acceptHeader = headers.get('accept') + if (acceptHeader) { + const values = acceptHeader.split(',').map((value) => value.trim()) + const filteredValues = values.filter( + (value) => value !== 'msw/passthrough', + ) + + if (filteredValues.length > 0) { + headers.set('accept', filteredValues.join(', ')) + } else { + headers.delete('accept') + } + } + + return fetch(requestClone, { headers }) + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough() + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough() + } + + // Notify the client that a request has been intercepted. + const serializedRequest = await serializeRequest(event.request) + const clientMessage = await sendToClient( + client, + { + type: 'REQUEST', + payload: { + id: requestId, + interceptedAt: requestInterceptedAt, + ...serializedRequest, + }, + }, + [serializedRequest.body], + ) + + switch (clientMessage.type) { + case 'MOCK_RESPONSE': { + return respondWithMock(clientMessage.data) + } + + case 'PASSTHROUGH': { + return passthrough() + } + } + + return passthrough() +} + +/** + * @param {Client} client + * @param {any} message + * @param {Array} transferrables + * @returns {Promise} + */ +function sendToClient(client, message, transferrables = []) { + return new Promise((resolve, reject) => { + const channel = new MessageChannel() + + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error) + } + + resolve(event.data) + } + + client.postMessage(message, [ + channel.port2, + ...transferrables.filter(Boolean), + ]) + }) +} + +/** + * @param {Response} response + * @returns {Response} + */ +function respondWithMock(response) { + // Setting response status code to 0 is a no-op. + // However, when responding with a "Response.error()", the produced Response + // instance will have status code set to 0. Since it's not possible to create + // a Response instance with status code 0, handle that use-case separately. + if (response.status === 0) { + return Response.error() + } + + const mockedResponse = new Response(response.body, response) + + Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, { + value: true, + enumerable: true, + }) + + return mockedResponse +} + +/** + * @param {Request} request + */ +async function serializeRequest(request) { + return { + url: request.url, + mode: request.mode, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: await request.arrayBuffer(), + keepalive: request.keepalive, + } +} diff --git a/apps/web/public/robots.txt b/apps/web/public/robots.txt new file mode 100644 index 0000000..c2a49f4 --- /dev/null +++ b/apps/web/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Allow: / diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx new file mode 100644 index 0000000..051f1fd --- /dev/null +++ b/apps/web/src/App.tsx @@ -0,0 +1,371 @@ +import { useEffect, lazy, Suspense, useMemo } from 'react'; +import { HashRouter, Routes, Route, Navigate, useLocation, useNavigate } from 'react-router-dom'; +import { ConfigProvider, theme as antdTheme, Spin, Result } from 'antd'; +import zhCN from 'antd/locale/zh_CN'; +import MainLayout from './layouts/MainLayout'; +import Login from './pages/Login'; +import { ErrorBoundary } from './components/ErrorBoundary'; +import { useAuthStore } from './stores/auth'; +import { useAppStore } from './stores/app'; +import type { ThemeName } from './stores/app'; +import { ROUTE_PERMISSIONS, FROZEN_ROUTES, validateRouteCoverage } from './routeConfig'; + +const Home = lazy(() => import('./pages/Home')); +const Users = lazy(() => import('./pages/Users')); +const Roles = lazy(() => import('./pages/Roles')); +const Organizations = lazy(() => import('./pages/Organizations')); +const Workflow = lazy(() => import('./pages/Workflow')); +const Messages = lazy(() => import('./pages/Messages')); +const Settings = lazy(() => import('./pages/Settings')); +const PluginAdmin = lazy(() => import('./pages/PluginAdmin')); +const PluginMarket = lazy(() => import('./pages/PluginMarket')); +const PluginCRUDPage = lazy(() => import('./pages/PluginCRUDPage')); +const PluginTabsPage = lazy(() => import('./pages/PluginTabsPage').then((m) => ({ default: m.PluginTabsPage }))); +const PluginTreePage = lazy(() => import('./pages/PluginTreePage').then((m) => ({ default: m.PluginTreePage }))); +const PluginGraphPage = lazy(() => import('./pages/PluginGraphPage').then((m) => ({ default: m.PluginGraphPage }))); +const PluginDashboardPage = lazy(() => import('./pages/PluginDashboardPage').then((m) => ({ default: m.PluginDashboardPage }))); +const PluginKanbanPage = lazy(() => import('./pages/PluginKanbanPage')); + +// 健康管理模块 +const PatientList = lazy(() => import('./pages/health/PatientList')); +const PatientDetail = lazy(() => import('./pages/health/PatientDetail')); +const PatientTagManage = lazy(() => import('./pages/health/PatientTagManage')); +const DoctorList = lazy(() => import('./pages/health/DoctorList')); +const AppointmentList = lazy(() => import('./pages/health/AppointmentList')); +const DoctorSchedule = lazy(() => import('./pages/health/DoctorSchedule')); +const FollowUpTaskList = lazy(() => import('./pages/health/FollowUpTaskList')); +const FollowUpRecordList = lazy(() => import('./pages/health/FollowUpRecordList')); +const ConsultationList = lazy(() => import('./pages/health/ConsultationList')); +const ConsultationDetail = lazy(() => import('./pages/health/ConsultationDetail')); +const PointsRuleList = lazy(() => import('./pages/health/PointsRuleList')); +const PointsProductList = lazy(() => import('./pages/health/PointsProductList')); +const PointsOrderList = lazy(() => import('./pages/health/PointsOrderList')); +const OfflineEventList = lazy(() => import('./pages/health/OfflineEventList')); +const StatisticsDashboard = lazy(() => import('./pages/health/StatisticsDashboard')); +const AiPromptList = lazy(() => import('./pages/health/AiPromptList')); +const AiAnalysisList = lazy(() => import('./pages/health/AiAnalysisList')); +const AiUsageDashboard = lazy(() => import('./pages/health/AiUsageDashboard')); +const AiConfigPage = lazy(() => import('./pages/health/AiConfigPage')); +const KnowledgeV2Page = lazy(() => import('./pages/ai/KnowledgeV2Page')); +const AiChatPage = lazy(() => import('./pages/ai/ChatPage')); +const AlertList = lazy(() => import('./pages/health/AlertList')); +const AlertDashboard = lazy(() => import('./pages/health/AlertDashboard')); +const AlertRuleList = lazy(() => import('./pages/health/AlertRuleList')); +const DeviceManage = lazy(() => import('./pages/health/DeviceManage')); +const RealtimeMonitor = lazy(() => import('./pages/health/RealtimeMonitor')); +const OAuthClientList = lazy(() => import('./pages/health/OAuthClientList')); +const DialysisManageList = lazy(() => import('./pages/health/DialysisManageList')); +const ActionInbox = lazy(() => import('./pages/health/ActionInbox')); +const FollowUpTemplateList = lazy(() => import('./pages/health/FollowUpTemplateList')); +const CarePlanList = lazy(() => import('./pages/health/CarePlanList')); +const CarePlanDetail = lazy(() => import('./pages/health/CarePlanDetail')); +const ShiftList = lazy(() => import('./pages/health/ShiftList')); +const ShiftDetail = lazy(() => import('./pages/health/ShiftDetail')); +const MedicationRecordList = lazy(() => import('./pages/health/MedicationRecordList')); +const BleGatewayList = lazy(() => import('./pages/health/BleGatewayList')); +const BleGatewayDetail = lazy(() => import('./pages/health/BleGatewayDetail')); +const CriticalValueThresholdList = lazy(() => import('./pages/health/CriticalValueThresholdList')); +const FamilyProxyPage = lazy(() => import('./pages/health/FamilyProxyPage')); + +// 内容管理 +const ArticleManageList = lazy(() => import('./pages/health/ArticleManageList')); +const ArticleEditor = lazy(() => import('./pages/health/articleEditor/ArticleEditor')); +const ArticleCategoryManage = lazy(() => import('./pages/health/ArticleCategoryManage')); +const ArticleTagManage = lazy(() => import('./pages/health/ArticleTagManage')); +const BannerManage = lazy(() => import('./pages/health/BannerManage')); +const MediaLibrary = lazy(() => import('./pages/health/MediaLibrary')); + +function FrozenRoute() { + return ; +} + +function ForbiddenPage() { + const navigate = useNavigate(); + return ( +
+ navigate('/')} style={{ cursor: 'pointer', color: 'var(--ant-color-primary)', background: 'none', border: 'none', fontSize: 14 }}>返回首页} + /> +
+ ); +} + +function PrivateRoute({ children }: { children: React.ReactNode }) { + const isAuthenticated = useAuthStore((s) => s.isAuthenticated); + const permissions = useAuthStore((s) => s.permissions); + const location = useLocation(); + + if (!isAuthenticated) return ; + + const path = location.pathname; + + // 冻结路由检查 + if (FROZEN_ROUTES.some((frozen) => path.startsWith(frozen))) { + return ; + } + + // 首页/工作台始终放行 + if (path === '/' || path === '') return <>{children}; + + const matchedPrefix = Object.keys(ROUTE_PERMISSIONS).find( + (prefix) => path === prefix || path.startsWith(prefix + '/'), + ); + if (matchedPrefix) { + const required = ROUTE_PERMISSIONS[matchedPrefix]; + const hasAccess = required.some((r) => permissions.includes(r)); + if (!hasAccess) return ; + } else { + return ; + } + + return <>{children}; +} + +const baseToken = { + borderRadius: 10, + borderRadiusLG: 12, + borderRadiusSM: 6, + fontFamily: "'Noto Sans SC', -apple-system, system-ui, 'Segoe UI', Roboto, 'PingFang SC', 'Microsoft YaHei', Helvetica, Arial, sans-serif", + fontSize: 14, + fontSizeHeading4: 20, + controlHeight: 40, + controlHeightLG: 44, + controlHeightSM: 32, + boxShadow: 'none', + boxShadowSecondary: '0 1px 3px rgba(0,0,0,0.06), 0 1px 2px rgba(0,0,0,0.04)', +}; + +const baseComponents = { + Button: { primaryShadow: 'none', fontWeight: 500 }, + Card: { paddingLG: 20 }, + Menu: { itemBorderRadius: 10, itemMarginInline: 8, itemHeight: 40 }, + Modal: { borderRadiusLG: 16 }, + Tag: { borderRadiusSM: 6 }, +}; + +const themeConfigs: Record; components: Record> }> = { + blue: { + token: { + ...baseToken, + colorPrimary: '#2563eb', + colorSuccess: '#059669', + colorWarning: '#d97706', + colorError: '#dc2626', + colorInfo: '#0284c7', + colorBgLayout: '#f8fafc', + colorBgContainer: '#ffffff', + colorBgElevated: '#ffffff', + colorBorder: '#e2e8f0', + colorBorderSecondary: '#f1f5f9', + }, + components: { + ...baseComponents, + Table: { headerBg: '#f1f5f9', headerColor: '#475569', rowHoverBg: '#f1f5f9', fontSize: 14 }, + }, + }, + warm: { + token: { + ...baseToken, + borderRadius: 12, + borderRadiusLG: 14, + borderRadiusSM: 8, + colorPrimary: '#C4623A', + colorSuccess: '#5B7A5E', + colorWarning: '#C4873A', + colorError: '#B54A4A', + colorInfo: '#8B7A5E', + colorBgLayout: '#F5F0EB', + colorBgContainer: '#ffffff', + colorBgElevated: '#ffffff', + colorBorder: '#E8E2DC', + colorBorderSecondary: '#F0EBE5', + }, + components: { + ...baseComponents, + Table: { headerBg: '#EDE8E2', headerColor: '#7A756E', rowHoverBg: '#F5F0EB', fontSize: 14 }, + }, + }, + dark: { + token: { + ...baseToken, + colorPrimary: '#60A5FA', + colorSuccess: '#34D399', + colorWarning: '#FBBF24', + colorError: '#F87171', + colorInfo: '#38BDF8', + colorBgLayout: '#0F172A', + colorBgContainer: '#1E293B', + colorBgElevated: '#334155', + colorBorder: '#334155', + colorBorderSecondary: 'rgba(255,255,255,0.06)', + boxShadow: 'none', + boxShadowSecondary: '0 2px 8px rgba(0,0,0,0.3), 0 1px 3px rgba(0,0,0,0.2)', + }, + components: { + ...baseComponents, + Table: { headerBg: '#1E293B', headerColor: '#94A3B8', rowHoverBg: '#1E293B', fontSize: 14 }, + }, + }, + emerald: { + token: { + ...baseToken, + borderRadius: 10, + borderRadiusLG: 14, + borderRadiusSM: 8, + colorPrimary: '#5B7A5E', + colorSuccess: '#3D7A42', + colorWarning: '#B8863A', + colorError: '#A54A4A', + colorInfo: '#4A7A8B', + colorBgLayout: '#F4F7F4', + colorBgContainer: '#ffffff', + colorBgElevated: '#ffffff', + colorBorder: '#D5DED5', + colorBorderSecondary: '#E5ECE5', + }, + components: { + ...baseComponents, + Table: { headerBg: '#EDF2ED', headerColor: '#5A6E5A', rowHoverBg: '#F4F7F4', fontSize: 14 }, + }, + }, +}; + +export default function App() { + const loadFromStorage = useAuthStore((s) => s.loadFromStorage); + const themeName = useAppStore((s) => s.theme); + + useEffect(() => { + loadFromStorage(); + }, [loadFromStorage]); + + useEffect(() => { + document.documentElement.setAttribute('data-theme', themeName); + }, [themeName]); + + // DEV mode: validate all routes have permission declarations + useEffect(() => { + validateRouteCoverage([ + "/users", "/roles", "/organizations", "/workflow", "/messages", "/settings", + "/plugins/admin", "/plugins/market", + "/health/statistics", "/health/patients", "/health/tags", "/health/doctors", + "/health/appointments", "/health/schedules", "/health/follow-up-tasks", + "/health/follow-up-records", "/health/consultations", + "/health/points-rules", "/health/points-products", "/health/points-orders", + "/health/offline-events", "/health/ai-prompts", "/health/ai-analysis", + "/health/ai-usage", "/health/ai-config", "/health/ai-knowledge", "/health/alerts", "/health/alert-dashboard", + "/ai/chat", + "/health/alert-rules", "/health/devices", "/health/realtime-monitor", + "/health/oauth-clients", "/health/dialysis", "/health/action-inbox", + "/health/follow-up-templates", "/health/care-plans", "/health/shifts", + "/health/medications", "/health/ble-gateways", + "/health/critical-value-thresholds", "/health/diagnoses", + "/health/family-proxy", "/health/consents", + "/health/articles", "/health/article-categories", "/health/article-tags", + "/health/banners", "/health/media-library", + "/health/medication-records", + ]); + }, []); + + const isDark = themeName === 'dark'; + const antTheme = useMemo(() => themeConfigs[themeName] ?? themeConfigs.blue, [themeName]); + + return ( + <> + 跳转到主要内容 + + + + } /> + + + + }> + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + {/* 健康管理 */} + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + {/* 内容管理 */} + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + + + + } + /> + + + + + ); +} diff --git a/apps/web/src/api/ai/analysis.test.ts b/apps/web/src/api/ai/analysis.test.ts new file mode 100644 index 0000000..f2ec95c --- /dev/null +++ b/apps/web/src/api/ai/analysis.test.ts @@ -0,0 +1,127 @@ +/** + * AI 模块 API 契约测试(analysis + prompts + suggestions + usage) + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('../client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import { analysisApi } from './analysis' +import { promptApi } from './prompts' +import { suggestionApi } from './suggestions' +import { usageApi } from './usage' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('analysisApi', () => { + const fakeRes = { data: { data: {} } } + + it('list 应调用 GET /ai/analysis/history 并传递查询参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await analysisApi.list({ patient_id: 'p-001', analysis_type: 'lab-report', page: 1, page_size: 10 }) + + expect(mockGet).toHaveBeenCalledWith('/ai/analysis/history', { + params: { patient_id: 'p-001', analysis_type: 'lab-report', page: 1, page_size: 10 }, + }) + }) + + it('get 应调用 GET /ai/analysis/:id', async () => { + mockGet.mockResolvedValue(fakeRes) + await analysisApi.get('ana-001') + + expect(mockGet).toHaveBeenCalledWith('/ai/analysis/ana-001') + }) +}) + +describe('promptApi', () => { + const fakeRes = { data: { data: {} } } + + it('list 应调用 GET /ai/prompts 并传递查询参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await promptApi.list({ category: 'analysis', page: 1, page_size: 10 }) + + expect(mockGet).toHaveBeenCalledWith('/ai/prompts', { + params: { category: 'analysis', page: 1, page_size: 10 }, + }) + }) + + it('create 应调用 POST /ai/prompts 并传递请求体', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { name: '化验解读', system_prompt: '你是专业医生', user_prompt_template: '解读: {report}', model_config: {}, category: 'analysis' } + await promptApi.create(req) + + expect(mockPost).toHaveBeenCalledWith('/ai/prompts', req) + }) + + it('activate 应调用 POST /ai/prompts/:id/activate', async () => { + mockPost.mockResolvedValue(fakeRes) + await promptApi.activate('prompt-001') + + expect(mockPost).toHaveBeenCalledWith('/ai/prompts/prompt-001/activate') + }) + + it('rollback 应调用 POST /ai/prompts/:id/rollback', async () => { + mockPost.mockResolvedValue(fakeRes) + await promptApi.rollback('prompt-001') + + expect(mockPost).toHaveBeenCalledWith('/ai/prompts/prompt-001/rollback') + }) +}) + +describe('suggestionApi', () => { + const fakeRes = { data: { data: {} } } + + it('list 应调用 GET /ai/suggestions 并传递查询参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await suggestionApi.list({ analysis_id: 'ana-001', status: 'pending' }) + + expect(mockGet).toHaveBeenCalledWith('/ai/suggestions', { + params: { analysis_id: 'ana-001', status: 'pending' }, + }) + }) + + it('approve 应调用 POST /ai/suggestions/:id/approve 并传递 action', async () => { + mockPost.mockResolvedValue(fakeRes) + await suggestionApi.approve('sug-001', 'approve') + + expect(mockPost).toHaveBeenCalledWith('/ai/suggestions/sug-001/approve', { action: 'approve' }) + }) + + it('getComparison 应调用 GET /ai/suggestions/:id/comparison', async () => { + mockGet.mockResolvedValue(fakeRes) + await suggestionApi.getComparison('sug-001') + + expect(mockGet).toHaveBeenCalledWith('/ai/suggestions/sug-001/comparison') + }) +}) + +describe('usageApi', () => { + const fakeRes = { data: { data: {} } } + + it('overview 应调用 GET /ai/usage/overview', async () => { + mockGet.mockResolvedValue(fakeRes) + await usageApi.overview() + + expect(mockGet).toHaveBeenCalledWith('/ai/usage/overview') + }) + + it('byType 应调用 GET /ai/usage/by-type', async () => { + mockGet.mockResolvedValue(fakeRes) + await usageApi.byType() + + expect(mockGet).toHaveBeenCalledWith('/ai/usage/by-type') + }) +}) diff --git a/apps/web/src/api/ai/analysis.ts b/apps/web/src/api/ai/analysis.ts new file mode 100644 index 0000000..cdfbff9 --- /dev/null +++ b/apps/web/src/api/ai/analysis.ts @@ -0,0 +1,47 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +export interface AnalysisItem { + id: string; + patient_id: string; + patient_name?: string; + analysis_type: string; + source_ref: string; + model_used: string; + status: string; + result_content: string | null; + result_metadata: Record | null; + error_message: string | null; + created_at: string; + updated_at: string; +} + +export interface HealthSummaryResponse { + patient_id: string; + risk_level: 'low' | 'medium' | 'high' | 'critical'; + active_insights_count: number; + recent_analyses_count: number; + latest_insight_title: string | null; + latest_analysis_type: string | null; + summary_items: Array<{ + category: string; + title: string; + severity: string | null; + created_at: string; + }>; +} + +export const analysisApi = { + list: async (params?: { patient_id?: string; analysis_type?: string; page?: number; page_size?: number }) => { + const resp = await client.get('/ai/analysis/history', { params }); + return resp.data.data as PaginatedResponse; + }, + get: async (id: string) => { + const resp = await client.get(`/ai/analysis/${id}`); + return resp.data.data as AnalysisItem; + }, + getHealthSummary: async (patientId: string) => { + const resp = await client.get('/ai/health-summary', { params: { patient_id: patientId } }); + return resp.data.data as HealthSummaryResponse; + }, +}; diff --git a/apps/web/src/api/ai/analysisSse.ts b/apps/web/src/api/ai/analysisSse.ts new file mode 100644 index 0000000..4dee7af --- /dev/null +++ b/apps/web/src/api/ai/analysisSse.ts @@ -0,0 +1,98 @@ +export type AnalysisType = 'lab-report' | 'trends' | 'checkup-plan' | 'report-summary' | 'follow-up-summary'; + +interface AnalyzeBody { + report_id?: string; + patient_id?: string; + metrics?: string[]; + source_id?: string; +} + +const ENDPOINT_MAP: Record = { + 'lab-report': '/ai/analyze/lab-report', + 'trends': '/ai/analyze/trends', + 'checkup-plan': '/ai/analyze/checkup-plan', + 'report-summary': '/ai/analyze/report-summary', + 'follow-up-summary': '/ai/analyze/follow-up-summary', +}; + +export interface SseCallbacks { + onChunk: (content: string, index: number) => void; + onError: (message: string) => void; + onDone: (analysisId: string) => void; +} + +export async function startAnalysis( + type: AnalysisType, + body: AnalyzeBody, + callbacks: SseCallbacks, +): Promise { + const controller = new AbortController(); + const endpoint = ENDPOINT_MAP[type]; + + const token = localStorage.getItem('hms-token'); + const resp = await fetch(`/api/v1${endpoint}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${token}`, + }, + body: JSON.stringify(body), + signal: controller.signal, + }); + + if (!resp.ok) { + const err = await resp.json().catch(() => ({ message: '分析请求失败' })); + callbacks.onError(err?.message || `HTTP ${resp.status}`); + return controller; + } + + const reader = resp.body?.getReader(); + if (!reader) { + callbacks.onError('无法读取响应流'); + return controller; + } + + const decoder = new TextDecoder(); + let chunkIndex = 0; + let buffer = ''; + + (async () => { + try { + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + buffer += decoder.decode(value, { stream: true }); + const lines = buffer.split('\n'); + buffer = lines.pop() || ''; + + for (const line of lines) { + if (line.startsWith('data: ')) { + const data = line.slice(6); + if (data === '[DONE]') { + continue; + } + try { + const event = JSON.parse(data); + if (event.type === 'chunk' && event.content) { + callbacks.onChunk(event.content, chunkIndex++); + } else if (event.type === 'done' && event.analysis_id) { + callbacks.onDone(event.analysis_id); + } else if (event.type === 'error') { + callbacks.onError(event.message || '分析出错'); + } + } catch { + // 非 JSON 行,跳过 + } + } + } + } + } catch (err) { + if (!controller.signal.aborted) { + callbacks.onError(err instanceof Error ? err.message : '连接中断'); + } + } + })(); + + return controller; +} diff --git a/apps/web/src/api/ai/chat.ts b/apps/web/src/api/ai/chat.ts new file mode 100644 index 0000000..b303e82 --- /dev/null +++ b/apps/web/src/api/ai/chat.ts @@ -0,0 +1,118 @@ +import client from '../client'; + +export interface ChatHistoryItem { + role: 'user' | 'assistant'; + content: string; +} + +export type DisplayHint = + | { + type: 'vital_card'; + indicator_type: string; + values: [string, number][]; + unit: string; + } + | { + type: 'lab_report_card'; + report_date: string; + abnormal_count: number; + } + | { + type: 'action_confirm'; + action_type: string; + summary: string; + confirm_payload: unknown; + } + | { + type: 'risk_alert'; + level: string; + message: string; + } + | { + type: 'trend_chart'; + metrics: string[]; + period: string; + summary: string; + } + | { + type: 'insight_card'; + title: string; + severity: string; + items: string[]; + } + | { + type: 'patient_profile'; + chronic_conditions: string[]; + medication_count: number; + } + | { type: 'text' }; + +export interface ChatResponse { + reply: string; + message_id: string; + iterations: number; + display_hints?: DisplayHint[]; +} + +export interface ChatSession { + id: string; + title: string | null; + patient_id: string | null; + status: string; + created_at: string; + updated_at: string; +} + +export const aiChatApi = { + sendMessage: async ( + message: string, + history: ChatHistoryItem[], + patientId?: string, + sessionId?: string + ): Promise => { + const resp = await client.post('/ai/chat', { + message, + history, + ...(patientId ? { patient_id: patientId } : {}), + ...(sessionId ? { session_id: sessionId } : {}), + }); + return resp.data.data as ChatResponse; + }, + + createSession: async ( + patientId?: string, + title?: string + ): Promise => { + const resp = await client.post('/ai/chat/sessions', { + ...(patientId ? { patient_id: patientId } : {}), + ...(title ? { title } : {}), + }); + return resp.data.data as ChatSession; + }, + + listSessions: async (): Promise => { + const resp = await client.get('/ai/chat/sessions'); + return resp.data.data as ChatSession[]; + }, + + renameSession: async ( + sessionId: string, + title: string + ): Promise => { + await client.put(`/ai/chat/sessions/${sessionId}/rename`, { title }); + }, + + closeSession: async (sessionId: string): Promise => { + await client.post(`/ai/chat/sessions/${sessionId}/close`); + }, + + getSessionMessages: async (sessionId: string): Promise> => { + const resp = await client.get(`/ai/chat/sessions/${sessionId}/messages`); + return resp.data.data; + }, +}; diff --git a/apps/web/src/api/ai/config.ts b/apps/web/src/api/ai/config.ts new file mode 100644 index 0000000..e371755 --- /dev/null +++ b/apps/web/src/api/ai/config.ts @@ -0,0 +1,45 @@ +import client from '../client'; + +export interface AiAgentConfig { + model: string; + temperature: number; + max_tokens: number; + max_iterations: number; + system_prompt: string; +} + +export interface AiAnalysisDefaults { + model: string; + temperature: number; + max_tokens: number; +} + +export interface AiProviderConfig { + provider_type: string; + enabled: boolean; + base_url: string; + api_key: string; + model: string; +} + +export interface AiConfig { + agent: AiAgentConfig; + analysis_defaults: AiAnalysisDefaults; + default_provider: string; + providers: Record; +} + +export const aiConfigApi = { + get: async () => { + const resp = await client.get('/ai/config'); + return resp.data.data as AiConfig; + }, + getDefaults: async () => { + const resp = await client.get('/ai/config/defaults'); + return resp.data.data as AiConfig; + }, + update: async (config: AiConfig) => { + const resp = await client.put('/ai/config', { config }); + return resp.data.data as AiConfig; + }, +}; diff --git a/apps/web/src/api/ai/dialysis.ts b/apps/web/src/api/ai/dialysis.ts new file mode 100644 index 0000000..70268ab --- /dev/null +++ b/apps/web/src/api/ai/dialysis.ts @@ -0,0 +1,23 @@ +import client from '../client'; + +export interface DialysisRiskRequest { + patient_id: string; + dialysis_session_id?: string; +} + +export interface DialysisRiskAssessment { + id: string; + patient_id: string; + risk_level: string; + risk_factors: string[]; + recommendations: string[]; + kdigo_stage?: string; + created_at: string; +} + +export const dialysisRiskApi = { + assess: async (data: DialysisRiskRequest) => { + const resp = await client.post('/ai/dialysis/risk-assessment', data); + return resp.data.data as DialysisRiskAssessment; + }, +}; diff --git a/apps/web/src/api/ai/knowledgeV2.ts b/apps/web/src/api/ai/knowledgeV2.ts new file mode 100644 index 0000000..3a1ba31 --- /dev/null +++ b/apps/web/src/api/ai/knowledgeV2.ts @@ -0,0 +1,188 @@ +import client from '../client'; + +// === Types === + +export interface KnowledgeBase { + id: string; + tenant_id: string; + name: string; + kb_type: string; + description: string | null; + icon: string | null; + chunk_strategy: Record; + intent_keywords: Record; + embedding_model: string | null; + is_enabled: boolean; + document_count: number; + chunk_count: number; + created_at: string; + updated_at: string; +} + +export interface KnowledgeDocument { + id: string; + tenant_id: string; + knowledge_base_id: string; + title: string; + doc_type: string; + source_type: string; + source_url: string | null; + file_name: string | null; + file_size: number | null; + file_mime_type: string | null; + content: string | null; + status: string; + chunk_count: number; + embedded_count: number; + error_message: string | null; + processing_started_at: string | null; + processing_completed_at: string | null; + created_at: string; + updated_at: string; +} + +export interface SearchHit { + chunk_id: string; + document_id: string; + chunk_index: number; + content: string; + doc_title: string; + similarity: number; + metadata: Record; +} + +export interface CreateKnowledgeBaseReq { + name: string; + kb_type: string; + description?: string; + icon?: string; + chunk_strategy?: Record; + intent_keywords?: Record; + embedding_model?: string; + is_enabled?: boolean; +} + +export interface UpdateKnowledgeBaseReq { + name?: string; + kb_type?: string; + description?: string; + icon?: string; + chunk_strategy?: Record; + intent_keywords?: Record; + embedding_model?: string; + is_enabled?: boolean; +} + +export interface CreateDocumentReq { + kb_id: string; + title: string; + doc_type?: string; + source_type?: string; + source_url?: string; + content?: string; +} + +// === API === + +export const knowledgeV2Api = { + // Knowledge Bases + listKnowledgeBases: async (params?: { + kb_type?: string; + is_enabled?: boolean; + page?: number; + page_size?: number; + }) => { + const resp = await client.get('/ai/knowledge-bases', { params }); + return resp.data.data as { + data: KnowledgeBase[]; + total: number; + page: number; + page_size: number; + }; + }, + + getKnowledgeBase: async (id: string) => { + const resp = await client.get(`/ai/knowledge-bases/${id}`); + return resp.data.data as KnowledgeBase; + }, + + createKnowledgeBase: async (data: CreateKnowledgeBaseReq) => { + const resp = await client.post('/ai/knowledge-bases', data); + return resp.data.data as { id: string }; + }, + + updateKnowledgeBase: async (id: string, data: UpdateKnowledgeBaseReq) => { + const resp = await client.put(`/ai/knowledge-bases/${id}`, data); + return resp.data.data as { id: string }; + }, + + deleteKnowledgeBase: async (id: string) => { + const resp = await client.delete(`/ai/knowledge-bases/${id}`); + return resp.data.data as { id: string }; + }, + + // Documents + listDocuments: async ( + kbId: string, + params?: { status?: string; page?: number; page_size?: number }, + ) => { + const resp = await client.get( + `/ai/knowledge-bases/${kbId}/documents`, + { params }, + ); + return resp.data.data as { + data: KnowledgeDocument[]; + total: number; + page: number; + page_size: number; + }; + }, + + getDocument: async (id: string) => { + const resp = await client.get(`/ai/documents/${id}`); + return resp.data.data as KnowledgeDocument; + }, + + createManualDocument: async (data: CreateDocumentReq) => { + const resp = await client.post('/ai/documents/manual', data); + return resp.data.data as { id: string }; + }, + + uploadDocument: async ( + kbId: string, + file: File, + title?: string, + ) => { + const formData = new FormData(); + formData.append('kb_id', kbId); + formData.append('file', file); + if (title) { + formData.append('title', title); + } + const resp = await client.post('/ai/documents/upload', formData, { + headers: { 'Content-Type': 'multipart/form-data' }, + }); + return resp.data.data as { id: string }; + }, + + deleteDocument: async (kbId: string, id: string) => { + const resp = await client.delete( + `/ai/knowledge-bases/${kbId}/documents/${id}`, + ); + return resp.data.data as { id: string }; + }, + + // Hit Test + hitTest: async (kbId: string, query: string, topK?: number) => { + const resp = await client.post('/ai/documents/hit-test', { + kb_id: kbId, + query, + top_k: topK, + }); + return resp.data.data as { + query: string; + total: number; + hits: SearchHit[]; + }; + }, +}; diff --git a/apps/web/src/api/ai/prompts.ts b/apps/web/src/api/ai/prompts.ts new file mode 100644 index 0000000..a8fcc24 --- /dev/null +++ b/apps/web/src/api/ai/prompts.ts @@ -0,0 +1,54 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +export interface PromptItem { + id: string; + name: string; + description: string; + system_prompt: string; + user_prompt_template: string; + model_config: Record; + version: number; + is_active: boolean; + category: string; + analysis_type: string; + tags: Record | null; + created_at: string; + updated_at: string; +} + +export interface CreatePromptReq { + name: string; + description?: string; + system_prompt: string; + user_prompt_template: string; + model_config: Record; + category: string; + analysis_type: string; +} + +export const promptApi = { + list: async (params?: { category?: string; analysis_type?: string; page?: number; page_size?: number }) => { + const resp = await client.get('/ai/prompts', { params }); + return resp.data.data as PaginatedResponse; + }, + create: async (data: CreatePromptReq) => { + const resp = await client.post('/ai/prompts', data); + return resp.data.data as PromptItem; + }, + activate: async (id: string) => { + const resp = await client.post(`/ai/prompts/${id}/activate`); + return resp.data.data as PromptItem; + }, + deactivate: async (id: string) => { + const resp = await client.post(`/ai/prompts/${id}/deactivate`); + return resp.data.data as PromptItem; + }, + rollback: async (id: string) => { + const resp = await client.post(`/ai/prompts/${id}/rollback`); + return resp.data.data as PromptItem; + }, + delete: async (id: string) => { + await client.delete(`/ai/prompts/${id}`); + }, +}; diff --git a/apps/web/src/api/ai/suggestions.ts b/apps/web/src/api/ai/suggestions.ts new file mode 100644 index 0000000..5e351fc --- /dev/null +++ b/apps/web/src/api/ai/suggestions.ts @@ -0,0 +1,38 @@ +import client from '../client'; + +export interface SuggestionItem { + id: string; + analysis_id: string; + suggestion_type: string; + risk_level: string; + params: Record | null; + status: string; + created_at: string; +} + +export interface ComparisonReport { + suggestion_id: string; + baseline: Record | null; + current: Record | null; + comparison_available: boolean; + message?: string; +} + +export const suggestionApi = { + list: async (params?: { analysis_id?: string; status?: string }) => { + const resp = await client.get('/ai/suggestions', { params }); + return resp.data.data as { data: SuggestionItem[]; total: number }; + }, + approve: async (id: string, action: 'approve' | 'reject') => { + const resp = await client.post(`/ai/suggestions/${id}/approve`, { action }); + return resp.data.data as { id: string; status: string }; + }, + execute: async (id: string) => { + const resp = await client.post(`/ai/suggestions/${id}/execute`); + return resp.data.data as { id: string; status: string }; + }, + getComparison: async (id: string) => { + const resp = await client.get(`/ai/suggestions/${id}/comparison`); + return resp.data.data as ComparisonReport; + }, +}; diff --git a/apps/web/src/api/ai/usage.ts b/apps/web/src/api/ai/usage.ts new file mode 100644 index 0000000..5b91f63 --- /dev/null +++ b/apps/web/src/api/ai/usage.ts @@ -0,0 +1,107 @@ +import client from '../client'; + +export interface UsageOverview { + total_count: number; +} + +export interface TypeDistribution { + analysis_type: string; + count: number; +} + +export interface ProviderInfo { + id: string; + name: string; + provider_type: string; + is_active: boolean; + model_name?: string; +} + +export interface ProviderHealth { + provider_id: string; + status: string; + latency_ms?: number; + last_checked_at?: string; +} + +export interface QuotaSummary { + provider_id: string; + quota_limit: number; + quota_used: number; + quota_remaining: number; + period: string; +} + +export interface BudgetStatus { + total_budget: number; + spent: number; + remaining: number; + period: string; +} + +export interface CostEstimate { + analysis_type: string; + estimated_cost: number; + currency: string; +} + +export interface DailyUsageRow { + date: string; + feature: string; + provider: string; + model: string; + total_calls: number; + total_input_tokens: number; + total_output_tokens: number; + total_cost_cents: number; +} + +export interface FeatureFlag { + feature: string; + is_enabled: boolean; +} + +export const usageApi = { + overview: async () => { + const resp = await client.get('/ai/usage/overview'); + return resp.data.data as UsageOverview; + }, + byType: async () => { + const resp = await client.get('/ai/usage/by-type'); + return resp.data.data as TypeDistribution[]; + }, + listProviders: async () => { + const resp = await client.get('/ai/providers'); + return resp.data.data as ProviderInfo[]; + }, + getProvidersHealth: async () => { + const resp = await client.get('/ai/providers/health'); + return resp.data.data as ProviderHealth[]; + }, + getQuotaSummary: async () => { + const resp = await client.get('/ai/quota/summary'); + return resp.data.data as QuotaSummary[]; + }, + getBudgetStatus: async () => { + const resp = await client.get('/ai/budget/status'); + return resp.data.data as BudgetStatus; + }, + getCostEstimate: async (params: { analysis_type: string }) => { + const resp = await client.get('/ai/cost/estimate', { params }); + return resp.data.data as CostEstimate; + }, + getDailyUsage: async (startDate: string, endDate: string) => { + const resp = await client.get('/ai/admin/daily-usage', { + params: { start_date: startDate, end_date: endDate }, + }); + return resp.data.data as DailyUsageRow[]; + }, + getFeatureFlags: async () => { + const resp = await client.get('/ai/admin/flags'); + return resp.data.data as FeatureFlag[]; + }, + updateFeatureFlag: async (feature: string, enabled: boolean) => { + const resp = await client.post('/ai/admin/flags', { feature, enabled }); + return resp.data.data as { feature: string; enabled: boolean }; + }, +}; diff --git a/apps/web/src/api/auditLogs.test.ts b/apps/web/src/api/auditLogs.test.ts new file mode 100644 index 0000000..1a71084 --- /dev/null +++ b/apps/web/src/api/auditLogs.test.ts @@ -0,0 +1,51 @@ +/** + * auditLogs API 契约测试 + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('./client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import * as auditLogsApi from './auditLogs' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('auditLogs API', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listAuditLogs 应调用 GET /audit-logs 并传递查询参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await auditLogsApi.listAuditLogs({ resource_type: 'user', user_id: 'u-001', page: 1, page_size: 10 }) + + expect(mockGet).toHaveBeenCalledWith('/audit-logs', { + params: expect.objectContaining({ + resource_type: 'user', + user_id: 'u-001', + page: 1, + page_size: 10, + }), + }) + }) + + it('listAuditLogs 默认应传 page=1 page_size=20', async () => { + mockGet.mockResolvedValue(fakeRes) + await auditLogsApi.listAuditLogs() + + expect(mockGet).toHaveBeenCalledWith('/audit-logs', { + params: expect.objectContaining({ page: 1, page_size: 20 }), + }) + }) +}) diff --git a/apps/web/src/api/auditLogs.ts b/apps/web/src/api/auditLogs.ts new file mode 100644 index 0000000..92be6cc --- /dev/null +++ b/apps/web/src/api/auditLogs.ts @@ -0,0 +1,31 @@ +import client from './client'; +import type { PaginatedResponse } from './types'; + +export interface AuditLogItem { + id: string; + tenant_id: string; + action: string; + resource_type: string; + resource_id: string; + user_id: string; + old_value?: string; + new_value?: string; + ip_address?: string; + user_agent?: string; + created_at: string; +} + +export interface AuditLogQuery { + resource_type?: string; + user_id?: string; + page?: number; + page_size?: number; +} + +export async function listAuditLogs(query: AuditLogQuery = {}) { + const { data } = await client.get<{ success: boolean; data: PaginatedResponse }>( + '/audit-logs', + { params: { page: query.page ?? 1, page_size: query.page_size ?? 20, ...query } }, + ); + return data.data; +} diff --git a/apps/web/src/api/auth.test.ts b/apps/web/src/api/auth.test.ts new file mode 100644 index 0000000..5a68920 --- /dev/null +++ b/apps/web/src/api/auth.test.ts @@ -0,0 +1,55 @@ +/** + * auth API 契约测试 + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('./client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import * as authApi from './auth' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('auth API', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('login 应调用 POST /auth/login 并传递用户名密码', async () => { + mockPost.mockResolvedValue(fakeRes) + await authApi.login({ username: 'admin', password: '123456' }) + + expect(mockPost).toHaveBeenCalledWith('/auth/login', { + username: 'admin', + password: '123456', + }) + }) + + it('logout 应调用 POST /auth/logout', async () => { + mockPost.mockResolvedValue(undefined) + await authApi.logout() + + expect(mockPost).toHaveBeenCalledWith('/auth/logout') + }) + + it('changePassword 应调用 POST /auth/change-password', async () => { + mockPost.mockResolvedValue(undefined) + await authApi.changePassword('oldPass', 'newPass') + + expect(mockPost).toHaveBeenCalledWith('/auth/change-password', { + current_password: 'oldPass', + new_password: 'newPass', + }) + }) +}) diff --git a/apps/web/src/api/auth.ts b/apps/web/src/api/auth.ts new file mode 100644 index 0000000..98cb7f7 --- /dev/null +++ b/apps/web/src/api/auth.ts @@ -0,0 +1,55 @@ +import client from './client'; + +export interface LoginRequest { + username: string; + password: string; +} + +export interface UserInfo { + id: string; + username: string; + email?: string; + phone?: string; + display_name?: string; + avatar_url?: string; + status: string; + roles: RoleInfo[]; + version: number; +} + +export interface RoleInfo { + id: string; + name: string; + code: string; + description?: string; + is_system: boolean; +} + +export interface LoginResponse { + access_token: string; + refresh_token: string; + expires_in: number; + user: UserInfo; +} + +export async function login(req: LoginRequest): Promise { + const { data } = await client.post<{ success: boolean; data: LoginResponse }>( + '/auth/login', + req + ); + return data.data; +} + +export async function logout(): Promise { + await client.post('/auth/logout'); +} + +export async function changePassword( + currentPassword: string, + newPassword: string +): Promise { + await client.post('/auth/change-password', { + current_password: currentPassword, + new_password: newPassword, + }); +} diff --git a/apps/web/src/api/client.ts b/apps/web/src/api/client.ts new file mode 100644 index 0000000..c568930 --- /dev/null +++ b/apps/web/src/api/client.ts @@ -0,0 +1,261 @@ +import axios from "axios"; +import { message as antMessage } from "antd"; + +// 请求缓存:短时间内相同请求复用结果 +interface CacheEntry { + data: unknown; + timestamp: number; +} + +const requestCache = new Map(); +const CACHE_TTL = 5000; // 5 秒缓存 + +function getCacheKey(config: { + url?: string; + params?: unknown; + method?: string; +}): string { + return `${config.method || "get"}:${config.url || ""}:${JSON.stringify(config.params || {})}`; +} + +const defaultAdapter = axios.getAdapter(axios.defaults.adapter); + +const client = axios.create({ + baseURL: "/api/v1", + timeout: 10000, + headers: { "Content-Type": "application/json" }, + adapter: (config) => { + // GET 请求检查缓存 + if (config.method === "get" && config.url) { + const key = getCacheKey(config); + const entry = requestCache.get(key); + if (entry && Date.now() - entry.timestamp < CACHE_TTL) { + return Promise.resolve({ + data: entry.data, + status: 200, + statusText: "OK (cached)", + headers: new axios.AxiosHeaders(), + config, + }); + } + } + return defaultAdapter(config); + }, +}); + +// Decode JWT payload without external library +function decodeJwtPayload( + token: string, +): { exp?: number; sub?: string } | null { + try { + const parts = token.split("."); + if (parts.length !== 3) return null; + const payload = JSON.parse( + atob(parts[1].replace(/-/g, "+").replace(/_/g, "/")), + ); + return payload; + } catch { + return null; + } +} + +// Check if token is expired or about to expire (within 30s buffer) +function isTokenExpiringSoon(token: string): boolean { + const payload = decodeJwtPayload(token); + if (!payload?.exp) return true; + return Date.now() / 1000 > payload.exp - 30; +} + +// Request interceptor: attach access token + proactive refresh +client.interceptors.request.use(async (config) => { + const token = localStorage.getItem("access_token"); + if (token) { + // If token is about to expire, proactively refresh before sending the request + if (isTokenExpiringSoon(token)) { + const refreshToken = localStorage.getItem("refresh_token"); + if (refreshToken && !isRefreshing) { + isRefreshing = true; + try { + const { data } = await axios.post("/api/v1/auth/refresh", { + refresh_token: refreshToken, + }); + const newAccess = data.data.access_token; + const newRefresh = data.data.refresh_token; + + // 验证新 token 的用户身份一致 + const currentUserSub = decodeJwtPayload(token)?.sub; + const newTokenSub = decodeJwtPayload(newAccess)?.sub; + if (currentUserSub && newTokenSub && currentUserSub !== newTokenSub) { + localStorage.removeItem("access_token"); + localStorage.removeItem("refresh_token"); + localStorage.removeItem("user"); + window.location.hash = "/login"; + return Promise.reject(new Error("身份验证失败,请重新登录")); + } + + localStorage.setItem("access_token", newAccess); + localStorage.setItem("refresh_token", newRefresh); + processQueue(null, newAccess); + config.headers.Authorization = `Bearer ${newAccess}`; + return config; + } catch { + processQueue(new Error("refresh failed"), null); + // Continue with old token, let 401 handler deal with it + } finally { + isRefreshing = false; + } + } + } + config.headers.Authorization = `Bearer ${token}`; + } + + return config; +}); + +// 响应拦截器:缓存 GET 响应 + 自动刷新 token +client.interceptors.response.use( + (response) => { + // 缓存 GET 响应 + if (response.config.method === "get" && response.config.url) { + const key = getCacheKey(response.config); + requestCache.set(key, { data: response.data, timestamp: Date.now() }); + } + return response; + }, + async (error) => { + const originalRequest = error.config; + if ( + error.response?.status === 401 && + !originalRequest._retry && + !originalRequest.url?.includes("/auth/login") + ) { + if (isRefreshing) { + return new Promise((resolve, reject) => { + failedQueue.push({ resolve, reject }); + }).then((token) => { + originalRequest.headers.Authorization = `Bearer ${token}`; + return client(originalRequest); + }); + } + + originalRequest._retry = true; + isRefreshing = true; + + try { + const refreshToken = localStorage.getItem("refresh_token"); + if (!refreshToken) throw new Error("No refresh token"); + + const { data } = await axios.post("/api/v1/auth/refresh", { + refresh_token: refreshToken, + }); + + const newAccessToken = data.data.access_token; + const newRefreshToken = data.data.refresh_token; + + // 验证新 token 的用户身份与当前用户一致,防止并发场景下身份切换 + const currentToken = localStorage.getItem("access_token"); + const currentUserSub = currentToken + ? decodeJwtPayload(currentToken)?.sub + : null; + const newTokenSub = decodeJwtPayload(newAccessToken)?.sub; + if (currentUserSub && newTokenSub && currentUserSub !== newTokenSub) { + // 身份不一致,强制登出 + localStorage.removeItem("access_token"); + localStorage.removeItem("refresh_token"); + localStorage.removeItem("user"); + window.location.hash = "/login"; + return Promise.reject(new Error("身份验证失败,请重新登录")); + } + + localStorage.setItem("access_token", newAccessToken); + localStorage.setItem("refresh_token", newRefreshToken); + + processQueue(null, newAccessToken); + + originalRequest.headers.Authorization = `Bearer ${newAccessToken}`; + return client(originalRequest); + } catch (refreshError) { + processQueue(refreshError, null); + localStorage.removeItem("access_token"); + localStorage.removeItem("refresh_token"); + window.location.hash = "/login"; + return Promise.reject(refreshError); + } finally { + isRefreshing = false; + } + } + + return Promise.reject(error); + }, +); + +// 全局错误提示(仅对未被组件处理的错误显示) +let globalErrorTimer: ReturnType | null = null; +function showGlobalError(msg: string) { + // 防止短时间内弹出大量相同提示 + if (globalErrorTimer) return; + antMessage.error(msg, 3); + globalErrorTimer = setTimeout(() => { + globalErrorTimer = null; + }, 3000); +} + +// 全局错误拦截 — 在响应拦截器之后、组件 catch 之前执行 +// 组件可通过 axios config 中设置 skipGlobalError: true 来抑制全局提示 +declare module "axios" { + interface AxiosRequestConfig { + skipGlobalError?: boolean; + } + interface InternalAxiosRequestConfig { + skipGlobalError?: boolean; + } +} + +client.interceptors.response.use( + (response) => response, + (error) => { + if (error.config?.skipGlobalError) { + return Promise.reject(error); + } + if (!error.response) { + showGlobalError("网络连接异常,请检查网络"); + } else if (error.response.status === 403) { + // 403 通常是权限不足,不全局提示 — 组件层通过 AuthButton 已隐藏操作入口 + } else if (error.response.status === 404) { + // 404 通常由组件自行处理(如跳转),不全局提示 + } else if (error.response.status >= 500) { + showGlobalError("服务器异常,请稍后重试"); + } + return Promise.reject(error); + }, +); + +let isRefreshing = false; +let failedQueue: Array<{ + resolve: (token: string) => void; + reject: (error: unknown) => void; +}> = []; + +function processQueue(error: unknown, token: string | null) { + failedQueue.forEach(({ resolve, reject }) => { + if (token) resolve(token); + else reject(error); + }); + failedQueue = []; +} + +// 清除缓存(登录/登出时调用) +export function clearApiCache() { + requestCache.clear(); +} + +// 通用错误处理:提取后端错误消息并展示 +export function handleApiError(err: unknown, fallback = "操作失败"): string { + const msg = + (err as { response?: { data?: { message?: string } } })?.response?.data + ?.message || fallback; + antMessage.error(msg); + return msg; +} + +export default client; diff --git a/apps/web/src/api/config-modules.test.ts b/apps/web/src/api/config-modules.test.ts new file mode 100644 index 0000000..f051af7 --- /dev/null +++ b/apps/web/src/api/config-modules.test.ts @@ -0,0 +1,197 @@ +/** + * config-modules API 契约测试(menus + settings + languages + numberingRules + themes) + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('./client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import * as menusApi from './menus' +import * as settingsApi from './settings' +import * as languagesApi from './languages' +import * as numberingApi from './numberingRules' +import * as themesApi from './themes' + +beforeEach(() => { + vi.clearAllMocks() +}) + +// ============================================================ +// menus +// ============================================================ +describe('menus API', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('getMenus 应调用 GET /config/menus', async () => { + mockGet.mockResolvedValue(fakeRes) + await menusApi.getMenus() + + expect(mockGet).toHaveBeenCalledWith('/config/menus') + }) + + it('getMenusForUser 应调用 GET /menus/user', async () => { + mockGet.mockResolvedValue(fakeRes) + await menusApi.getMenusForUser() + + expect(mockGet).toHaveBeenCalledWith('/menus/user') + }) + + it('batchSaveMenus 应调用 PUT /config/menus 并传递 menus 数组', async () => { + mockPut.mockResolvedValue(undefined) + const menus = [{ title: '仪表盘', path: '/dashboard' }] + await menusApi.batchSaveMenus(menus) + + expect(mockPut).toHaveBeenCalledWith('/config/menus', { menus }) + }) + + it('createMenu 应调用 POST /config/menus', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { title: '新菜单', path: '/new', sort_order: 10 } + await menusApi.createMenu(req) + + expect(mockPost).toHaveBeenCalledWith('/config/menus', req) + }) + + it('deleteMenu 应调用 DELETE /config/menus/:id 并在 body 传递 version', async () => { + mockDelete.mockResolvedValue(undefined) + await menusApi.deleteMenu('menu-001', 3) + + expect(mockDelete).toHaveBeenCalledWith('/config/menus/menu-001', { data: { version: 3 } }) + }) +}) + +// ============================================================ +// settings +// ============================================================ +describe('settings API', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('getSetting 应调用 GET /config/settings/:key 并传递 scope 参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await settingsApi.getSetting('site.name', 'global', 'org-001') + + expect(mockGet).toHaveBeenCalledWith('/config/settings/site.name', { + params: { scope: 'global', scope_id: 'org-001' }, + }) + }) + + it('updateSetting 应调用 PUT /config/settings/:key', async () => { + mockPut.mockResolvedValue(fakeRes) + await settingsApi.updateSetting('site.name', '新名称', 1) + + expect(mockPut).toHaveBeenCalledWith('/config/settings/site.name', { + setting_value: '新名称', + version: 1, + }) + }) + + it('deleteSetting 应调用 DELETE /config/settings/:key', async () => { + mockDelete.mockResolvedValue(undefined) + await settingsApi.deleteSetting('site.name', 2) + + expect(mockDelete).toHaveBeenCalledWith('/config/settings/site.name', { data: { version: 2 } }) + }) +}) + +// ============================================================ +// languages +// ============================================================ +describe('languages API', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listLanguages 应调用 GET /config/languages', async () => { + mockGet.mockResolvedValue(fakeRes) + await languagesApi.listLanguages() + + expect(mockGet).toHaveBeenCalledWith('/config/languages') + }) + + it('updateLanguage 应调用 PUT /config/languages/:code', async () => { + mockPut.mockResolvedValue(fakeRes) + await languagesApi.updateLanguage('zh-CN', { is_active: true, name: '简体中文' }) + + expect(mockPut).toHaveBeenCalledWith('/config/languages/zh-CN', { + is_active: true, + name: '简体中文', + }) + }) +}) + +// ============================================================ +// numberingRules +// ============================================================ +describe('numberingRules API', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listNumberingRules 应调用 GET /config/numbering-rules', async () => { + mockGet.mockResolvedValue(fakeRes) + await numberingApi.listNumberingRules(1, 10) + + expect(mockGet).toHaveBeenCalledWith('/config/numbering-rules', { + params: { page: 1, page_size: 10 }, + }) + }) + + it('createNumberingRule 应调用 POST /config/numbering-rules', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { name: '患者编号', code: 'patient', prefix: 'P', seq_length: 6 } + await numberingApi.createNumberingRule(req) + + expect(mockPost).toHaveBeenCalledWith('/config/numbering-rules', req) + }) + + it('updateNumberingRule 应调用 PUT /config/numbering-rules/:id', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { prefix: 'HMS', version: 1 } + await numberingApi.updateNumberingRule('nr-001', req) + + expect(mockPut).toHaveBeenCalledWith('/config/numbering-rules/nr-001', req) + }) + + it('generateNumber 应调用 POST /config/numbering-rules/:id/generate', async () => { + mockPost.mockResolvedValue(fakeRes) + await numberingApi.generateNumber('nr-001') + + expect(mockPost).toHaveBeenCalledWith('/config/numbering-rules/nr-001/generate') + }) + + it('deleteNumberingRule 应调用 DELETE /config/numbering-rules/:id', async () => { + mockDelete.mockResolvedValue(undefined) + await numberingApi.deleteNumberingRule('nr-001', 1) + + expect(mockDelete).toHaveBeenCalledWith('/config/numbering-rules/nr-001', { data: { version: 1 } }) + }) +}) + +// ============================================================ +// themes +// ============================================================ +describe('themes API', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('getTheme 应调用 GET /config/themes', async () => { + mockGet.mockResolvedValue(fakeRes) + await themesApi.getTheme() + + expect(mockGet).toHaveBeenCalledWith('/config/themes') + }) + + it('updateTheme 应调用 PUT /config/themes', async () => { + mockPut.mockResolvedValue(fakeRes) + const theme = { primary_color: '#1890ff', brand_name: 'HMS' } + await themesApi.updateTheme(theme) + + expect(mockPut).toHaveBeenCalledWith('/config/themes', theme) + }) +}) diff --git a/apps/web/src/api/copilot.ts b/apps/web/src/api/copilot.ts new file mode 100644 index 0000000..535e48e --- /dev/null +++ b/apps/web/src/api/copilot.ts @@ -0,0 +1,66 @@ +import client from './client'; +import type { PaginatedResponse } from './types'; + +// --- Types --- + +export type InsightType = 'risk_score' | 'anomaly' | 'follow_up_hint' | 'consult_hint'; +export type InsightSource = 'rule' | 'llm' | 'hybrid'; +export type RiskLevel = 'low' | 'medium' | 'high' | 'critical'; + +export interface MatchedRule { + rule_id: string; + name: string; + score: number; + severity: string; + suggestion?: string; +} + +export interface RiskScore { + score: number; + level: RiskLevel; + matched_rules: MatchedRule[]; +} + +export interface CopilotInsight { + id: string; + patient_id: string; + insight_type: InsightType; + source: InsightSource; + severity: 'info' | 'warning' | 'critical'; + title: string; + content: Record; + rule_matches?: MatchedRule[]; + llm_supplement?: string; + created_at: string; +} + +// --- API Functions --- + +export async function getPatientRisk(patientId: string) { + const { data } = await client.get<{ success: boolean; data: RiskScore }>(`/copilot/patients/${patientId}/risk`); + return data.data; +} + +export async function listInsights(params: { + patient_id?: string; + insight_type?: string; + severity?: string; + page?: number; + page_size?: number; +}) { + const { data } = await client.get<{ success: boolean; data: PaginatedResponse }>('/copilot/insights', { params }); + return data.data; +} + +export function dismissInsight(id: string) { + return client.post(`/copilot/insights/${id}/dismiss`); +} + +export function listAlerts(params?: { severity?: string; page?: number; page_size?: number }) { + return listInsights({ insight_type: 'anomaly', ...params }); +} + +export async function listRules(params?: { page?: number; page_size?: number }) { + const { data } = await client.get<{ success: boolean; data: PaginatedResponse> }>('/copilot/rules', { params }); + return data.data; +} diff --git a/apps/web/src/api/dictionaries.test.ts b/apps/web/src/api/dictionaries.test.ts new file mode 100644 index 0000000..a0000a6 --- /dev/null +++ b/apps/web/src/api/dictionaries.test.ts @@ -0,0 +1,96 @@ +/** + * dictionaries API 契约测试 + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('./client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import * as dictApi from './dictionaries' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('dictionaries API', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listDictionaries 应调用 GET /config/dictionaries 并传递分页参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await dictApi.listDictionaries(1, 10) + + expect(mockGet).toHaveBeenCalledWith('/config/dictionaries', { + params: { page: 1, page_size: 10 }, + }) + }) + + it('createDictionary 应调用 POST /config/dictionaries', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { name: '性别', code: 'gender', description: '性别字典' } + await dictApi.createDictionary(req) + + expect(mockPost).toHaveBeenCalledWith('/config/dictionaries', req) + }) + + it('updateDictionary 应调用 PUT /config/dictionaries/:id', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { name: '性别(更新)', version: 1 } + await dictApi.updateDictionary('dict-001', req) + + expect(mockPut).toHaveBeenCalledWith('/config/dictionaries/dict-001', req) + }) + + it('deleteDictionary 应调用 DELETE /config/dictionaries/:id 并在 body 传递 version', async () => { + mockDelete.mockResolvedValue(undefined) + await dictApi.deleteDictionary('dict-001', 2) + + expect(mockDelete).toHaveBeenCalledWith('/config/dictionaries/dict-001', { + data: { version: 2 }, + }) + }) + + it('listItemsByCode 应调用 GET /config/dictionaries/items 并传递 code 参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await dictApi.listItemsByCode('gender') + + expect(mockGet).toHaveBeenCalledWith('/config/dictionaries/items', { + params: { code: 'gender' }, + }) + }) + + it('createDictionaryItem 应调用 POST /config/dictionaries/:id/items', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { label: '男', value: 'male', sort_order: 1 } + await dictApi.createDictionaryItem('dict-001', req) + + expect(mockPost).toHaveBeenCalledWith('/config/dictionaries/dict-001/items', req) + }) + + it('updateDictionaryItem 应调用 PUT /config/dictionaries/:dictId/items/:itemId', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { label: '女', version: 1 } + await dictApi.updateDictionaryItem('dict-001', 'item-001', req) + + expect(mockPut).toHaveBeenCalledWith('/config/dictionaries/dict-001/items/item-001', req) + }) + + it('deleteDictionaryItem 应调用 DELETE /config/dictionaries/:dictId/items/:itemId', async () => { + mockDelete.mockResolvedValue(undefined) + await dictApi.deleteDictionaryItem('dict-001', 'item-001', 1) + + expect(mockDelete).toHaveBeenCalledWith('/config/dictionaries/dict-001/items/item-001', { + data: { version: 1 }, + }) + }) +}) diff --git a/apps/web/src/api/dictionaries.ts b/apps/web/src/api/dictionaries.ts new file mode 100644 index 0000000..b44d443 --- /dev/null +++ b/apps/web/src/api/dictionaries.ts @@ -0,0 +1,111 @@ +import client from './client'; +import type { PaginatedResponse } from './types'; + +export interface DictionaryItemInfo { + id: string; + dictionary_id: string; + label: string; + value: string; + sort_order: number; + color?: string; + version: number; +} + +export interface DictionaryInfo { + id: string; + name: string; + code: string; + description?: string; + items: DictionaryItemInfo[]; + version: number; +} + +export interface CreateDictionaryRequest { + name: string; + code: string; + description?: string; +} + +export interface UpdateDictionaryRequest { + name?: string; + description?: string; + version: number; +} + +export async function listDictionaries(page = 1, pageSize = 20) { + const { data } = await client.get<{ success: boolean; data: PaginatedResponse }>( + '/config/dictionaries', + { params: { page, page_size: pageSize } }, + ); + return data.data; +} + +export async function createDictionary(req: CreateDictionaryRequest) { + const { data } = await client.post<{ success: boolean; data: DictionaryInfo }>( + '/config/dictionaries', + req, + ); + return data.data; +} + +export async function updateDictionary(id: string, req: UpdateDictionaryRequest) { + const { data } = await client.put<{ success: boolean; data: DictionaryInfo }>( + `/config/dictionaries/${id}`, + req, + ); + return data.data; +} + +export async function deleteDictionary(id: string, version: number) { + await client.delete(`/config/dictionaries/${id}`, { data: { version } }); +} + +export async function listItemsByCode(code: string) { + const { data } = await client.get<{ success: boolean; data: DictionaryItemInfo[] }>( + '/config/dictionaries/items', + { params: { code } }, + ); + return data.data; +} + +export interface CreateDictionaryItemRequest { + label: string; + value: string; + sort_order?: number; + color?: string; +} + +export interface UpdateDictionaryItemRequest { + label?: string; + value?: string; + sort_order?: number; + color?: string; + version: number; +} + +export async function createDictionaryItem( + dictionaryId: string, + req: CreateDictionaryItemRequest, +) { + const { data } = await client.post<{ success: boolean; data: DictionaryItemInfo }>( + `/config/dictionaries/${dictionaryId}/items`, + req, + ); + return data.data; +} + +export async function updateDictionaryItem( + dictionaryId: string, + itemId: string, + req: UpdateDictionaryItemRequest, +) { + const { data } = await client.put<{ success: boolean; data: DictionaryItemInfo }>( + `/config/dictionaries/${dictionaryId}/items/${itemId}`, + req, + ); + return data.data; +} + +export async function deleteDictionaryItem(dictionaryId: string, itemId: string, version: number) { + await client.delete(`/config/dictionaries/${dictionaryId}/items/${itemId}`, { data: { version } }); +} diff --git a/apps/web/src/api/health/actionInbox.ts b/apps/web/src/api/health/actionInbox.ts new file mode 100644 index 0000000..951ba63 --- /dev/null +++ b/apps/web/src/api/health/actionInbox.ts @@ -0,0 +1,128 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +export type ActionType = 'ai_suggestion' | 'alert' | 'followup' | 'data_anomaly'; +export type ActionPriority = 'urgent' | 'high' | 'medium' | 'low'; +export type ActionStatus = 'pending' | 'in_progress' | 'completed' | 'dismissed'; + +export interface ActionItem { + id: string; + action_type: ActionType; + priority: ActionPriority; + status: ActionStatus; + title: string; + summary: string; + patient_id: string; + patient_name: string; + source_ref: string; + created_at: string; + updated_at: string; +} + +export interface ThreadEvent { + step: string; + label: string; + status: ActionStatus; + detail?: string; + timestamp?: string; + operator?: string; + link_to?: string; +} + +export interface ActionDefinition { + key: string; + label: string; + variant: 'primary' | 'danger' | 'default'; + api_endpoint?: string; +} + +export interface ThreadResponse { + action_item: ActionItem; + thread: ThreadEvent[]; + available_actions: ActionDefinition[]; +} + +export interface WorkbenchStats { + total_pending: number; + ai_suggestion_pending: number; + urgent_alerts: number; + followup_due: number; + completion_rate: number | null; +} + +export interface NursePatientSummary { + patient_id: string; + patient_name: string; + pending_actions: number; + highest_priority: ActionPriority; +} + +export interface TeamMemberOverview { + user_id: string; + name: string; + title: string; + pending_count: number; + completed_count: number; + overdue_count: number; + completion_rate: number; +} + +export interface TeamOverview { + members: TeamMemberOverview[]; + risk_distribution: { + high: number; + medium: number; + low: number; + }; + total_pending: number; + total_completed: number; +} + +export const actionInboxApi = { + list: async (params?: { + status?: string; + type?: string; + page?: number; + page_size?: number; + assigned_to_me?: boolean; + patient_id?: string; + }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/action-inbox', { params }); + return data.data; + }, + + getThread: async (sourceRef: string) => { + const { data } = await client.get<{ + success: boolean; + data: ThreadResponse; + }>(`/health/action-inbox/${encodeURIComponent(sourceRef)}/thread`); + return data.data; + }, + + stats: async (params?: { assigned_to_me?: boolean }) => { + const { data } = await client.get<{ + success: boolean; + data: WorkbenchStats; + }>('/health/action-inbox/stats', { params }); + return data.data; + }, + + myPatients: async () => { + const { data } = await client.get<{ + success: boolean; + data: NursePatientSummary[]; + }>('/health/action-inbox/my-patients'); + return data.data; + }, + + team: async () => { + const { data } = await client.get<{ + success: boolean; + data: TeamOverview; + }>('/health/action-inbox/team'); + return data.data; + }, +}; diff --git a/apps/web/src/api/health/alerts.test.ts b/apps/web/src/api/health/alerts.test.ts new file mode 100644 index 0000000..716bbfc --- /dev/null +++ b/apps/web/src/api/health/alerts.test.ts @@ -0,0 +1,100 @@ +/** + * alerts API 契约测试 + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('../client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import { alertApi, alertRuleApi } from './alerts' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('alertApi', () => { + const fakeRes = { data: { data: {} } } + + it('list 应调用 GET /health/alerts 并传递查询参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await alertApi.list({ patient_id: 'p-001', status: 'active', page: 1, page_size: 20 }) + + expect(mockGet).toHaveBeenCalledWith('/health/alerts', { + params: { patient_id: 'p-001', status: 'active', page: 1, page_size: 20 }, + }) + }) + + it('acknowledge 应调用 PUT /health/alerts/:id/acknowledge 并传递 version', async () => { + mockPut.mockResolvedValue(fakeRes) + await alertApi.acknowledge('a-001', 2) + + expect(mockPut).toHaveBeenCalledWith('/health/alerts/a-001/acknowledge', { version: 2 }) + }) + + it('dismiss 应调用 PUT /health/alerts/:id/dismiss', async () => { + mockPut.mockResolvedValue(fakeRes) + await alertApi.dismiss('a-001', 1) + + expect(mockPut).toHaveBeenCalledWith('/health/alerts/a-001/dismiss', { version: 1 }) + }) + + it('resolve 应调用 PUT /health/alerts/:id/resolve', async () => { + mockPut.mockResolvedValue(fakeRes) + await alertApi.resolve('a-001', 3) + + expect(mockPut).toHaveBeenCalledWith('/health/alerts/a-001/resolve', { version: 3 }) + }) +}) + +describe('alertRuleApi', () => { + const fakeRes = { data: { data: {} } } + + it('list 应调用 GET /health/alert-rules 并传递查询参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await alertRuleApi.list({ device_type: 'blood_pressure', page: 1, page_size: 10 }) + + expect(mockGet).toHaveBeenCalledWith('/health/alert-rules', { + params: { device_type: 'blood_pressure', page: 1, page_size: 10 }, + }) + }) + + it('create 应调用 POST /health/alert-rules 并传递请求体', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { + name: '血压偏高告警', + device_type: 'blood_pressure', + condition_type: 'threshold', + condition_params: { field: 'systolic', operator: '>', value: 140 }, + severity: 'high', + } + await alertRuleApi.create(req) + + expect(mockPost).toHaveBeenCalledWith('/health/alert-rules', req) + }) + + it('update 应调用 PUT /health/alert-rules/:id', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { severity: 'critical', version: 1 } + await alertRuleApi.update('rule-001', req) + + expect(mockPut).toHaveBeenCalledWith('/health/alert-rules/rule-001', req) + }) + + it('deactivate 应调用 PUT /health/alert-rules/:id/deactivate', async () => { + mockPut.mockResolvedValue(fakeRes) + await alertRuleApi.deactivate('rule-001', 2) + + expect(mockPut).toHaveBeenCalledWith('/health/alert-rules/rule-001/deactivate', { version: 2 }) + }) +}) diff --git a/apps/web/src/api/health/alerts.ts b/apps/web/src/api/health/alerts.ts new file mode 100644 index 0000000..f1f6f1c --- /dev/null +++ b/apps/web/src/api/health/alerts.ts @@ -0,0 +1,118 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --- Types --- +export interface Alert { + id: string; + patient_id: string; + patient_name?: string; + rule_id: string; + severity: string; + title: string; + detail?: Record; + status: string; + acknowledged_by?: string; + acknowledged_by_name?: string; + acknowledged_at?: string; + resolved_at?: string; + created_at: string; + version: number; +} + +export interface AlertRule { + id: string; + name: string; + description?: string; + device_type: string; + condition_type: string; + condition_params: Record; + severity: string; + is_active: boolean; + apply_tags?: Record; + notify_roles: unknown[]; + cooldown_minutes: number; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateAlertRuleReq { + name: string; + description?: string; + device_type: string; + condition_type: string; + condition_params: Record; + severity?: string; + apply_tags?: Record; + notify_roles?: unknown[]; + cooldown_minutes?: number; +} + +export interface UpdateAlertRuleReq { + name?: string; + description?: string; + condition_params?: Record; + severity?: string; + apply_tags?: Record; + notify_roles?: unknown[]; + cooldown_minutes?: number; + version: number; +} + +// --- API --- +export const alertApi = { + list: (params?: { patient_id?: string; doctor_id?: string; status?: string; page?: number; page_size?: number }) => + client.get('/health/alerts', { params }).then((r) => r.data.data as PaginatedResponse), + + acknowledge: (id: string, version: number) => + client.put(`/health/alerts/${id}/acknowledge`, { version }).then((r) => r.data.data as Alert), + + dismiss: (id: string, version: number) => + client.put(`/health/alerts/${id}/dismiss`, { version }).then((r) => r.data.data as Alert), + + resolve: (id: string, version: number) => + client.put(`/health/alerts/${id}/resolve`, { version }).then((r) => r.data.data as Alert), +}; + +export const alertRuleApi = { + list: (params?: { device_type?: string; page?: number; page_size?: number }) => + client.get('/health/alert-rules', { params }).then((r) => r.data.data as PaginatedResponse), + + create: (data: CreateAlertRuleReq) => + client.post('/health/alert-rules', data).then((r) => r.data.data as AlertRule), + + update: (id: string, data: UpdateAlertRuleReq) => + client.put(`/health/alert-rules/${id}`, data).then((r) => r.data.data as AlertRule), + + deactivate: (id: string, version: number) => + client.put(`/health/alert-rules/${id}/deactivate`, { version }).then((r) => r.data.data as AlertRule), +}; + +// --- Critical Alerts API --- + +export interface CriticalAlert { + id: string; + patient_id: string; + patient_name?: string; + alert_type: string; + severity: string; + title: string; + detail?: Record; + status: string; + acknowledged_by?: string; + acknowledged_at?: string; + notes?: string; + created_at: string; + version: number; +} + +export const criticalAlertApi = { + list: (params?: { page?: number; page_size?: number }) => + client.get('/health/critical-alerts', { params }).then((r) => r.data.data as PaginatedResponse), + + get: (id: string) => + client.get(`/health/critical-alerts/${id}`).then((r) => r.data.data as CriticalAlert), + + acknowledge: (id: string, req: { notes?: string }) => + client.post(`/health/critical-alerts/${id}/acknowledge`, req).then((r) => r.data), +}; diff --git a/apps/web/src/api/health/api.test.ts b/apps/web/src/api/health/api.test.ts new file mode 100644 index 0000000..9ab5d84 --- /dev/null +++ b/apps/web/src/api/health/api.test.ts @@ -0,0 +1,172 @@ +/** + * 健康模块新增 API 函数的契约测试 + * + * 验证 dialysisApi / pointsAdminApi / healthDataApi 的日常监测与报告审核函数 + * 是否调用了正确的 HTTP 方法、URL 路径和参数。 + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +// --- Mock axios client --- +// 三个被测文件都 import client from '../client',相对路径一致 +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('../client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +// 在 mock 生效后导入被测模块 +import { dialysisApi } from './dialysis' +import { pointsAdminApi } from './points' +import { healthDataApi } from './healthData' + +beforeEach(() => { + vi.clearAllMocks() +}) + +// ============================================================ +// dialysisApi +// ============================================================ +describe('dialysisApi', () => { + const fakeResponse = { data: { success: true, data: {} } } + + it('listRecords 应调用 GET /health/patients/:id/dialysis-records 并传递分页参数', async () => { + mockGet.mockResolvedValue(fakeResponse) + await dialysisApi.listRecords('p-001', { page: 2, page_size: 20 }) + + expect(mockGet).toHaveBeenCalledTimes(1) + expect(mockGet).toHaveBeenCalledWith( + '/health/patients/p-001/dialysis-records', + { params: { page: 2, page_size: 20 } }, + ) + }) + + it('getRecord 应调用 GET /health/dialysis-records/:id', async () => { + mockGet.mockResolvedValue(fakeResponse) + await dialysisApi.getRecord('rec-123') + + expect(mockGet).toHaveBeenCalledWith('/health/dialysis-records/rec-123') + }) + + it('createRecord 应调用 POST /health/dialysis-records 并传递请求体', async () => { + mockPost.mockResolvedValue(fakeResponse) + const req = { patient_id: 'p-001', dialysis_date: '2026-04-30', dialysis_type: 'hemodialysis' } + await dialysisApi.createRecord(req) + + expect(mockPost).toHaveBeenCalledWith('/health/dialysis-records', req) + }) + + it('updateRecord 应调用 PUT /health/dialysis-records/:id 并传递请求体', async () => { + mockPut.mockResolvedValue(fakeResponse) + const req = { dry_weight: 65.0, version: 3 } + await dialysisApi.updateRecord('rec-123', req) + + expect(mockPut).toHaveBeenCalledWith('/health/dialysis-records/rec-123', req) + }) + + it('deleteRecord 应调用 DELETE /health/dialysis-records/:id 并在 body 中传递 version', async () => { + mockDelete.mockResolvedValue(undefined) + await dialysisApi.deleteRecord('rec-123', 3) + + expect(mockDelete).toHaveBeenCalledWith('/health/dialysis-records/rec-123', { + data: { version: 3 }, + }) + }) + + it('reviewRecord 应调用 PUT /health/dialysis-records/:id/review', async () => { + mockPut.mockResolvedValue(fakeResponse) + const req = { version: 2, doctor_notes: '指标正常' } + await dialysisApi.reviewRecord('rec-456', req) + + expect(mockPut).toHaveBeenCalledWith('/health/dialysis-records/rec-456/review', req) + }) +}) + +// ============================================================ +// pointsAdminApi +// ============================================================ +describe('pointsAdminApi', () => { + const fakeResponse = { data: { success: true, data: {} } } + + it('getPatientAccount 应调用 GET /health/admin/points/patients/:id/account', async () => { + mockGet.mockResolvedValue(fakeResponse) + await pointsAdminApi.getPatientAccount('p-001') + + expect(mockGet).toHaveBeenCalledWith('/health/admin/points/patients/p-001/account') + }) + + it('listPatientTransactions 应调用 GET 并传递分页参数', async () => { + mockGet.mockResolvedValue(fakeResponse) + await pointsAdminApi.listPatientTransactions('p-001', { page: 1, page_size: 10 }) + + expect(mockGet).toHaveBeenCalledWith( + '/health/admin/points/patients/p-001/transactions', + { params: { page: 1, page_size: 10 } }, + ) + }) +}) + +// ============================================================ +// healthDataApi — 日常监测 + 报告审核 +// ============================================================ +describe('healthDataApi 日常监测', () => { + const fakeResponse = { data: { success: true, data: {} } } + + it('listDailyMonitoring 应调用 GET /health/patients/:id/daily-monitoring 并传递分页参数', async () => { + mockGet.mockResolvedValue(fakeResponse) + await healthDataApi.listDailyMonitoring('p-001', { page: 1, page_size: 15 }) + + expect(mockGet).toHaveBeenCalledWith( + '/health/patients/p-001/daily-monitoring', + { params: { page: 1, page_size: 15 } }, + ) + }) + + it('createDailyMonitoring 应调用 POST /health/daily-monitoring 并传递请求体', async () => { + mockPost.mockResolvedValue(fakeResponse) + const req = { + patient_id: 'p-001', + record_date: '2026-04-30', + weight: 70.5, + blood_sugar: 5.2, + } + await healthDataApi.createDailyMonitoring(req) + + expect(mockPost).toHaveBeenCalledWith('/health/daily-monitoring', req) + }) + + it('updateDailyMonitoring 应调用 PUT /health/daily-monitoring/:id 并传递请求体', async () => { + mockPut.mockResolvedValue(fakeResponse) + const req = { weight: 71.0, version: 1 } + await healthDataApi.updateDailyMonitoring('dm-123', req) + + expect(mockPut).toHaveBeenCalledWith('/health/daily-monitoring/dm-123', req) + }) + + it('deleteDailyMonitoring 应调用 DELETE /health/daily-monitoring/:id 并在 body 中传递 version', async () => { + mockDelete.mockResolvedValue(undefined) + await healthDataApi.deleteDailyMonitoring('dm-123', 2) + + expect(mockDelete).toHaveBeenCalledWith('/health/daily-monitoring/dm-123', { + data: { version: 2 }, + }) + }) + + it('reviewLabReport 应调用 PUT /health/patients/:pid/lab-reports/:rid/review', async () => { + mockPut.mockResolvedValue(fakeResponse) + const req = { version: 1, doctor_notes: '指标略有异常,建议复查' } + await healthDataApi.reviewLabReport('p-001', 'lr-456', req) + + expect(mockPut).toHaveBeenCalledWith( + '/health/patients/p-001/lab-reports/lr-456/review', + req, + ) + }) +}) diff --git a/apps/web/src/api/health/appointments.test.ts b/apps/web/src/api/health/appointments.test.ts new file mode 100644 index 0000000..49fc27d --- /dev/null +++ b/apps/web/src/api/health/appointments.test.ts @@ -0,0 +1,106 @@ +/** + * appointments API 契约测试 + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('../client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import { appointmentApi } from './appointments' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('appointmentApi', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('list 应调用 GET /health/appointments 并传递查询参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await appointmentApi.list({ page: 1, page_size: 20, status: 'confirmed', doctor_id: 'd-001' }) + + expect(mockGet).toHaveBeenCalledWith('/health/appointments', { + params: { page: 1, page_size: 20, status: 'confirmed', doctor_id: 'd-001' }, + }) + }) + + it('get 应调用 GET /health/appointments/:id', async () => { + mockGet.mockResolvedValue(fakeRes) + await appointmentApi.get('appt-001') + + expect(mockGet).toHaveBeenCalledWith('/health/appointments/appt-001') + }) + + it('create 应调用 POST /health/appointments 并传递请求体', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { + patient_id: 'p-001', + doctor_id: 'd-001', + appointment_date: '2026-05-10', + start_time: '09:00', + end_time: '09:30', + } + await appointmentApi.create(req) + + expect(mockPost).toHaveBeenCalledWith('/health/appointments', req) + }) + + it('updateStatus 应调用 PUT /health/appointments/:id/status', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { status: 'cancelled', cancel_reason: '时间冲突', version: 2 } + await appointmentApi.updateStatus('appt-001', req) + + expect(mockPut).toHaveBeenCalledWith('/health/appointments/appt-001/status', req) + }) + + it('listSchedules 应调用 GET /health/doctor-schedules 并传递查询参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await appointmentApi.listSchedules({ doctor_id: 'd-001', date: '2026-05-10' }) + + expect(mockGet).toHaveBeenCalledWith('/health/doctor-schedules', { + params: { doctor_id: 'd-001', date: '2026-05-10' }, + }) + }) + + it('createSchedule 应调用 POST /health/doctor-schedules', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { + doctor_id: 'd-001', + schedule_date: '2026-05-10', + start_time: '08:00', + end_time: '12:00', + max_appointments: 10, + } + await appointmentApi.createSchedule(req) + + expect(mockPost).toHaveBeenCalledWith('/health/doctor-schedules', req) + }) + + it('updateSchedule 应调用 PUT /health/doctor-schedules/:id', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { max_appointments: 15, version: 1 } + await appointmentApi.updateSchedule('sch-001', req) + + expect(mockPut).toHaveBeenCalledWith('/health/doctor-schedules/sch-001', req) + }) + + it('calendar 应调用 GET /health/doctor-schedules/calendar', async () => { + mockGet.mockResolvedValue(fakeRes) + await appointmentApi.calendar({ start_date: '2026-05-01', end_date: '2026-05-31', doctor_id: 'd-001' }) + + expect(mockGet).toHaveBeenCalledWith('/health/doctor-schedules/calendar', { + params: { start_date: '2026-05-01', end_date: '2026-05-31', doctor_id: 'd-001' }, + }) + }) +}) diff --git a/apps/web/src/api/health/appointments.ts b/apps/web/src/api/health/appointments.ts new file mode 100644 index 0000000..5488412 --- /dev/null +++ b/apps/web/src/api/health/appointments.ts @@ -0,0 +1,164 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --- Types --- +export interface Appointment { + id: string; + patient_id: string; + doctor_id?: string; + appointment_type: string; + appointment_date: string; + start_time: string; + end_time: string; + status: string; + cancel_reason?: string; + notes?: string; + patient_name?: string; + doctor_name?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateAppointmentReq { + patient_id: string; + doctor_id?: string; + appointment_type?: string; + appointment_date: string; + start_time: string; + end_time: string; + notes?: string; +} + +export interface UpdateAppointmentStatusReq { + status: string; + cancel_reason?: string; +} + +export interface Schedule { + id: string; + doctor_id: string; + schedule_date: string; + period_type: string; + start_time: string; + end_time: string; + max_appointments: number; + current_appointments: number; + status: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateScheduleReq { + doctor_id: string; + schedule_date: string; + period_type?: string; + start_time: string; + end_time: string; + max_appointments: number; +} + +export interface UpdateScheduleReq { + start_time?: string; + end_time?: string; + max_appointments?: number; + status?: string; +} + +export interface CalendarDay { + date: string; + schedules: Schedule[]; +} + +// --- API --- +export const appointmentApi = { + list: async (params: { + page?: number; + page_size?: number; + status?: string; + patient_id?: string; + doctor_id?: string; + date?: string; + search?: string; + appointment_type?: string; + }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/appointments', { params }); + return data.data; + }, + + get: async (id: string) => { + const { data } = await client.get<{ + success: boolean; + data: Appointment; + }>(`/health/appointments/${id}`); + return data.data; + }, + + create: async (req: CreateAppointmentReq) => { + const { data } = await client.post<{ + success: boolean; + data: Appointment; + }>('/health/appointments', req); + return data.data; + }, + + updateStatus: async ( + id: string, + req: UpdateAppointmentStatusReq & { version: number }, + ) => { + const { data } = await client.put<{ + success: boolean; + data: Appointment; + }>(`/health/appointments/${id}/status`, req); + return data.data; + }, + + // Schedules + listSchedules: async (params: { + page?: number; + page_size?: number; + doctor_id?: string; + date?: string; + }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/doctor-schedules', { params }); + return data.data; + }, + + createSchedule: async (req: CreateScheduleReq) => { + const { data } = await client.post<{ + success: boolean; + data: Schedule; + }>('/health/doctor-schedules', req); + return data.data; + }, + + updateSchedule: async ( + id: string, + req: UpdateScheduleReq & { version: number }, + ) => { + const { data } = await client.put<{ + success: boolean; + data: Schedule; + }>(`/health/doctor-schedules/${id}`, req); + return data.data; + }, + + calendar: async (params: { + start_date: string; + end_date: string; + doctor_id?: string; + }) => { + const { data } = await client.get<{ + success: boolean; + data: CalendarDay[]; + }>('/health/doctor-schedules/calendar', { params }); + return data.data; + }, +}; diff --git a/apps/web/src/api/health/articles.test.ts b/apps/web/src/api/health/articles.test.ts new file mode 100644 index 0000000..3cb9315 --- /dev/null +++ b/apps/web/src/api/health/articles.test.ts @@ -0,0 +1,173 @@ +/** + * articles API 契约测试 + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('../client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import { articleApi, articleCategoryApi, articleTagApi } from './articles' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('articleApi', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('list 应调用 GET /health/articles 并传递查询参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await articleApi.list({ page: 1, page_size: 10, status: 'published', category_id: 'cat-001' }) + + expect(mockGet).toHaveBeenCalledWith('/health/articles', { + params: { page: 1, page_size: 10, status: 'published', category_id: 'cat-001' }, + }) + }) + + it('get 应调用 GET /health/articles/:id', async () => { + mockGet.mockResolvedValue(fakeRes) + await articleApi.get('art-001') + + expect(mockGet).toHaveBeenCalledWith('/health/articles/art-001') + }) + + it('create 应调用 POST /health/articles', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { title: '健康饮食指南', content: '正文内容', content_type: 'markdown' as const } + await articleApi.create(req) + + expect(mockPost).toHaveBeenCalledWith('/health/articles', req) + }) + + it('update 应调用 PUT /health/articles/:id 并传递请求体', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { title: '健康饮食指南(修订)', version: 1 } + await articleApi.update('art-001', req) + + expect(mockPut).toHaveBeenCalledWith('/health/articles/art-001', req) + }) + + it('delete 应调用 DELETE /health/articles/:id', async () => { + mockDelete.mockResolvedValue({ data: { success: true, data: null } }) + await articleApi.delete('art-001', 1) + + expect(mockDelete).toHaveBeenCalledWith('/health/articles/art-001') + }) + + it('submit 应调用 POST /health/articles/:id/submit 并传递 version', async () => { + mockPost.mockResolvedValue(fakeRes) + await articleApi.submit('art-001', 2) + + expect(mockPost).toHaveBeenCalledWith('/health/articles/art-001/submit', { version: 2 }) + }) + + it('approve 应调用 POST /health/articles/:id/approve', async () => { + mockPost.mockResolvedValue(fakeRes) + await articleApi.approve('art-001', 2) + + expect(mockPost).toHaveBeenCalledWith('/health/articles/art-001/approve', { version: 2 }) + }) + + it('reject 应调用 POST /health/articles/:id/reject 并传递 review_note', async () => { + mockPost.mockResolvedValue(fakeRes) + await articleApi.reject('art-001', 2, '内容需要修改') + + expect(mockPost).toHaveBeenCalledWith('/health/articles/art-001/reject', { + version: 2, + review_note: '内容需要修改', + }) + }) + + it('unpublish 应调用 POST /health/articles/:id/unpublish', async () => { + mockPost.mockResolvedValue(fakeRes) + await articleApi.unpublish('art-001', 3) + + expect(mockPost).toHaveBeenCalledWith('/health/articles/art-001/unpublish', { version: 3 }) + }) + + it('view 应调用 POST /health/articles/:id/view', async () => { + mockPost.mockResolvedValue(fakeRes) + await articleApi.view('art-001') + + expect(mockPost).toHaveBeenCalledWith('/health/articles/art-001/view') + }) +}) + +describe('articleCategoryApi', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('list 应调用 GET /health/article-categories', async () => { + mockGet.mockResolvedValue(fakeRes) + await articleCategoryApi.list() + + expect(mockGet).toHaveBeenCalledWith('/health/article-categories') + }) + + it('create 应调用 POST /health/article-categories', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { name: '营养健康', sort_order: 1 } + await articleCategoryApi.create(req) + + expect(mockPost).toHaveBeenCalledWith('/health/article-categories', req) + }) + + it('update 应调用 PUT /health/article-categories/:id', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { name: '营养健康(更新)' } + await articleCategoryApi.update('cat-001', req) + + expect(mockPut).toHaveBeenCalledWith('/health/article-categories/cat-001', req) + }) + + it('delete 应调用 DELETE /health/article-categories/:id', async () => { + mockDelete.mockResolvedValue({ data: { success: true, data: null } }) + await articleCategoryApi.delete('cat-001') + + expect(mockDelete).toHaveBeenCalledWith('/health/article-categories/cat-001') + }) +}) + +describe('articleTagApi', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('list 应调用 GET /health/article-tags', async () => { + mockGet.mockResolvedValue(fakeRes) + await articleTagApi.list() + + expect(mockGet).toHaveBeenCalledWith('/health/article-tags') + }) + + it('create 应调用 POST /health/article-tags', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { name: '高血压', color: '#ff0000' } + await articleTagApi.create(req) + + expect(mockPost).toHaveBeenCalledWith('/health/article-tags', req) + }) + + it('update 应调用 PUT /health/article-tags/:id', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { name: '高血压管理', version: 1 } + await articleTagApi.update('tag-001', req) + + expect(mockPut).toHaveBeenCalledWith('/health/article-tags/tag-001', req) + }) + + it('delete 应调用 DELETE /health/article-tags/:id 并在 body 传递 version', async () => { + mockDelete.mockResolvedValue({ data: { success: true, data: null } }) + await articleTagApi.delete('tag-001', 2) + + expect(mockDelete).toHaveBeenCalledWith('/health/article-tags/tag-001', { data: { version: 2 } }) + }) +}) diff --git a/apps/web/src/api/health/articles.ts b/apps/web/src/api/health/articles.ts new file mode 100644 index 0000000..7dd070c --- /dev/null +++ b/apps/web/src/api/health/articles.ts @@ -0,0 +1,283 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --- Article Types --- + +export type ArticleStatus = 'draft' | 'pending_review' | 'published' | 'rejected'; +export type ArticleContentType = 'rich_text' | 'markdown'; + +export interface ArticleListItem { + id: string; + title: string; + summary?: string; + cover_image?: string; + content_type: ArticleContentType; + status: ArticleStatus; + slug?: string; + category_id?: string; + category_name?: string; + tags?: ArticleTagItem[]; + author?: string; + reviewed_by?: string; + reviewed_at?: string; + review_note?: string; + view_count: number; + sort_order: number; + is_public: boolean; + published_at?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface Article extends ArticleListItem { + content?: string; +} + +export interface CreateArticleReq { + title: string; + summary?: string; + content?: string; + content_type?: ArticleContentType; + cover_image?: string; + slug?: string; + category_id?: string; + tag_ids?: string[]; + sort_order?: number; + is_public?: boolean; +} + +export interface UpdateArticleReq { + title?: string; + summary?: string; + content?: string; + content_type?: ArticleContentType; + cover_image?: string; + slug?: string; + category_id?: string; + tag_ids?: string[]; + sort_order?: number; + is_public?: boolean; + version: number; +} + +export interface ArticleListParams { + page?: number; + page_size?: number; + status?: ArticleStatus; + category_id?: string; + tag_id?: string; + keyword?: string; +} + +// --- Category Types --- + +export interface ArticleCategory { + id: string; + name: string; + slug?: string; + parent_id?: string; + parent_name?: string; + sort_order: number; + description?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateCategoryReq { + name: string; + slug?: string; + parent_id?: string; + sort_order?: number; + description?: string; +} + +export interface UpdateCategoryReq { + name?: string; + slug?: string; + parent_id?: string; + sort_order?: number; + description?: string; +} + +// --- Tag Types --- + +export interface ArticleTagItem { + id: string; + name: string; + slug?: string; + color?: string; + created_at: string; + version?: number; +} + +export interface CreateTagReq { + name: string; + slug?: string; + color?: string; +} + +// --- Article API --- + +export const articleApi = { + list: async (params: ArticleListParams) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/articles', { params }); + return data.data; + }, + + get: async (id: string) => { + const { data } = await client.get<{ + success: boolean; + data: Article; + }>(`/health/articles/${id}`); + return data.data; + }, + + create: async (req: CreateArticleReq) => { + const { data } = await client.post<{ + success: boolean; + data: Article; + }>('/health/articles', req); + return data.data; + }, + + update: async (id: string, req: UpdateArticleReq) => { + const { data } = await client.put<{ + success: boolean; + data: Article; + }>(`/health/articles/${id}`, req); + return data.data; + }, + + delete: async (id: string, version: number) => { + const { data } = await client.delete<{ + success: boolean; + data: null; + }>(`/health/articles/${id}`, { data: { version } }); + return data.data; + }, + + submit: async (id: string, version: number) => { + const { data } = await client.post<{ + success: boolean; + data: Article; + }>(`/health/articles/${id}/submit`, { version }); + return data.data; + }, + + approve: async (id: string, version: number) => { + const { data } = await client.post<{ + success: boolean; + data: Article; + }>(`/health/articles/${id}/approve`, { version }); + return data.data; + }, + + reject: async (id: string, version: number, review_note: string) => { + const { data } = await client.post<{ + success: boolean; + data: Article; + }>(`/health/articles/${id}/reject`, { version, review_note }); + return data.data; + }, + + unpublish: async (id: string, version: number) => { + const { data } = await client.post<{ + success: boolean; + data: Article; + }>(`/health/articles/${id}/unpublish`, { version }); + return data.data; + }, + + view: async (id: string) => { + const { data } = await client.post<{ + success: boolean; + data: Article; + }>(`/health/articles/${id}/view`); + return data.data; + }, + + listRevisions: async (id: string, params?: { page?: number; page_size?: number }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse>; + }>(`/health/articles/${id}/revisions`, { params }); + return data.data; + }, +}; + +// --- Category API --- + +export const articleCategoryApi = { + list: async () => { + const { data } = await client.get<{ + success: boolean; + data: ArticleCategory[]; + }>('/health/article-categories'); + return data.data; + }, + + create: async (req: CreateCategoryReq) => { + const { data } = await client.post<{ + success: boolean; + data: ArticleCategory; + }>('/health/article-categories', req); + return data.data; + }, + + update: async (id: string, req: UpdateCategoryReq) => { + const { data } = await client.put<{ + success: boolean; + data: ArticleCategory; + }>(`/health/article-categories/${id}`, req); + return data.data; + }, + + delete: async (id: string, version: number) => { + const { data } = await client.delete<{ + success: boolean; + data: null; + }>(`/health/article-categories/${id}`, { data: { version } }); + return data.data; + }, +}; + +// --- Tag API --- + +export const articleTagApi = { + list: async () => { + const { data } = await client.get<{ + success: boolean; + data: ArticleTagItem[]; + }>('/health/article-tags'); + return data.data; + }, + + create: async (req: CreateTagReq) => { + const { data } = await client.post<{ + success: boolean; + data: ArticleTagItem; + }>('/health/article-tags', req); + return data.data; + }, + + update: async (id: string, req: { name: string; version: number }) => { + const { data } = await client.put<{ + success: boolean; + data: ArticleTagItem; + }>(`/health/article-tags/${id}`, req); + return data.data; + }, + + delete: async (id: string, version: number) => { + const { data } = await client.delete<{ + success: boolean; + data: null; + }>(`/health/article-tags/${id}`, { data: { version } }); + return data.data; + }, +}; diff --git a/apps/web/src/api/health/banners.ts b/apps/web/src/api/health/banners.ts new file mode 100644 index 0000000..937e2ec --- /dev/null +++ b/apps/web/src/api/health/banners.ts @@ -0,0 +1,116 @@ +import client from '../client'; + +// --------------------------------------------------------------------------- +// 轮播图类型 +// --------------------------------------------------------------------------- + +export interface BannerItem { + id: string; + tenant_id: string; + media_item_id: string; + title?: string; + subtitle?: string; + link_type?: string; + link_target?: string; + sort_order: number; + status: string; + start_time?: string; + end_time?: string; + image_url?: string; + thumbnail_url?: string; + media_deleted: boolean; + created_at: string; + updated_at: string; + created_by?: string; + updated_by?: string; + version: number; +} + +export interface CreateBannerReq { + media_item_id: string; + title?: string; + subtitle?: string; + link_type?: string; + link_target?: string; + sort_order?: number; + status?: string; + start_time?: string; + end_time?: string; +} + +export interface UpdateBannerReq { + media_item_id?: string; + title?: string; + subtitle?: string; + link_type?: string; + link_target?: string; + sort_order?: number; + status?: string; + start_time?: string; + end_time?: string; + version: number; +} + +export interface SortBannerReq { + items: Array<{ id: string; sort_order: number }>; +} + +// --------------------------------------------------------------------------- +// 轮播图 API +// --------------------------------------------------------------------------- + +export const bannerApi = { + /** 获取轮播图列表(可按状态筛选) */ + list: async (status?: string) => { + const { data } = await client.get<{ + success: boolean; + data: BannerItem[]; + }>('/health/banners', { params: status ? { status } : undefined }); + return data.data; + }, + + /** 获取单个轮播图 */ + get: async (id: string) => { + const { data } = await client.get<{ + success: boolean; + data: BannerItem; + }>(`/health/banners/${id}`); + return data.data; + }, + + /** 创建轮播图 */ + create: async (req: CreateBannerReq) => { + const { data } = await client.post<{ + success: boolean; + data: BannerItem; + }>('/health/banners', req); + return data.data; + }, + + /** 更新轮播图 */ + update: async (id: string, req: UpdateBannerReq) => { + const { data } = await client.put<{ + success: boolean; + data: BannerItem; + }>(`/health/banners/${id}`, req); + return data.data; + }, + + /** 删除轮播图 */ + delete: async (id: string, version: number) => { + const { data } = await client.delete<{ + success: boolean; + data: null; + }>(`/health/banners/${id}`, { data: { version } }); + return data.data; + }, + + /** 轮播图排序 */ + sort: async (req: SortBannerReq) => { + const { data } = await client.put<{ + success: boolean; + data: null; + }>('/health/banners/sort', req); + return data.data; + }, +}; diff --git a/apps/web/src/api/health/bleGateways.ts b/apps/web/src/api/health/bleGateways.ts new file mode 100644 index 0000000..4c1c8b5 --- /dev/null +++ b/apps/web/src/api/health/bleGateways.ts @@ -0,0 +1,170 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --- Types --- + +export interface BleGateway { + id: string; + tenant_id: string; + gateway_id: string; + name: string; + status: string; + firmware_version?: string; + ip_address?: string; + last_heartbeat_at?: string; + metadata?: Record; + created_at: string; + updated_at: string; + version: number; + api_key?: string; + patient_count?: number; +} + +export interface GatewayBinding { + id: string; + tenant_id: string; + gateway_id: string; + patient_id: string; + peripheral_mac?: string; + device_type?: string; + status: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateBleGatewayReq { + gateway_id: string; + name: string; + firmware_version?: string; + metadata?: Record; +} + +export interface UpdateBleGatewayReq { + name?: string; + status?: string; + firmware_version?: string; + metadata?: Record; +} + +export interface ListBleGatewaysParams { + page?: number; + page_size?: number; + status?: string; +} + +export interface CreateBindingReq { + patient_id: string; + peripheral_mac?: string; + device_type?: string; +} + +export interface BatchBindReq { + bindings: CreateBindingReq[]; +} + +// --- Constants --- + +export const GATEWAY_STATUS_OPTIONS = [ + { label: '在线', value: 'online' }, + { label: '离线', value: 'offline' }, + { label: '未激活', value: 'inactive' }, + { label: '已禁用', value: 'disabled' }, +]; + +export const GATEWAY_STATUS_COLOR: Record = { + online: 'green', + offline: 'red', + inactive: 'default', + disabled: 'error', +}; + +export const GATEWAY_STATUS_LABEL: Record = Object.fromEntries( + GATEWAY_STATUS_OPTIONS.map((o) => [o.value, o.label]), +); + +export const BINDING_STATUS_COLOR: Record = { + active: 'green', + inactive: 'default', + unbound: 'error', +}; + +// --- API --- + +export const bleGatewayApi = { + // --- Gateways --- + + list: async (params?: ListBleGatewaysParams) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/ble-gateways', { params }); + return data.data; + }, + + get: async (gatewayId: string) => { + const { data } = await client.get<{ + success: boolean; + data: BleGateway; + }>(`/health/ble-gateways/${gatewayId}`); + return data.data; + }, + + create: async (req: CreateBleGatewayReq) => { + const { data } = await client.post<{ + success: boolean; + data: BleGateway; + }>('/health/ble-gateways', req); + return data.data; + }, + + update: async (gatewayId: string, req: UpdateBleGatewayReq & { version: number }) => { + const { data } = await client.put<{ + success: boolean; + data: BleGateway; + }>(`/health/ble-gateways/${gatewayId}`, req); + return data.data; + }, + + delete: async (gatewayId: string, version: number) => { + await client.delete(`/health/ble-gateways/${gatewayId}`, { data: { version } }); + }, + + regenerateKey: async (gatewayId: string) => { + const { data } = await client.post<{ + success: boolean; + data: BleGateway; + }>(`/health/ble-gateways/${gatewayId}/regenerate-key`); + return data.data; + }, + + // --- Bindings --- + + listBindings: async (gatewayId: string, params?: { page?: number; page_size?: number }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>(`/health/ble-gateways/${gatewayId}/bindings`, { params }); + return data.data; + }, + + bindPatient: async (gatewayId: string, req: CreateBindingReq) => { + const { data } = await client.post<{ + success: boolean; + data: GatewayBinding; + }>(`/health/ble-gateways/${gatewayId}/bindings`, req); + return data.data; + }, + + batchBind: async (gatewayId: string, req: BatchBindReq) => { + const { data } = await client.post<{ + success: boolean; + data: GatewayBinding[]; + }>(`/health/ble-gateways/${gatewayId}/bindings/batch`, req); + return data.data; + }, + + unbindPatient: async (gatewayId: string, bindingId: string, version: number) => { + await client.delete(`/health/ble-gateways/${gatewayId}/bindings/${bindingId}`, { data: { version } }); + }, +}; diff --git a/apps/web/src/api/health/carePlans.ts b/apps/web/src/api/health/carePlans.ts new file mode 100644 index 0000000..6a8b2c5 --- /dev/null +++ b/apps/web/src/api/health/carePlans.ts @@ -0,0 +1,245 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --- Types --- + +export interface CarePlan { + id: string; + patient_id: string; + plan_type: string; + status: string; + title: string; + goals?: Record; + start_date?: string; + end_date?: string; + notes?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CarePlanItem { + id: string; + plan_id: string; + item_type: string; + title: string; + description?: string; + status: string; + schedule?: string; + sort_order?: number; + created_at: string; + updated_at: string; + version: number; +} + +export interface CarePlanOutcome { + id: string; + plan_id: string; + item_id?: string; + metric: string; + baseline_value: string; + target_value: string; + current_value?: string; + measured_at?: string; + notes?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateCarePlanReq { + patient_id: string; + plan_type: string; + title: string; + goals?: Record; + start_date?: string; + end_date?: string; + notes?: string; +} + +export interface UpdateCarePlanReq { + plan_type?: string; + title?: string; + status?: string; + goals?: Record; + start_date?: string; + end_date?: string; + notes?: string; +} + +export interface CreateCarePlanItemReq { + item_type: string; + title: string; + description?: string; + schedule?: string; + sort_order?: number; +} + +export interface UpdateCarePlanItemReq { + item_type?: string; + title?: string; + description?: string; + status?: string; + schedule?: string; + sort_order?: number; +} + +export interface CreateCarePlanOutcomeReq { + item_id?: string; + metric: string; + baseline_value: string; + target_value: string; + current_value?: string; + measured_at?: string; + notes?: string; +} + +export interface UpdateCarePlanOutcomeReq { + item_id?: string; + metric?: string; + baseline_value?: string; + target_value?: string; + current_value?: string; + measured_at?: string; + notes?: string; +} + +export interface ListCarePlansParams { + page?: number; + page_size?: number; + patient_id?: string; + plan_type?: string; + status?: string; +} + +// --- Constants --- + +export const PLAN_TYPE_OPTIONS = [ + { label: '血液透析', value: 'hemodialysis' }, + { label: '腹膜透析', value: 'peritoneal' }, + { label: '慢性病管理', value: 'chronic_disease' }, + { label: '康复计划', value: 'rehabilitation' }, +]; + +export const PLAN_STATUS_OPTIONS = [ + { label: '草稿', value: 'draft' }, + { label: '进行中', value: 'active' }, + { label: '已完成', value: 'completed' }, + { label: '已取消', value: 'cancelled' }, +]; + +export const ITEM_TYPE_OPTIONS = [ + { label: '药物干预', value: 'medication' }, + { label: '饮食管理', value: 'diet' }, + { label: '运动计划', value: 'exercise' }, + { label: '监测项目', value: 'monitoring' }, + { label: '教育指导', value: 'education' }, + { label: '其他', value: 'other' }, +]; + +export const PLAN_STATUS_COLOR: Record = { + draft: 'default', + active: 'processing', + completed: 'success', + cancelled: 'error', +}; + +// --- API --- + +export const carePlanApi = { + list: async (params: ListCarePlansParams) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/care-plans', { params }); + return data.data; + }, + + get: async (id: string) => { + const { data } = await client.get<{ + success: boolean; + data: CarePlan; + }>(`/health/care-plans/${id}`); + return data.data; + }, + + create: async (req: CreateCarePlanReq) => { + const { data } = await client.post<{ + success: boolean; + data: CarePlan; + }>('/health/care-plans', req); + return data.data; + }, + + update: async (id: string, req: UpdateCarePlanReq & { version: number }) => { + const { data } = await client.put<{ + success: boolean; + data: CarePlan; + }>(`/health/care-plans/${id}`, req); + return data.data; + }, + + delete: async (id: string, version: number) => { + await client.delete(`/health/care-plans/${id}`, { data: { version } }); + }, + + // --- Items --- + + listItems: async (planId: string, params?: { page?: number; page_size?: number }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>(`/health/care-plans/${planId}/items`, { params }); + return data.data; + }, + + createItem: async (planId: string, req: CreateCarePlanItemReq) => { + const { data } = await client.post<{ + success: boolean; + data: CarePlanItem; + }>(`/health/care-plans/${planId}/items`, req); + return data.data; + }, + + updateItem: async (planId: string, itemId: string, req: UpdateCarePlanItemReq & { version: number }) => { + const { data } = await client.put<{ + success: boolean; + data: CarePlanItem; + }>(`/health/care-plans/${planId}/items/${itemId}`, req); + return data.data; + }, + + deleteItem: async (planId: string, itemId: string, version: number) => { + await client.delete(`/health/care-plans/${planId}/items/${itemId}`, { data: { version } }); + }, + + // --- Outcomes --- + + listOutcomes: async (planId: string, params?: { page?: number; page_size?: number }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>(`/health/care-plans/${planId}/outcomes`, { params }); + return data.data; + }, + + createOutcome: async (planId: string, req: CreateCarePlanOutcomeReq) => { + const { data } = await client.post<{ + success: boolean; + data: CarePlanOutcome; + }>(`/health/care-plans/${planId}/outcomes`, req); + return data.data; + }, + + updateOutcome: async (planId: string, outcomeId: string, req: UpdateCarePlanOutcomeReq & { version: number }) => { + const { data } = await client.put<{ + success: boolean; + data: CarePlanOutcome; + }>(`/health/care-plans/${planId}/outcomes/${outcomeId}`, req); + return data.data; + }, + + deleteOutcome: async (planId: string, outcomeId: string, version: number) => { + await client.delete(`/health/care-plans/${planId}/outcomes/${outcomeId}`, { data: { version } }); + }, +}; diff --git a/apps/web/src/api/health/consents.ts b/apps/web/src/api/health/consents.ts new file mode 100644 index 0000000..94ae960 --- /dev/null +++ b/apps/web/src/api/health/consents.ts @@ -0,0 +1,92 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --- Types --- + +export interface Consent { + id: string; + patient_id: string; + consent_type: string; + consent_scope: string; + status: string; + granted_at?: string; + revoked_at?: string; + expiry_date?: string; + consent_method?: string; + witness_name?: string; + notes?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateConsentReq { + patient_id: string; + consent_type: string; + consent_scope: string; + expiry_date?: string; + consent_method?: string; + witness_name?: string; + notes?: string; +} + +export interface RevokeConsentReq { + notes?: string; + version: number; +} + +// --- Constants --- + +export const CONSENT_TYPE_OPTIONS = [ + { label: '治疗同意', value: 'treatment' }, + { label: '数据共享', value: 'data_sharing' }, + { label: '隐私政策', value: 'privacy' }, + { label: '研究参与', value: 'research' }, +]; + +export const CONSENT_SCOPE_OPTIONS = [ + { label: '全部', value: 'all' }, + { label: '健康数据', value: 'health_data' }, + { label: '基本信息', value: 'basic_info' }, + { label: '体检报告', value: 'examination' }, +]; + +export const CONSENT_STATUS_COLOR: Record = { + active: 'green', + revoked: 'red', + expired: 'default', +}; + +export const CONSENT_STATUS_LABEL: Record = { + active: '生效中', + revoked: '已撤销', + expired: '已过期', +}; + +// --- API --- + +export const consentApi = { + list: async (patientId: string, params?: { page?: number; page_size?: number }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>(`/health/patients/${patientId}/consents`, { params }); + return data.data; + }, + + grant: async (req: CreateConsentReq) => { + const { data } = await client.post<{ + success: boolean; + data: Consent; + }>('/health/consents', req); + return data.data; + }, + + revoke: async (consentId: string, req: RevokeConsentReq) => { + const { data } = await client.put<{ + success: boolean; + data: Consent; + }>(`/health/consents/${consentId}/revoke`, req); + return data.data; + }, +}; diff --git a/apps/web/src/api/health/consultations.test.ts b/apps/web/src/api/health/consultations.test.ts new file mode 100644 index 0000000..275f53b --- /dev/null +++ b/apps/web/src/api/health/consultations.test.ts @@ -0,0 +1,98 @@ +/** + * consultations API 契约测试 + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('../client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import { consultationApi } from './consultations' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('consultationApi', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listSessions 应调用 GET /health/consultation-sessions 并传递查询参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await consultationApi.listSessions({ page: 1, page_size: 20, status: 'active', patient_id: 'p-001' }) + + expect(mockGet).toHaveBeenCalledWith('/health/consultation-sessions', { + params: { page: 1, page_size: 20, status: 'active', patient_id: 'p-001' }, + }) + }) + + it('createSession 应调用 POST /health/consultation-sessions', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { patient_id: 'p-001', doctor_id: 'd-001', consultation_type: 'online' } + await consultationApi.createSession(req) + + expect(mockPost).toHaveBeenCalledWith('/health/consultation-sessions', req) + }) + + it('getSession 应调用 GET /health/consultation-sessions/:id', async () => { + mockGet.mockResolvedValue(fakeRes) + await consultationApi.getSession('sess-001') + + expect(mockGet).toHaveBeenCalledWith('/health/consultation-sessions/sess-001') + }) + + it('closeSession 应调用 PUT /health/consultation-sessions/:id/close', async () => { + mockPut.mockResolvedValue(fakeRes) + await consultationApi.closeSession('sess-001', { version: 1 }) + + expect(mockPut).toHaveBeenCalledWith('/health/consultation-sessions/sess-001/close', { version: 1 }) + }) + + it('listMessages 应调用 GET /health/consultation-sessions/:id/messages 并传递分页参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await consultationApi.listMessages('sess-001', { page: 2, page_size: 50, after_id: 'msg-100' }) + + expect(mockGet).toHaveBeenCalledWith('/health/consultation-sessions/sess-001/messages', { + params: { page: 2, page_size: 50, after_id: 'msg-100' }, + }) + }) + + it('createMessage 应调用 POST /health/consultation-messages', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { session_id: 'sess-001', content_type: 'text', content: '你好' } + await consultationApi.createMessage(req) + + expect(mockPost).toHaveBeenCalledWith('/health/consultation-messages', req) + }) + + it('createFollowUpFromSession 应调用 POST /health/consultation-sessions/:id/follow-up', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { follow_up_type: 'phone', planned_date: '2026-06-01' } + await consultationApi.createFollowUpFromSession('sess-001', req) + + expect(mockPost).toHaveBeenCalledWith('/health/consultation-sessions/sess-001/follow-up', req) + }) + + it('triggerAiAnalysisFromSession 应调用 POST /health/consultation-sessions/:id/ai-analysis', async () => { + mockPost.mockResolvedValue(fakeRes) + await consultationApi.triggerAiAnalysisFromSession('sess-001') + + expect(mockPost).toHaveBeenCalledWith('/health/consultation-sessions/sess-001/ai-analysis', {}) + }) + + it('triggerAiAnalysisFromSession 传入 analysis_type 时应携带参数', async () => { + mockPost.mockResolvedValue(fakeRes) + await consultationApi.triggerAiAnalysisFromSession('sess-001', { analysis_type: 'trend' }) + + expect(mockPost).toHaveBeenCalledWith('/health/consultation-sessions/sess-001/ai-analysis', { analysis_type: 'trend' }) + }) +}) diff --git a/apps/web/src/api/health/consultations.ts b/apps/web/src/api/health/consultations.ts new file mode 100644 index 0000000..2bfc355 --- /dev/null +++ b/apps/web/src/api/health/consultations.ts @@ -0,0 +1,185 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --- Types --- +export interface Session { + id: string; + patient_id: string; + doctor_id?: string; + patient_name?: string; + doctor_name?: string; + consultation_type: string; + status: string; + last_message_at?: string; + unread_count_patient: number; + unread_count_doctor: number; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateSessionReq { + patient_id: string; + doctor_id?: string; + consultation_type?: string; +} + +export interface Message { + id: string; + session_id: string; + sender_id: string; + sender_role: string; + content_type: string; + content: string; + is_read: boolean; + created_at: string; +} + +export interface CreateMessageReq { + session_id: string; + content_type?: string; + content: string; +} + +// --- 咨询联动请求类型 --- +export interface CreateFollowUpFromConsultationReq { + follow_up_type: string; + planned_date: string; + assigned_to?: string; + content_template?: string; +} + +export interface FollowUpFromConsultationResp { + task_id: string; + session_id: string; + patient_id: string; +} + +export interface TriggerAiAnalysisReq { + analysis_type?: string; +} + +export interface AiAnalysisTriggeredResp { + session_id: string; + patient_id: string; + analysis_type: string; +} + +// --- API --- +export const consultationApi = { + listSessions: async (params: { + page?: number; + page_size?: number; + status?: string; + patient_id?: string; + doctor_id?: string; + }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/consultation-sessions', { params }); + return data.data; + }, + + createSession: async (req: CreateSessionReq) => { + const { data } = await client.post<{ + success: boolean; + data: Session; + }>('/health/consultation-sessions', req); + return data.data; + }, + + getSession: async (id: string) => { + const { data } = await client.get<{ + success: boolean; + data: Session; + }>(`/health/consultation-sessions/${id}`); + return data.data; + }, + + closeSession: async ( + id: string, + req: { version: number }, + ) => { + const { data } = await client.put<{ + success: boolean; + data: Session; + }>(`/health/consultation-sessions/${id}/close`, req); + return data.data; + }, + + listMessages: async ( + sessionId: string, + params: { page?: number; page_size?: number; after_id?: string }, + ) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>(`/health/consultation-sessions/${sessionId}/messages`, { params }); + return data.data; + }, + + createMessage: async (req: CreateMessageReq) => { + const { data } = await client.post<{ + success: boolean; + data: Message; + }>('/health/consultation-messages', req); + return data.data; + }, + + pollMessages: async ( + sessionId: string, + afterId?: string, + ) => { + const { data } = await client.get<{ + success: boolean; + data: Message[]; + }>(`/health/consultation-sessions/${sessionId}/messages/poll`, { + params: { after_id: afterId, timeout: 25 }, + timeout: 30000, + }); + return data.data; + }, + + markSessionRead: async (id: string) => { + await client.put(`/health/consultation-sessions/${id}/read`); + }, + + exportSessions: async (params?: { + status?: string; + patient_id?: string; + doctor_id?: string; + page?: number; + page_size?: number; + }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/consultation-sessions/export', { params }); + return data.data; + }, + + /** 从咨询会话创建随访任务 */ + createFollowUpFromSession: async ( + sessionId: string, + req: CreateFollowUpFromConsultationReq, + ) => { + const { data } = await client.post<{ + success: boolean; + data: FollowUpFromConsultationResp; + }>(`/health/consultation-sessions/${sessionId}/follow-up`, req); + return data.data; + }, + + /** 从咨询会话触发 AI 分析 */ + triggerAiAnalysisFromSession: async ( + sessionId: string, + req?: TriggerAiAnalysisReq, + ) => { + const { data } = await client.post<{ + success: boolean; + data: AiAnalysisTriggeredResp; + }>(`/health/consultation-sessions/${sessionId}/ai-analysis`, req ?? {}); + return data.data; + }, +}; diff --git a/apps/web/src/api/health/criticalValueThresholds.ts b/apps/web/src/api/health/criticalValueThresholds.ts new file mode 100644 index 0000000..55f75a4 --- /dev/null +++ b/apps/web/src/api/health/criticalValueThresholds.ts @@ -0,0 +1,110 @@ +import client from '../client'; + +// --- Types --- + +export interface CriticalValueThreshold { + id: string; + tenant_id: string; + indicator: string; + direction: string; + threshold_value: number; + level: string; + department?: string; + age_min?: number; + age_max?: number; + is_active: boolean; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateThresholdReq { + indicator: string; + direction: string; + threshold_value: number; + level?: string; + department?: string; + age_min?: number; + age_max?: number; +} + +export interface UpdateThresholdReq { + threshold_value: number; + level?: string; + department?: string; + age_min?: number; + age_max?: number; + version: number; +} + +// --- Constants --- + +export const INDICATOR_OPTIONS = [ + { label: '收缩压', value: 'systolic_bp' }, + { label: '舒张压', value: 'diastolic_bp' }, + { label: '心率', value: 'heart_rate' }, + { label: '血糖', value: 'blood_sugar' }, + { label: '空腹血糖', value: 'blood_sugar_fasting' }, + { label: '餐后血糖', value: 'blood_sugar_postprandial' }, + { label: '血氧', value: 'blood_oxygen' }, + { label: '体温', value: 'temperature' }, +]; + +export const DIRECTION_OPTIONS = [ + { label: '偏高', value: 'high' }, + { label: '偏低', value: 'low' }, +]; + +export const LEVEL_OPTIONS = [ + { label: '危急', value: 'critical' }, + { label: '警告', value: 'warning' }, +]; + +export const LEVEL_COLOR: Record = { + critical: 'red', + warning: 'orange', +}; + +export const INDICATOR_LABEL: Record = Object.fromEntries( + INDICATOR_OPTIONS.map((o) => [o.value, o.label]), +); + +export const DIRECTION_LABEL: Record = Object.fromEntries( + DIRECTION_OPTIONS.map((o) => [o.value, o.label]), +); + +export const LEVEL_LABEL: Record = Object.fromEntries( + LEVEL_OPTIONS.map((o) => [o.value, o.label]), +); + +// --- API --- + +export const criticalValueThresholdApi = { + list: async () => { + const { data } = await client.get<{ + success: boolean; + data: CriticalValueThreshold[]; + }>('/health/critical-value-thresholds'); + return data.data; + }, + + create: async (req: CreateThresholdReq) => { + const { data } = await client.post<{ + success: boolean; + data: CriticalValueThreshold; + }>('/health/critical-value-thresholds', req); + return data.data; + }, + + update: async (id: string, req: UpdateThresholdReq) => { + const { data } = await client.put<{ + success: boolean; + data: CriticalValueThreshold; + }>(`/health/critical-value-thresholds/${id}`, req); + return data.data; + }, + + delete: async (id: string) => { + await client.delete(`/health/critical-value-thresholds/${id}`); + }, +}; diff --git a/apps/web/src/api/health/dashboard.test.ts b/apps/web/src/api/health/dashboard.test.ts new file mode 100644 index 0000000..a58b82d --- /dev/null +++ b/apps/web/src/api/health/dashboard.test.ts @@ -0,0 +1,105 @@ +/** + * dashboard + actionInbox API 契约测试 + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('../client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import { dashboardApi } from './dashboard' +import { actionInboxApi } from './actionInbox' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('dashboardApi', () => { + const fakeRes = { data: { data: {} } } + + it('getSystemHealth 应调用 GET /health/admin/system-health', async () => { + mockGet.mockResolvedValue(fakeRes) + await dashboardApi.getSystemHealth() + + expect(mockGet).toHaveBeenCalledWith('/health/admin/system-health') + }) + + it('getUserActivity 应调用 GET /health/admin/user-activity', async () => { + mockGet.mockResolvedValue(fakeRes) + await dashboardApi.getUserActivity() + + expect(mockGet).toHaveBeenCalledWith('/health/admin/user-activity') + }) + + it('getModuleStatus 应调用 GET /health/admin/modules', async () => { + mockGet.mockResolvedValue(fakeRes) + await dashboardApi.getModuleStatus() + + expect(mockGet).toHaveBeenCalledWith('/health/admin/modules') + }) + + it('getPointsRecentActivity 应调用 GET /health/points/recent-activity', async () => { + mockGet.mockResolvedValue(fakeRes) + await dashboardApi.getPointsRecentActivity() + + expect(mockGet).toHaveBeenCalledWith('/health/points/recent-activity') + }) + + it('getArticleStats 应调用 GET /health/articles/stats', async () => { + mockGet.mockResolvedValue(fakeRes) + await dashboardApi.getArticleStats() + + expect(mockGet).toHaveBeenCalledWith('/health/articles/stats') + }) +}) + +describe('actionInboxApi', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('list 应调用 GET /health/action-inbox 并传递查询参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await actionInboxApi.list({ status: 'pending', type: 'alert', page: 1, page_size: 20 }) + + expect(mockGet).toHaveBeenCalledWith('/health/action-inbox', { + params: { status: 'pending', type: 'alert', page: 1, page_size: 20 }, + }) + }) + + it('getThread 应调用 GET /health/action-inbox/:sourceRef/thread', async () => { + mockGet.mockResolvedValue(fakeRes) + await actionInboxApi.getThread('ref-001') + + expect(mockGet).toHaveBeenCalledWith('/health/action-inbox/ref-001/thread') + }) + + it('getThread 应对特殊字符 URL 编码', async () => { + mockGet.mockResolvedValue(fakeRes) + await actionInboxApi.getThread('ref/with:special') + + expect(mockGet).toHaveBeenCalledWith('/health/action-inbox/ref%2Fwith%3Aspecial/thread') + }) + + it('stats 应调用 GET /health/action-inbox/stats', async () => { + mockGet.mockResolvedValue(fakeRes) + await actionInboxApi.stats() + + expect(mockGet).toHaveBeenCalledWith('/health/action-inbox/stats') + }) + + it('team 应调用 GET /health/action-inbox/team', async () => { + mockGet.mockResolvedValue(fakeRes) + await actionInboxApi.team() + + expect(mockGet).toHaveBeenCalledWith('/health/action-inbox/team') + }) +}) diff --git a/apps/web/src/api/health/dashboard.ts b/apps/web/src/api/health/dashboard.ts new file mode 100644 index 0000000..43201ef --- /dev/null +++ b/apps/web/src/api/health/dashboard.ts @@ -0,0 +1,69 @@ +import client from '../client'; + +export interface ServiceHealthStatus { + name: string; + status: string; + message: string; + response_ms: number | null; +} + +export interface SystemHealthResp { + services: ServiceHealthStatus[]; + checked_at: string; +} + +export interface RoleCount { + role: string; + count: number; +} + +export interface UserActivityResp { + daily_active: number; + weekly_active: number; + monthly_active: number; + total_registered: number; + by_role: RoleCount[]; +} + +export interface ModuleStatusResp { + name: string; + display_name: string; + description: string; + active: boolean; + entity_count: number | null; + route_count: number | null; +} + +export interface PointsActivityItem { + id: string; + user_name: string; + detail: string; + amount: string; + type: string; + created_at: string; +} + +export interface ArticleStatsResp { + published: number; + draft: number; + pending_review: number; + rejected: number; + total_views: number; +} + +export const dashboardApi = { + getSystemHealth: () => + client.get('/health/admin/system-health').then((r) => r.data.data as SystemHealthResp), + + getUserActivity: () => + client.get('/health/admin/user-activity').then((r) => r.data.data as UserActivityResp), + + getModuleStatus: () => + client.get('/health/admin/modules').then((r) => r.data.data as ModuleStatusResp[]), + + getPointsRecentActivity: () => + client.get('/health/points/recent-activity').then((r) => r.data.data as PointsActivityItem[]), + + getArticleStats: () => + client.get('/health/articles/stats').then((r) => r.data.data as ArticleStatsResp), +}; diff --git a/apps/web/src/api/health/deviceReadings.test.ts b/apps/web/src/api/health/deviceReadings.test.ts new file mode 100644 index 0000000..ae0b191 --- /dev/null +++ b/apps/web/src/api/health/deviceReadings.test.ts @@ -0,0 +1,82 @@ +/** + * deviceReadings + devices API 契约测试 + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('../client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import { deviceReadingApi } from './deviceReadings' +import { deviceApi } from './devices' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('deviceReadingApi', () => { + const fakeRes = { data: { data: {} } } + + it('batchCreate 应调用 POST /health/patients/:id/device-readings/batch', async () => { + mockPost.mockResolvedValue(fakeRes) + const data = { + device_id: 'dev-001', + readings: [ + { device_type: 'blood_pressure', values: { systolic: 130, diastolic: 85 }, measured_at: '2026-05-03T08:00:00Z' }, + ], + } + await deviceReadingApi.batchCreate('p-001', data) + + expect(mockPost).toHaveBeenCalledWith('/health/patients/p-001/device-readings/batch', data) + }) + + it('query 应调用 GET /health/patients/:id/device-readings 并剥离 patient_id', async () => { + mockGet.mockResolvedValue(fakeRes) + await deviceReadingApi.query({ patient_id: 'p-001', device_type: 'blood_pressure', hours: 24 }) + + expect(mockGet).toHaveBeenCalledWith('/health/patients/p-001/device-readings', { + params: { device_type: 'blood_pressure', hours: 24 }, + }) + }) + + it('queryHourly 应调用 GET /health/patients/:id/device-readings/hourly', async () => { + mockGet.mockResolvedValue(fakeRes) + await deviceReadingApi.queryHourly({ patient_id: 'p-001', device_type: 'blood_pressure', days: 7 }) + + expect(mockGet).toHaveBeenCalledWith('/health/patients/p-001/device-readings/hourly', { + params: { device_type: 'blood_pressure', days: 7 }, + }) + }) +}) + +describe('deviceApi', () => { + const fakeRes = { data: { data: {} } } + + it('listDevices 应调用 GET /health/devices 并传递查询参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await deviceApi.listDevices({ patient_id: 'p-001', device_type: 'blood_pressure', page: 1, page_size: 10 }) + + expect(mockGet).toHaveBeenCalledWith('/health/devices', { + params: { patient_id: 'p-001', device_type: 'blood_pressure', page: 1, page_size: 10 }, + }) + }) + + it('unbindDevice 应调用 DELETE /health/devices/:id 并在 body 传递 version', async () => { + mockDelete.mockResolvedValue(fakeRes) + await deviceApi.unbindDevice('dev-001', 2) + + expect(mockDelete).toHaveBeenCalledWith('/health/devices/dev-001', { + data: { version: 2 }, + }) + }) +}) diff --git a/apps/web/src/api/health/deviceReadings.ts b/apps/web/src/api/health/deviceReadings.ts new file mode 100644 index 0000000..94f0424 --- /dev/null +++ b/apps/web/src/api/health/deviceReadings.ts @@ -0,0 +1,72 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --- Types --- +export interface DeviceReading { + id: string; + device_id?: string; + device_type: string; + device_model?: string; + raw_value: Record; + measured_at: string; + created_at: string; +} + +export interface HourlyReading { + id: string; + device_type: string; + hour_start: string; + min_val?: number; + max_val?: number; + avg_val: number; + sample_count: number; +} + +export interface DailyReading { + id: string; + device_type: string; + date_bucket: string; + min_val?: number; + max_val?: number; + avg_val: number; + sample_count: number; + percentile_95?: number; +} + +export interface BatchReadingRequest { + device_id: string; + device_model?: string; + readings: { + device_type: string; + values: Record; + measured_at: string; + }[]; +} + +export interface BatchResult { + accepted: number; + duplicates: number; + earliest?: string; + latest?: string; +} + +// --- API --- +export const deviceReadingApi = { + batchCreate: (patientId: string, data: BatchReadingRequest) => + client.post(`/health/patients/${patientId}/device-readings/batch`, data).then((r) => r.data.data as BatchResult), + + query: (params: { patient_id: string; device_type?: string; hours?: number; page?: number; page_size?: number }) => { + const { patient_id, ...query } = params; + return client.get(`/health/patients/${patient_id}/device-readings`, { params: query }).then((r) => r.data.data as PaginatedResponse); + }, + + queryHourly: (params: { patient_id: string; device_type: string; days?: number; page?: number; page_size?: number }) => { + const { patient_id, ...query } = params; + return client.get(`/health/patients/${patient_id}/device-readings/hourly`, { params: query }).then((r) => r.data.data as PaginatedResponse); + }, + + queryDaily: (params: { patient_id: string; device_type?: string; from_date?: string; to_date?: string; page?: number; page_size?: number }) => { + const { patient_id, ...query } = params; + return client.get(`/health/vital-signs/daily`, { params: { ...query, patient_id } }).then((r) => r.data.data as PaginatedResponse); + }, +}; diff --git a/apps/web/src/api/health/devices.ts b/apps/web/src/api/health/devices.ts new file mode 100644 index 0000000..d7b6cbd --- /dev/null +++ b/apps/web/src/api/health/devices.ts @@ -0,0 +1,37 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --- Types --- +export interface DeviceItem { + id: string; + patient_id: string; + device_id: string; + device_model: string; + device_type: string; + status?: string; + firmware_version?: string; + manufacturer?: string; + connection_type?: string; + metadata?: Record; + bound_at: string; + last_sync_at: string; + version: number; +} + +// --- API --- +export const deviceApi = { + listDevices: (params?: { + patient_id?: string; + device_type?: string; + page?: number; + page_size?: number; + }) => + client + .get('/health/devices', { params }) + .then((r) => r.data.data as PaginatedResponse), + + unbindDevice: (id: string, version: number) => + client + .delete(`/health/devices/${id}`, { data: { version } }) + .then((r) => r.data.data as DeviceItem), +}; diff --git a/apps/web/src/api/health/diagnoses.ts b/apps/web/src/api/health/diagnoses.ts new file mode 100644 index 0000000..3fe68e7 --- /dev/null +++ b/apps/web/src/api/health/diagnoses.ts @@ -0,0 +1,108 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --- Types --- + +export interface Diagnosis { + id: string; + patient_id: string; + health_record_id?: string; + icd_code: string; + diagnosis_name: string; + diagnosis_type: string; + diagnosed_date: string; + status: string; + diagnosed_by?: string; + notes?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateDiagnosisReq { + icd_code: string; + diagnosis_name: string; + diagnosis_type?: string; + diagnosed_date: string; + status?: string; + health_record_id?: string; + diagnosed_by?: string; + notes?: string; +} + +export interface UpdateDiagnosisReq { + icd_code?: string; + diagnosis_name?: string; + diagnosis_type?: string; + diagnosed_date?: string; + status?: string; + health_record_id?: string; + diagnosed_by?: string; + notes?: string; +} + +// --- Constants --- + +export const DIAGNOSIS_TYPE_OPTIONS = [ + { label: '主要诊断', value: 'primary' }, + { label: '次要诊断', value: 'secondary' }, + { label: '合并症', value: 'comorbid' }, +]; + +export const DIAGNOSIS_STATUS_OPTIONS = [ + { label: '活跃', value: 'active' }, + { label: '已缓解', value: 'resolved' }, + { label: '慢性', value: 'chronic' }, +]; + +export const DIAGNOSIS_TYPE_COLOR: Record = { + primary: 'red', + secondary: 'blue', + comorbid: 'orange', +}; + +export const DIAGNOSIS_STATUS_COLOR: Record = { + active: 'green', + resolved: 'default', + chronic: 'orange', +}; + +export const DIAGNOSIS_TYPE_LABEL: Record = Object.fromEntries( + DIAGNOSIS_TYPE_OPTIONS.map((o) => [o.value, o.label]), +); + +export const DIAGNOSIS_STATUS_LABEL: Record = Object.fromEntries( + DIAGNOSIS_STATUS_OPTIONS.map((o) => [o.value, o.label]), +); + +// --- API --- + +export const diagnosisApi = { + list: async (patientId: string, params?: { page?: number; page_size?: number }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>(`/health/patients/${patientId}/diagnoses`, { params }); + return data.data; + }, + + create: async (patientId: string, req: CreateDiagnosisReq) => { + const { data } = await client.post<{ + success: boolean; + data: Diagnosis; + }>(`/health/patients/${patientId}/diagnoses`, req); + return data.data; + }, + + update: async (diagnosisId: string, req: UpdateDiagnosisReq & { version: number }) => { + const { data } = await client.put<{ + success: boolean; + data: Diagnosis; + }>(`/health/diagnoses/${diagnosisId}`, req); + return data.data; + }, + + delete: async (diagnosisId: string, version: number) => { + await client.delete(`/health/diagnoses/${diagnosisId}`, { data: { version } }); + }, +}; diff --git a/apps/web/src/api/health/dialysis.ts b/apps/web/src/api/health/dialysis.ts new file mode 100644 index 0000000..582941e --- /dev/null +++ b/apps/web/src/api/health/dialysis.ts @@ -0,0 +1,116 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --- Types --- + +export interface DialysisRecord { + id: string; + patient_id: string; + dialysis_date: string; + start_time?: string; + end_time?: string; + dry_weight?: number; + pre_weight?: number; + post_weight?: number; + pre_bp_systolic?: number; + pre_bp_diastolic?: number; + post_bp_systolic?: number; + post_bp_diastolic?: number; + pre_heart_rate?: number; + post_heart_rate?: number; + ultrafiltration_volume?: number; + dialysis_duration?: number; + blood_flow_rate?: number; + dialysis_type: string; + symptoms?: Record; + complication_notes?: string; + status: string; + reviewed_by?: string; + reviewed_at?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateDialysisRecordReq { + patient_id: string; + dialysis_date: string; + start_time?: string; + end_time?: string; + dry_weight?: number; + pre_weight?: number; + post_weight?: number; + pre_bp_systolic?: number; + pre_bp_diastolic?: number; + post_bp_systolic?: number; + post_bp_diastolic?: number; + pre_heart_rate?: number; + post_heart_rate?: number; + ultrafiltration_volume?: number; + dialysis_duration?: number; + blood_flow_rate?: number; + dialysis_type?: string; + complication_notes?: string; +} + +// --- API --- + +export const dialysisApi = { + listRecords: async ( + patientId: string, + params: { page?: number; page_size?: number }, + ) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>(`/health/patients/${patientId}/dialysis-records`, { params }); + return data.data; + }, + + getRecord: async (id: string) => { + const { data } = await client.get<{ + success: boolean; + data: DialysisRecord; + }>(`/health/dialysis-records/${id}`); + return data.data; + }, + + createRecord: async (req: CreateDialysisRecordReq) => { + const { data } = await client.post<{ + success: boolean; + data: DialysisRecord; + }>('/health/dialysis-records', req); + return data.data; + }, + + updateRecord: async ( + id: string, + req: Partial & { version: number }, + ) => { + const { data } = await client.put<{ + success: boolean; + data: DialysisRecord; + }>(`/health/dialysis-records/${id}`, req); + return data.data; + }, + + deleteRecord: async (id: string, version: number) => { + await client.delete(`/health/dialysis-records/${id}`, { data: { version } }); + }, + + reviewRecord: async (id: string, req: { version: number; doctor_notes?: string }) => { + const { data } = await client.put<{ + success: boolean; + data: Record; + }>(`/health/dialysis-records/${id}/review`, req); + return data.data; + }, + + completeRecord: async (id: string, version: number) => { + const { data } = await client.put<{ + success: boolean; + data: DialysisRecord; + }>(`/health/dialysis-records/${id}/complete`, { version }); + return data.data; + }, +}; diff --git a/apps/web/src/api/health/doctors.test.ts b/apps/web/src/api/health/doctors.test.ts new file mode 100644 index 0000000..1c9996d --- /dev/null +++ b/apps/web/src/api/health/doctors.test.ts @@ -0,0 +1,67 @@ +/** + * doctors API 契约测试 + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('../client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import { doctorApi } from './doctors' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('doctorApi', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('list 应调用 GET /health/doctors 并传递查询参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await doctorApi.list({ page: 1, page_size: 10, search: '王', department: '内科' }) + + expect(mockGet).toHaveBeenCalledWith('/health/doctors', { + params: { page: 1, page_size: 10, search: '王', department: '内科' }, + }) + }) + + it('get 应调用 GET /health/doctors/:id', async () => { + mockGet.mockResolvedValue(fakeRes) + await doctorApi.get('d-001') + + expect(mockGet).toHaveBeenCalledWith('/health/doctors/d-001') + }) + + it('create 应调用 POST /health/doctors 并传递请求体', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { name: '王医生', department: '内科', title: '主任医师' } + await doctorApi.create(req) + + expect(mockPost).toHaveBeenCalledWith('/health/doctors', req) + }) + + it('update 应调用 PUT /health/doctors/:id 并传递请求体含 version', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { title: '副主任医师', version: 1 } + await doctorApi.update('d-001', req) + + expect(mockPut).toHaveBeenCalledWith('/health/doctors/d-001', req) + }) + + it('delete 应调用 DELETE /health/doctors/:id', async () => { + mockDelete.mockResolvedValue(undefined) + await doctorApi.delete('d-001', 1) + + expect(mockDelete).toHaveBeenCalledWith('/health/doctors/d-001') + }) +}) diff --git a/apps/web/src/api/health/doctors.ts b/apps/web/src/api/health/doctors.ts new file mode 100644 index 0000000..3dcfedb --- /dev/null +++ b/apps/web/src/api/health/doctors.ts @@ -0,0 +1,83 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --- Types --- +export interface Doctor { + id: string; + user_id?: string; + name: string; + department?: string; + title?: string; + specialty?: string; + license_number?: string; + bio?: string; + online_status: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateDoctorReq { + user_id?: string; + name: string; + department?: string; + title?: string; + specialty?: string; + license_number?: string; + bio?: string; +} + +export interface UpdateDoctorReq { + name?: string; + department?: string; + title?: string; + specialty?: string; + license_number?: string; + bio?: string; + online_status?: string; +} + +// --- API --- +export const doctorApi = { + list: async (params: { + page?: number; + page_size?: number; + search?: string; + department?: string; + title?: string; + }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/doctors', { params }); + return data.data; + }, + + get: async (id: string) => { + const { data } = await client.get<{ + success: boolean; + data: Doctor; + }>(`/health/doctors/${id}`); + return data.data; + }, + + create: async (req: CreateDoctorReq) => { + const { data } = await client.post<{ + success: boolean; + data: Doctor; + }>('/health/doctors', req); + return data.data; + }, + + update: async (id: string, req: UpdateDoctorReq & { version: number }) => { + const { data } = await client.put<{ + success: boolean; + data: Doctor; + }>(`/health/doctors/${id}`, req); + return data.data; + }, + + delete: async (id: string, version: number) => { + await client.delete(`/health/doctors/${id}`, { data: { version } }); + }, +}; diff --git a/apps/web/src/api/health/familyProxy.ts b/apps/web/src/api/health/familyProxy.ts new file mode 100644 index 0000000..d584c2a --- /dev/null +++ b/apps/web/src/api/health/familyProxy.ts @@ -0,0 +1,109 @@ +import client from '../client'; + +// --- Types --- + +export interface FamilyMember { + id: string; + patient_id: string; + name: string; + relationship: string; + phone?: string; + birth_date?: string; + notes?: string; + user_id?: string; + consent_status: string; + access_level: string; + consented_at?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface FamilyPatientSummary { + family_member_id: string; + patient_id: string; + patient_name: string; + relationship: string; + consent_status: string; + access_level: string; + consented_at?: string; +} + +export interface FamilyHealthSummary { + patient_id: string; + patient_name: string; + latest_vital_signs?: Record; + active_care_plan?: Record; + recent_alerts_count: number; + next_appointment?: Record; +} + +export interface GrantAccessReq { + access_level: string; +} + +// --- Constants --- + +export const CONSENT_STATUS_OPTIONS = [ + { label: '已同意', value: 'granted' }, + { label: '待确认', value: 'pending' }, + { label: '已撤销', value: 'revoked' }, + { label: '已过期', value: 'expired' }, +]; + +export const ACCESS_LEVEL_OPTIONS = [ + { label: '完全访问', value: 'full' }, + { label: '只读', value: 'read_only' }, + { label: '摘要', value: 'summary' }, +]; + +export const CONSENT_STATUS_COLOR: Record = { + granted: 'green', + pending: 'orange', + revoked: 'red', + expired: 'default', +}; + +export const ACCESS_LEVEL_LABEL: Record = Object.fromEntries( + ACCESS_LEVEL_OPTIONS.map((o) => [o.value, o.label]), +); + +export const CONSENT_STATUS_LABEL: Record = Object.fromEntries( + CONSENT_STATUS_OPTIONS.map((o) => [o.value, o.label]), +); + +// --- API --- + +export const familyProxyApi = { + grantAccess: async (patientId: string, familyMemberId: string, req: GrantAccessReq, version: number) => { + const { data } = await client.post<{ + success: boolean; + data: FamilyMember; + }>(`/health/patients/${patientId}/family-members/${familyMemberId}/grant-access`, { ...req, version }); + return data.data; + }, + + revokeAccess: async (patientId: string, familyMemberId: string, version: number) => { + const { data } = await client.put<{ + success: boolean; + data: FamilyMember; + }>(`/health/patients/${patientId}/family-members/${familyMemberId}/revoke-access`, { version }); + return data.data; + }, + + listMyPatients: async () => { + const { data } = await client.get<{ + success: boolean; + data: FamilyPatientSummary[]; + }>('/health/family/patients'); + return data.data; + }, + + getHealthSummary: async (patientId: string) => { + const { data } = await client.get<{ + success: boolean; + data: FamilyHealthSummary; + }>(`/health/family/patients/${patientId}/health-summary`); + return data.data; + }, +}; diff --git a/apps/web/src/api/health/followUp.test.ts b/apps/web/src/api/health/followUp.test.ts new file mode 100644 index 0000000..3460c3d --- /dev/null +++ b/apps/web/src/api/health/followUp.test.ts @@ -0,0 +1,97 @@ +/** + * followUp API 契约测试 + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('../client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import { followUpApi } from './followUp' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('followUpApi - Tasks', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listTasks 应调用 GET /health/follow-up-tasks 并传递查询参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await followUpApi.listTasks({ page: 1, page_size: 20, patient_id: 'p-001', status: 'pending' }) + + expect(mockGet).toHaveBeenCalledWith('/health/follow-up-tasks', { + params: { page: 1, page_size: 20, patient_id: 'p-001', status: 'pending' }, + }) + }) + + it('getTask 应调用 GET /health/follow-up-tasks/:id', async () => { + mockGet.mockResolvedValue(fakeRes) + await followUpApi.getTask('task-001') + + expect(mockGet).toHaveBeenCalledWith('/health/follow-up-tasks/task-001') + }) + + it('createTask 应调用 POST /health/follow-up-tasks', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { patient_id: 'p-001', follow_up_type: 'phone', planned_date: '2026-05-10' } + await followUpApi.createTask(req) + + expect(mockPost).toHaveBeenCalledWith('/health/follow-up-tasks', req) + }) + + it('updateTask 应调用 PUT /health/follow-up-tasks/:id 并传递 version', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { status: 'completed', version: 1 } + await followUpApi.updateTask('task-001', req) + + expect(mockPut).toHaveBeenCalledWith('/health/follow-up-tasks/task-001', req) + }) + + it('deleteTask 应调用 DELETE /health/follow-up-tasks/:id 并在 body 传递 version', async () => { + mockDelete.mockResolvedValue(undefined) + await followUpApi.deleteTask('task-001', 2) + + expect(mockDelete).toHaveBeenCalledWith('/health/follow-up-tasks/task-001', { + data: { version: 2 }, + }) + }) +}) + +describe('followUpApi - Records', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listRecords 应调用 GET /health/follow-up-records 并传递查询参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await followUpApi.listRecords({ page: 1, page_size: 10, task_id: 'task-001' }) + + expect(mockGet).toHaveBeenCalledWith('/health/follow-up-records', { + params: { page: 1, page_size: 10, task_id: 'task-001' }, + }) + }) + + it('createRecord 应调用 POST /health/follow-up-tasks/:taskId/records 并注入 task_id', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { + executed_date: '2026-05-10', + result: '已完成', + patient_condition: '良好', + } + await followUpApi.createRecord('task-001', req) + + expect(mockPost).toHaveBeenCalledWith('/health/follow-up-tasks/task-001/records', { + ...req, + task_id: 'task-001', + }) + }) +}) diff --git a/apps/web/src/api/health/followUp.ts b/apps/web/src/api/health/followUp.ts new file mode 100644 index 0000000..c2b6a44 --- /dev/null +++ b/apps/web/src/api/health/followUp.ts @@ -0,0 +1,133 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --- Types --- +export interface FollowUpTask { + id: string; + patient_id: string; + assigned_to?: string; + patient_name?: string; + assigned_to_name?: string; + follow_up_type: string; + planned_date: string; + status: string; + content_template?: string; + related_appointment_id?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateFollowUpTaskReq { + patient_id: string; + assigned_to?: string; + follow_up_type: string; + planned_date: string; + content_template?: string; + related_appointment_id?: string; +} + +export interface UpdateFollowUpTaskReq { + assigned_to?: string; + follow_up_type?: string; + planned_date?: string; + content_template?: string; + status?: string; +} + +export interface FollowUpRecord { + id: string; + task_id: string; + executed_by?: string; + executed_date: string; + result?: string; + patient_condition?: string; + medical_advice?: string; + next_follow_up_date?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateFollowUpRecordReq { + task_id: string; + executed_by?: string; + executed_date: string; + result?: string; + patient_condition?: string; + medical_advice?: string; + next_follow_up_date?: string; +} + +// --- API --- +export const followUpApi = { + // Tasks + listTasks: async (params: { + page?: number; + page_size?: number; + patient_id?: string; + assigned_to?: string; + status?: string; + }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/follow-up-tasks', { params }); + return data.data; + }, + + getTask: async (id: string) => { + const { data } = await client.get<{ + success: boolean; + data: FollowUpTask; + }>(`/health/follow-up-tasks/${id}`); + return data.data; + }, + + createTask: async (req: CreateFollowUpTaskReq) => { + const { data } = await client.post<{ + success: boolean; + data: FollowUpTask; + }>('/health/follow-up-tasks', req); + return data.data; + }, + + updateTask: async ( + id: string, + req: UpdateFollowUpTaskReq & { version: number }, + ) => { + const { data } = await client.put<{ + success: boolean; + data: FollowUpTask; + }>(`/health/follow-up-tasks/${id}`, req); + return data.data; + }, + + deleteTask: async (id: string, version: number) => { + await client.delete(`/health/follow-up-tasks/${id}`, { + data: { version }, + }); + }, + + // Records + listRecords: async (params: { + page?: number; + page_size?: number; + task_id?: string; + patient_id?: string; + }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/follow-up-records', { params }); + return data.data; + }, + + createRecord: async (taskId: string, req: Omit) => { + const { data } = await client.post<{ + success: boolean; + data: FollowUpRecord; + }>(`/health/follow-up-tasks/${taskId}/records`, { ...req, task_id: taskId }); + return data.data; + }, +}; diff --git a/apps/web/src/api/health/followUpTemplates.test.ts b/apps/web/src/api/health/followUpTemplates.test.ts new file mode 100644 index 0000000..637fb65 --- /dev/null +++ b/apps/web/src/api/health/followUpTemplates.test.ts @@ -0,0 +1,75 @@ +/** + * followUpTemplates API 契约测试 + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('../client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import { followUpTemplateApi } from './followUpTemplates' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('followUpTemplateApi', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('list 应调用 GET /health/follow-up-templates 并传递查询参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await followUpTemplateApi.list({ page: 1, page_size: 10, follow_up_type: 'phone', status: 'active' }) + + expect(mockGet).toHaveBeenCalledWith('/health/follow-up-templates', { + params: { page: 1, page_size: 10, follow_up_type: 'phone', status: 'active' }, + }) + }) + + it('get 应调用 GET /health/follow-up-templates/:id', async () => { + mockGet.mockResolvedValue(fakeRes) + await followUpTemplateApi.get('tpl-001') + + expect(mockGet).toHaveBeenCalledWith('/health/follow-up-templates/tpl-001') + }) + + it('create 应调用 POST /health/follow-up-templates 并传递请求体', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { + name: '电话随访模板', + follow_up_type: 'phone', + fields: [ + { label: '患者状态', field_key: 'patient_status', field_type: 'select', required: true, options: '良好,一般,较差' }, + ], + } + await followUpTemplateApi.create(req) + + expect(mockPost).toHaveBeenCalledWith('/health/follow-up-templates', req) + }) + + it('update 应调用 PUT /health/follow-up-templates/:id 并传递请求体', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { name: '更新后模板', status: 'active', version: 1 } + await followUpTemplateApi.update('tpl-001', req) + + expect(mockPut).toHaveBeenCalledWith('/health/follow-up-templates/tpl-001', req) + }) + + it('delete 应调用 DELETE /health/follow-up-templates/:id 并在 body 传递 version', async () => { + mockDelete.mockResolvedValue(undefined) + await followUpTemplateApi.delete('tpl-001', 2) + + expect(mockDelete).toHaveBeenCalledWith('/health/follow-up-templates/tpl-001', { + data: { version: 2 }, + }) + }) +}) diff --git a/apps/web/src/api/health/followUpTemplates.ts b/apps/web/src/api/health/followUpTemplates.ts new file mode 100644 index 0000000..626e72a --- /dev/null +++ b/apps/web/src/api/health/followUpTemplates.ts @@ -0,0 +1,119 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +export type FollowUpType = 'phone' | 'outpatient' | 'home_visit' | 'online' | 'wechat'; +export type TemplateStatus = 'active' | 'draft' | 'archived'; + +export interface TemplateField { + id: string; + template_id: string; + label: string; + field_key: string; + field_type: string; + required: boolean; + options?: string; + placeholder?: string; + validation?: string; + sort_order: number; + created_at: string; + updated_at: string; + version: number; +} + +export interface TemplateFieldReq { + label: string; + field_key: string; + field_type: string; + required?: boolean; + options?: string; + placeholder?: string; + validation?: string; + sort_order?: number; +} + +export interface FollowUpTemplate { + id: string; + name: string; + description?: string; + follow_up_type: string; + applicable_scope?: string; + status: string; + fields: TemplateField[]; + created_at: string; + updated_at: string; + version: number; +} + +export interface FollowUpTemplateListItem { + id: string; + name: string; + description?: string; + follow_up_type: string; + status: string; + field_count: number; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateTemplateReq { + name: string; + description?: string; + follow_up_type: string; + applicable_scope?: string; + fields: TemplateFieldReq[]; +} + +export interface UpdateTemplateReq { + name?: string; + description?: string; + follow_up_type?: string; + applicable_scope?: string; + status?: string; + fields?: TemplateFieldReq[]; +} + +export const followUpTemplateApi = { + list: async (params?: { + page?: number; + page_size?: number; + follow_up_type?: string; + status?: string; + }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/follow-up-templates', { params }); + return data.data; + }, + + get: async (id: string) => { + const { data } = await client.get<{ + success: boolean; + data: FollowUpTemplate; + }>(`/health/follow-up-templates/${id}`); + return data.data; + }, + + create: async (req: CreateTemplateReq) => { + const { data } = await client.post<{ + success: boolean; + data: FollowUpTemplate; + }>('/health/follow-up-templates', req); + return data.data; + }, + + update: async (id: string, req: UpdateTemplateReq & { version: number }) => { + const { data } = await client.put<{ + success: boolean; + data: FollowUpTemplate; + }>(`/health/follow-up-templates/${id}`, req); + return data.data; + }, + + delete: async (id: string, version: number) => { + await client.delete(`/health/follow-up-templates/${id}`, { + data: { version }, + }); + }, +}; diff --git a/apps/web/src/api/health/healthData.test.ts b/apps/web/src/api/health/healthData.test.ts new file mode 100644 index 0000000..1f9862e --- /dev/null +++ b/apps/web/src/api/health/healthData.test.ts @@ -0,0 +1,135 @@ +/** + * healthData API 契约测试(体征/化验报告/健康记录/趋势/日常监测) + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('../client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import { healthDataApi } from './healthData' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('healthDataApi - Vital Signs', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listVitalSigns 应调用 GET /health/patients/:id/vital-signs 并传递分页', async () => { + mockGet.mockResolvedValue(fakeRes) + await healthDataApi.listVitalSigns('p-001', { page: 1, page_size: 10 }) + + expect(mockGet).toHaveBeenCalledWith('/health/patients/p-001/vital-signs', { + params: { page: 1, page_size: 10 }, + }) + }) + + it('createVitalSigns 应调用 POST /health/patients/:id/vital-signs', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { record_date: '2026-05-03', systolic_bp_morning: 120, diastolic_bp_morning: 80 } + await healthDataApi.createVitalSigns('p-001', req) + + expect(mockPost).toHaveBeenCalledWith('/health/patients/p-001/vital-signs', req) + }) + + it('updateVitalSigns 应调用 PUT /health/patients/:pid/vital-signs/:id', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { systolic_bp_morning: 125, version: 1 } + await healthDataApi.updateVitalSigns('p-001', 'vs-001', req) + + expect(mockPut).toHaveBeenCalledWith('/health/patients/p-001/vital-signs/vs-001', req) + }) + + it('deleteVitalSigns 应调用 DELETE /health/patients/:pid/vital-signs/:id', async () => { + mockDelete.mockResolvedValue(undefined) + await healthDataApi.deleteVitalSigns('p-001', 'vs-001') + + expect(mockDelete).toHaveBeenCalledWith('/health/patients/p-001/vital-signs/vs-001') + }) +}) + +describe('healthDataApi - Lab Reports', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listLabReports 应调用 GET /health/patients/:id/lab-reports', async () => { + mockGet.mockResolvedValue(fakeRes) + await healthDataApi.listLabReports('p-001', { page: 1, page_size: 10 }) + + expect(mockGet).toHaveBeenCalledWith('/health/patients/p-001/lab-reports', { + params: { page: 1, page_size: 10 }, + }) + }) + + it('createLabReport 应调用 POST /health/patients/:id/lab-reports', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { report_date: '2026-05-03', report_type: 'blood_test' } + await healthDataApi.createLabReport('p-001', req) + + expect(mockPost).toHaveBeenCalledWith('/health/patients/p-001/lab-reports', req) + }) + + it('reviewLabReport 应调用 PUT /health/patients/:pid/lab-reports/:rid/review', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { version: 1, doctor_notes: '指标正常' } + await healthDataApi.reviewLabReport('p-001', 'lr-001', req) + + expect(mockPut).toHaveBeenCalledWith('/health/patients/p-001/lab-reports/lr-001/review', req) + }) + + it('deleteLabReport 应调用 DELETE /health/patients/:pid/lab-reports/:id', async () => { + mockDelete.mockResolvedValue(undefined) + await healthDataApi.deleteLabReport('p-001', 'lr-001') + + expect(mockDelete).toHaveBeenCalledWith('/health/patients/p-001/lab-reports/lr-001') + }) +}) + +describe('healthDataApi - Health Records', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listHealthRecords 应调用 GET /health/patients/:id/health-records', async () => { + mockGet.mockResolvedValue(fakeRes) + await healthDataApi.listHealthRecords('p-001', { page: 1, page_size: 10 }) + + expect(mockGet).toHaveBeenCalledWith('/health/patients/p-001/health-records', { + params: { page: 1, page_size: 10 }, + }) + }) + + it('createHealthRecord 应调用 POST /health/patients/:id/health-records', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { record_type: 'checkup', record_date: '2026-05-03', content: '体检结果正常' } + await healthDataApi.createHealthRecord('p-001', req) + + expect(mockPost).toHaveBeenCalledWith('/health/patients/p-001/health-records', req) + }) +}) + +describe('healthDataApi - Trends', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listTrends 应调用 GET /health/patients/:id/trends', async () => { + mockGet.mockResolvedValue(fakeRes) + await healthDataApi.listTrends('p-001') + + expect(mockGet).toHaveBeenCalledWith('/health/patients/p-001/trends') + }) + + it('getIndicatorTimeseries 应调用 GET /health/patients/:id/trends/:indicator 并编码', async () => { + mockGet.mockResolvedValue(fakeRes) + await healthDataApi.getIndicatorTimeseries('p-001', 'blood_pressure/systolic') + + expect(mockGet).toHaveBeenCalledWith('/health/patients/p-001/trends/blood_pressure%2Fsystolic') + }) +}) diff --git a/apps/web/src/api/health/healthData.ts b/apps/web/src/api/health/healthData.ts new file mode 100644 index 0000000..cc766a7 --- /dev/null +++ b/apps/web/src/api/health/healthData.ts @@ -0,0 +1,304 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --- Types --- +export interface VitalSigns { + id: string; + patient_id: string; + record_date: string; + systolic_bp_morning?: number; + diastolic_bp_morning?: number; + systolic_bp_evening?: number; + diastolic_bp_evening?: number; + heart_rate?: number; + weight?: number; + blood_sugar?: number; + water_intake_ml?: number; + urine_output_ml?: number; + notes?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateVitalSignsReq { + record_date: string; + systolic_bp_morning?: number; + diastolic_bp_morning?: number; + systolic_bp_evening?: number; + diastolic_bp_evening?: number; + heart_rate?: number; + weight?: number; + blood_sugar?: number; + water_intake_ml?: number; + urine_output_ml?: number; + notes?: string; +} + +export interface LabReport { + id: string; + patient_id: string; + report_date: string; + report_type: string; + items?: unknown; + image_urls?: string[]; + doctor_notes?: string; + source?: string; + status: string; + reviewed_by?: string; + reviewed_at?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateLabReportReq { + report_date: string; + report_type: string; + items?: unknown; + image_urls?: string[]; + doctor_notes?: string; +} + +export interface HealthRecord { + id: string; + patient_id: string; + record_type: string; + record_date: string; + overall_assessment?: string; + report_file_url?: string; + source?: string; + notes?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateHealthRecordReq { + record_type: string; + record_date: string; + overall_assessment?: string; + report_file_url?: string; +} + +export interface DailyMonitoring { + id: string; + patient_id: string; + record_date: string; + morning_bp_systolic?: number; + morning_bp_diastolic?: number; + evening_bp_systolic?: number; + evening_bp_diastolic?: number; + weight?: number; + blood_sugar?: number; + fluid_intake?: number; + urine_output?: number; + notes?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateDailyMonitoringReq { + patient_id: string; + record_date: string; + morning_bp_systolic?: number; + morning_bp_diastolic?: number; + evening_bp_systolic?: number; + evening_bp_diastolic?: number; + weight?: number; + blood_sugar?: number; + fluid_intake?: number; + urine_output?: number; + notes?: string; +} + +export interface TrendData { + id: string; + patient_id: string; + indicator: string; + trend_data: { date: string; value: number }[]; + generated_at: string; +} + +// --- API --- +export const healthDataApi = { + // Vital Signs + listVitalSigns: async ( + patientId: string, + params: { page?: number; page_size?: number }, + ) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>(`/health/patients/${patientId}/vital-signs`, { params }); + return data.data; + }, + + createVitalSigns: async (patientId: string, req: CreateVitalSignsReq) => { + const { data } = await client.post<{ + success: boolean; + data: VitalSigns; + }>(`/health/patients/${patientId}/vital-signs`, req); + return data.data; + }, + + updateVitalSigns: async ( + patientId: string, + id: string, + req: Partial & { version: number }, + ) => { + const { data } = await client.put<{ + success: boolean; + data: VitalSigns; + }>(`/health/patients/${patientId}/vital-signs/${id}`, req); + return data.data; + }, + + deleteVitalSigns: async (patientId: string, id: string) => { + await client.delete(`/health/patients/${patientId}/vital-signs/${id}`); + }, + + // Lab Reports + listLabReports: async ( + patientId: string, + params: { page?: number; page_size?: number }, + ) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>(`/health/patients/${patientId}/lab-reports`, { params }); + return data.data; + }, + + createLabReport: async (patientId: string, req: CreateLabReportReq) => { + const { data } = await client.post<{ + success: boolean; + data: LabReport; + }>(`/health/patients/${patientId}/lab-reports`, req); + return data.data; + }, + + updateLabReport: async ( + patientId: string, + id: string, + req: Partial & { version: number }, + ) => { + const { data } = await client.put<{ + success: boolean; + data: LabReport; + }>(`/health/patients/${patientId}/lab-reports/${id}`, req); + return data.data; + }, + + deleteLabReport: async (patientId: string, id: string) => { + await client.delete(`/health/patients/${patientId}/lab-reports/${id}`); + }, + + reviewLabReport: async (patientId: string, reportId: string, req: { version: number; doctor_notes?: string }) => { + const { data } = await client.put<{ + success: boolean; + data: Record; + }>(`/health/patients/${patientId}/lab-reports/${reportId}/review`, req); + return data.data; + }, + + // Health Records + listHealthRecords: async ( + patientId: string, + params: { page?: number; page_size?: number }, + ) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>(`/health/patients/${patientId}/health-records`, { params }); + return data.data; + }, + + createHealthRecord: async ( + patientId: string, + req: CreateHealthRecordReq, + ) => { + const { data } = await client.post<{ + success: boolean; + data: HealthRecord; + }>(`/health/patients/${patientId}/health-records`, req); + return data.data; + }, + + updateHealthRecord: async ( + patientId: string, + id: string, + req: Partial & { version: number }, + ) => { + const { data } = await client.put<{ + success: boolean; + data: HealthRecord; + }>(`/health/patients/${patientId}/health-records/${id}`, req); + return data.data; + }, + + deleteHealthRecord: async (patientId: string, id: string) => { + await client.delete(`/health/patients/${patientId}/health-records/${id}`); + }, + + // Trends + listTrends: async (patientId: string) => { + const { data } = await client.get<{ + success: boolean; + data: TrendData[]; + }>(`/health/patients/${patientId}/trends`); + return data.data; + }, + + generateTrend: async (patientId: string, req: { indicator: string; start_date?: string; end_date?: string }) => { + const { data } = await client.post<{ + success: boolean; + data: TrendData; + }>(`/health/patients/${patientId}/trends/generate`, req); + return data.data; + }, + + getIndicatorTimeseries: async (patientId: string, indicator: string) => { + const { data } = await client.get<{ + success: boolean; + data: { date: string; value: number }[]; + }>(`/health/patients/${patientId}/trends/${encodeURIComponent(indicator)}`); + return data.data; + }, + + // Daily Monitoring + listDailyMonitoring: async ( + patientId: string, + params: { page?: number; page_size?: number }, + ) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>(`/health/patients/${patientId}/daily-monitoring`, { params }); + return data.data; + }, + + createDailyMonitoring: async (req: CreateDailyMonitoringReq) => { + const { data } = await client.post<{ + success: boolean; + data: DailyMonitoring; + }>('/health/daily-monitoring', req); + return data.data; + }, + + updateDailyMonitoring: async ( + id: string, + req: Partial & { version: number }, + ) => { + const { data } = await client.put<{ + success: boolean; + data: DailyMonitoring; + }>(`/health/daily-monitoring/${id}`, req); + return data.data; + }, + + deleteDailyMonitoring: async (id: string, version: number) => { + await client.delete(`/health/daily-monitoring/${id}`, { data: { version } }); + }, +}; diff --git a/apps/web/src/api/health/media.ts b/apps/web/src/api/health/media.ts new file mode 100644 index 0000000..79ebe38 --- /dev/null +++ b/apps/web/src/api/health/media.ts @@ -0,0 +1,208 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --------------------------------------------------------------------------- +// 媒体文件类型 +// --------------------------------------------------------------------------- + +export interface MediaItem { + id: string; + tenant_id: string; + folder_id?: string; + filename: string; + storage_path: string; + thumbnail_path?: string; + content_type: string; + file_size: number; + width?: number; + height?: number; + alt_text?: string; + is_public: boolean; + created_at: string; + updated_at: string; + created_by?: string; + updated_by?: string; + version: number; +} + +export interface MediaListParams { + page?: number; + page_size?: number; + folder_id?: string; + content_type?: string; + keyword?: string; + is_public?: boolean; +} + +export interface UpdateMediaReq { + filename?: string; + alt_text?: string; + is_public?: boolean; + folder_id?: string; + version: number; +} + +export interface MoveMediaReq { + folder_id?: string; + version: number; +} + +export interface CropReq { + x: number; + y: number; + width: number; + height: number; + version: number; +} + +// --------------------------------------------------------------------------- +// 文件夹类型 +// --------------------------------------------------------------------------- + +export interface FolderItem { + id: string; + tenant_id: string; + name: string; + parent_id?: string; + sort_order: number; + children: FolderItem[]; + item_count: number; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateFolderReq { + name: string; + parent_id?: string; + sort_order?: number; +} + +export interface UpdateFolderReq { + name?: string; + parent_id?: string; + sort_order?: number; + version: number; +} + +// --------------------------------------------------------------------------- +// 媒体文件 API +// --------------------------------------------------------------------------- + +export const mediaApi = { + /** 分页查询媒体文件列表 */ + list: async (params: MediaListParams) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/media', { params }); + return data.data; + }, + + /** 上传媒体文件(multipart/form-data) */ + upload: async (formData: FormData) => { + const { data } = await client.post<{ + success: boolean; + data: MediaItem; + }>('/health/media/upload', formData, { + headers: { 'Content-Type': 'multipart/form-data' }, + }); + return data.data; + }, + + /** 获取单个媒体文件详情 */ + get: async (id: string) => { + const { data } = await client.get<{ + success: boolean; + data: MediaItem; + }>(`/health/media/${id}`); + return data.data; + }, + + /** 更新媒体文件信息 */ + update: async (id: string, req: UpdateMediaReq) => { + const { data } = await client.put<{ + success: boolean; + data: MediaItem; + }>(`/health/media/${id}`, req); + return data.data; + }, + + /** 删除媒体文件 */ + delete: async (id: string, version: number) => { + const { data } = await client.delete<{ + success: boolean; + data: null; + }>(`/health/media/${id}`, { data: { version } }); + return data.data; + }, + + /** 移动媒体文件到指定文件夹 */ + move: async (id: string, req: MoveMediaReq) => { + const { data } = await client.post<{ + success: boolean; + data: MediaItem; + }>(`/health/media/${id}/move`, req); + return data.data; + }, + + /** 批量删除媒体文件 */ + batchDelete: async (ids: string[]) => { + const { data } = await client.post<{ + success: boolean; + data: null; + }>('/health/media/batch-delete', { ids }); + return data.data; + }, + + /** 裁剪媒体文件 */ + crop: async (id: string, req: CropReq) => { + const { data } = await client.post<{ + success: boolean; + data: MediaItem; + }>(`/health/media/${id}/crop`, req); + return data.data; + }, +}; + +// --------------------------------------------------------------------------- +// 文件夹 API +// --------------------------------------------------------------------------- + +export const mediaFolderApi = { + /** 获取文件夹树形结构 */ + tree: async () => { + const { data } = await client.get<{ + success: boolean; + data: FolderItem[]; + }>('/health/media-folders'); + return data.data; + }, + + /** 创建文件夹 */ + create: async (req: CreateFolderReq) => { + const { data } = await client.post<{ + success: boolean; + data: FolderItem; + }>('/health/media-folders', req); + return data.data; + }, + + /** 更新文件夹 */ + update: async (id: string, req: UpdateFolderReq) => { + const { data } = await client.put<{ + success: boolean; + data: FolderItem; + }>(`/health/media-folders/${id}`, req); + return data.data; + }, + + /** 删除文件夹 */ + delete: async (id: string, version: number) => { + const { data } = await client.delete<{ + success: boolean; + data: null; + }>(`/health/media-folders/${id}`, { data: { version } }); + return data.data; + }, +}; diff --git a/apps/web/src/api/health/medicationRecords.ts b/apps/web/src/api/health/medicationRecords.ts new file mode 100644 index 0000000..6731cef --- /dev/null +++ b/apps/web/src/api/health/medicationRecords.ts @@ -0,0 +1,111 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --- Types --- + +export interface MedicationRecord { + id: string; + patient_id: string; + medication_name: string; + generic_name?: string; + dosage?: string; + unit?: string; + frequency?: string; + route?: string; + start_date?: string; + end_date?: string; + is_current: boolean; + prescribed_by?: string; + notes?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateMedicationRecordReq { + patient_id: string; + medication_name: string; + generic_name?: string; + dosage?: string; + unit?: string; + frequency?: string; + route?: string; + start_date?: string; + end_date?: string; + is_current?: boolean; + prescribed_by?: string; + notes?: string; +} + +export interface UpdateMedicationRecordReq { + medication_name?: string; + generic_name?: string; + dosage?: string; + unit?: string; + frequency?: string; + route?: string; + start_date?: string; + end_date?: string; + is_current?: boolean; + prescribed_by?: string; + notes?: string; +} + +// --- Constants --- + +export const FREQUENCY_OPTIONS = [ + { label: '每日一次', value: 'QD' }, + { label: '每日两次', value: 'BID' }, + { label: '每日三次', value: 'TID' }, + { label: '每晚一次', value: 'QN' }, + { label: '每周一次', value: 'QW' }, + { label: '必要时', value: 'PRN' }, +]; + +export const ROUTE_OPTIONS = [ + { label: '口服', value: 'oral' }, + { label: '静脉注射', value: 'iv' }, + { label: '皮下注射', value: 'sc' }, + { label: '外用', value: 'topical' }, + { label: '吸入', value: 'inhalation' }, +]; + +// --- API --- + +export const medicationRecordApi = { + list: async (patientId: string, params?: { page?: number; page_size?: number }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>(`/health/patients/${patientId}/medications`, { params }); + return data.data; + }, + + get: async (id: string) => { + const { data } = await client.get<{ + success: boolean; + data: MedicationRecord; + }>(`/health/medications/${id}`); + return data.data; + }, + + create: async (req: CreateMedicationRecordReq) => { + const { data } = await client.post<{ + success: boolean; + data: MedicationRecord; + }>('/health/medications', req); + return data.data; + }, + + update: async (id: string, req: UpdateMedicationRecordReq & { version: number }) => { + const { data } = await client.put<{ + success: boolean; + data: MedicationRecord; + }>(`/health/medications/${id}`, req); + return data.data; + }, + + delete: async (id: string, version: number) => { + await client.delete(`/health/medications/${id}`, { data: { version } }); + }, +}; diff --git a/apps/web/src/api/health/medicationReminders.ts b/apps/web/src/api/health/medicationReminders.ts new file mode 100644 index 0000000..51a2841 --- /dev/null +++ b/apps/web/src/api/health/medicationReminders.ts @@ -0,0 +1,75 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --- Types --- + +export interface MedicationReminder { + id: string; + patient_id: string; + medication_name: string; + dosage?: string; + frequency: string; + reminder_times: unknown; + start_date?: string; + end_date?: string; + is_active: boolean; + notes?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateMedicationReminderReq { + patient_id: string; + medication_name: string; + dosage?: string; + frequency?: string; + reminder_times?: unknown; + start_date?: string; + end_date?: string; + is_active?: boolean; + notes?: string; +} + +export interface UpdateMedicationReminderReq { + medication_name?: string; + dosage?: string; + frequency?: string; + reminder_times?: unknown; + start_date?: string; + end_date?: string; + is_active?: boolean; + notes?: string; +} + +// --- API --- + +export const medicationReminderApi = { + list: async (patientId: string, params?: { page?: number; page_size?: number }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>(`/health/patients/${patientId}/medication-reminders`, { params }); + return data.data; + }, + + create: async (req: CreateMedicationReminderReq) => { + const { data } = await client.post<{ + success: boolean; + data: MedicationReminder; + }>('/health/medication-reminders', req); + return data.data; + }, + + update: async (id: string, req: UpdateMedicationReminderReq & { version: number }) => { + const { data } = await client.put<{ + success: boolean; + data: MedicationReminder; + }>(`/health/medication-reminders/${id}`, req); + return data.data; + }, + + delete: async (id: string, version: number) => { + await client.delete(`/health/medication-reminders/${id}`, { data: { version } }); + }, +}; diff --git a/apps/web/src/api/health/oauthClients.ts b/apps/web/src/api/health/oauthClients.ts new file mode 100644 index 0000000..9ff7615 --- /dev/null +++ b/apps/web/src/api/health/oauthClients.ts @@ -0,0 +1,73 @@ +import client from '../client'; + +// --- Types --- +export interface OAuthClient { + id: string; + client_id: string; + client_name: string; + scopes: string[]; + rate_limit_per_minute: number; + is_active: boolean; + token_lifetime_seconds: number; + created_at: string; + version: number; +} + +export interface OAuthClientDetail extends OAuthClient { + tenant_id: string; + client_secret: string; + allowed_patient_ids?: string[]; +} + +export interface CreateOAuthClientReq { + client_name: string; + scopes: string[]; + allowed_patient_ids?: string[]; + rate_limit_per_minute?: number; + token_lifetime_seconds?: number; +} + +export interface UpdateOAuthClientReq { + client_name?: string; + scopes?: string[]; + allowed_patient_ids?: string[] | null; + rate_limit_per_minute?: number; + is_active?: boolean; + token_lifetime_seconds?: number; + version: number; +} + +export interface RegenerateSecretResp { + client_id: string; + client_secret: string; +} + +// --- FHIR Scope --- +export const FHIR_SCOPE_OPTIONS = [ + { value: 'Patient.read', label: 'Patient.read — 读取患者' }, + { value: 'Observation.read', label: 'Observation.read — 读取体征' }, + { value: 'Device.read', label: 'Device.read — 读取设备' }, + { value: 'DiagnosticReport.read', label: 'DiagnosticReport.read — 读取诊断报告' }, + { value: 'Encounter.read', label: 'Encounter.read — 读取就诊记录' }, + { value: 'Practitioner.read', label: 'Practitioner.read — 读取医护' }, + { value: 'Appointment.read', label: 'Appointment.read — 读取预约' }, + { value: 'Task.read', label: 'Task.read — 读取随访任务' }, +]; + +// --- API --- +export const oauthClientApi = { + list: () => + client.get('/health/oauth/clients').then((r) => r.data.data as OAuthClient[]), + + create: (data: CreateOAuthClientReq) => + client.post('/health/oauth/clients', data).then((r) => r.data.data as OAuthClientDetail), + + update: (id: string, data: UpdateOAuthClientReq) => + client.put(`/health/oauth/clients/${id}`, data).then((r) => r.data.data as OAuthClient), + + delete: (id: string) => + client.delete(`/health/oauth/clients/${id}`).then((r) => r.data), + + regenerateSecret: (id: string) => + client.post(`/health/oauth/clients/${id}/regenerate-secret`).then((r) => r.data.data as RegenerateSecretResp), +}; diff --git a/apps/web/src/api/health/patients.test.ts b/apps/web/src/api/health/patients.test.ts new file mode 100644 index 0000000..9654cc1 --- /dev/null +++ b/apps/web/src/api/health/patients.test.ts @@ -0,0 +1,126 @@ +/** + * patients API 契约测试 + * + * 验证 patientApi 各函数调用正确的 HTTP 方法、URL 路径和参数序列化。 + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('../client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import { patientApi } from './patients' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('patientApi', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('list 应调用 GET /health/patients 并传递查询参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await patientApi.list({ page: 1, page_size: 20, search: '张三', status: 'active' }) + + expect(mockGet).toHaveBeenCalledWith('/health/patients', { + params: { page: 1, page_size: 20, search: '张三', status: 'active' }, + }) + }) + + it('list 应支持 tag_id 过滤参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await patientApi.list({ tag_id: 'tag-001' }) + + expect(mockGet).toHaveBeenCalledWith('/health/patients', { + params: { tag_id: 'tag-001' }, + }) + }) + + it('get 应调用 GET /health/patients/:id', async () => { + mockGet.mockResolvedValue(fakeRes) + await patientApi.get('p-001') + + expect(mockGet).toHaveBeenCalledWith('/health/patients/p-001') + }) + + it('create 应调用 POST /health/patients 并传递请求体', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { name: '李四', gender: 'male', birth_date: '1990-01-01' } + await patientApi.create(req) + + expect(mockPost).toHaveBeenCalledWith('/health/patients', req) + }) + + it('update 应调用 PUT /health/patients/:id 并传递请求体含 version', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { name: '李四改', version: 2 } + await patientApi.update('p-001', req) + + expect(mockPut).toHaveBeenCalledWith('/health/patients/p-001', req) + }) + + it('delete 应调用 DELETE /health/patients/:id 并在 body 中传递 version', async () => { + mockDelete.mockResolvedValue(undefined) + await patientApi.delete('p-001', 3) + + expect(mockDelete).toHaveBeenCalledWith('/health/patients/p-001', { + data: { version: 3 }, + }) + }) + + it('manageTags 应调用 POST /health/patients/:id/tags 并传递 tag_ids', async () => { + mockPost.mockResolvedValue(undefined) + await patientApi.manageTags('p-001', ['tag-1', 'tag-2']) + + expect(mockPost).toHaveBeenCalledWith('/health/patients/p-001/tags', { + tag_ids: ['tag-1', 'tag-2'], + }) + }) + + it('listFamilyMembers 应调用 GET /health/patients/:id/family-members', async () => { + mockGet.mockResolvedValue(fakeRes) + await patientApi.listFamilyMembers('p-001') + + expect(mockGet).toHaveBeenCalledWith('/health/patients/p-001/family-members') + }) + + it('createFamilyMember 应调用 POST /health/patients/:id/family-members', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { name: '家属A', relationship: 'spouse', phone: '13800138000' } + await patientApi.createFamilyMember('p-001', req) + + expect(mockPost).toHaveBeenCalledWith('/health/patients/p-001/family-members', req) + }) + + it('updateFamilyMember 应调用 PUT /health/patients/:pid/family-members/:mid', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { name: '家属A改', version: 1 } + await patientApi.updateFamilyMember('p-001', 'fm-001', req) + + expect(mockPut).toHaveBeenCalledWith('/health/patients/p-001/family-members/fm-001', req) + }) + + it('deleteFamilyMember 应调用 DELETE /health/patients/:pid/family-members/:mid', async () => { + mockDelete.mockResolvedValue(undefined) + await patientApi.deleteFamilyMember('p-001', 'fm-001') + + expect(mockDelete).toHaveBeenCalledWith('/health/patients/p-001/family-members/fm-001') + }) + + it('listTags 应调用 GET /health/patient-tags', async () => { + mockGet.mockResolvedValue(fakeRes) + await patientApi.listTags() + + expect(mockGet).toHaveBeenCalledWith('/health/patient-tags') + }) +}) diff --git a/apps/web/src/api/health/patients.ts b/apps/web/src/api/health/patients.ts new file mode 100644 index 0000000..89472a0 --- /dev/null +++ b/apps/web/src/api/health/patients.ts @@ -0,0 +1,175 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --- Types --- +export interface PatientListItem { + id: string; + name: string; + gender?: string; + birth_date?: string; + blood_type?: string; + status: string; + verification_status: string; + source?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface PatientDetail { + id: string; + user_id?: string; + name: string; + gender?: string; + birth_date?: string; + blood_type?: string; + id_number?: string; + allergy_history?: string; + medical_history_summary?: string; + emergency_contact_name?: string; + emergency_contact_phone?: string; + status: string; + verification_status: string; + source?: string; + notes?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreatePatientReq { + name: string; + gender?: string; + birth_date?: string; + blood_type?: string; + id_number?: string; + allergy_history?: string; + medical_history_summary?: string; + emergency_contact_name?: string; + emergency_contact_phone?: string; + source?: string; + notes?: string; +} + +export interface UpdatePatientReq extends Partial { + status?: string; + verification_status?: string; +} + +export interface FamilyMember { + id: string; + name: string; + relationship: string; + phone?: string; + id_number?: string; + notes?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateFamilyMemberReq { + name: string; + relationship: string; + phone?: string; + id_number?: string; + notes?: string; +} + +export interface TagItem { + id: string; + name: string; + color: string | null; + description: string | null; +} + +// --- API --- +export const patientApi = { + list: async (params: { + page?: number; + page_size?: number; + search?: string; + status?: string; + tag_id?: string; + }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/patients', { params }); + return data.data; + }, + + get: async (id: string) => { + const { data } = await client.get<{ + success: boolean; + data: PatientDetail; + }>(`/health/patients/${id}`); + return data.data; + }, + + create: async (req: CreatePatientReq) => { + const { data } = await client.post<{ + success: boolean; + data: PatientDetail; + }>('/health/patients', req); + return data.data; + }, + + update: async (id: string, req: UpdatePatientReq & { version: number }) => { + const { data } = await client.put<{ + success: boolean; + data: PatientDetail; + }>(`/health/patients/${id}`, req); + return data.data; + }, + + delete: async (id: string, version: number) => { + await client.delete(`/health/patients/${id}`, { data: { version } }); + }, + + manageTags: async (id: string, tagIds: string[]) => { + await client.post(`/health/patients/${id}/tags`, { tag_ids: tagIds }); + }, + + listFamilyMembers: async (id: string) => { + const { data } = await client.get<{ + success: boolean; + data: FamilyMember[]; + }>(`/health/patients/${id}/family-members`); + return data.data; + }, + + createFamilyMember: async (id: string, req: CreateFamilyMemberReq) => { + const { data } = await client.post<{ + success: boolean; + data: FamilyMember; + }>(`/health/patients/${id}/family-members`, req); + return data.data; + }, + + updateFamilyMember: async ( + patientId: string, + memberId: string, + req: Partial & { version: number }, + ) => { + const { data } = await client.put<{ + success: boolean; + data: FamilyMember; + }>(`/health/patients/${patientId}/family-members/${memberId}`, req); + return data.data; + }, + + deleteFamilyMember: async (patientId: string, memberId: string) => { + await client.delete( + `/health/patients/${patientId}/family-members/${memberId}`, + ); + }, + + listTags: async () => { + const { data } = await client.get<{ + success: boolean; + data: TagItem[]; + }>('/health/patient-tags'); + return data.data; + }, +}; diff --git a/apps/web/src/api/health/points.test.ts b/apps/web/src/api/health/points.test.ts new file mode 100644 index 0000000..a898c99 --- /dev/null +++ b/apps/web/src/api/health/points.test.ts @@ -0,0 +1,230 @@ +/** + * points API 契约测试(完整覆盖 pointsApi + pointsAdminApi) + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('../client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import { pointsApi, pointsAdminApi } from './points' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('pointsAdminApi', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('getPatientAccount 应调用 GET /health/admin/points/patients/:id/account', async () => { + mockGet.mockResolvedValue(fakeRes) + await pointsAdminApi.getPatientAccount('p-001') + + expect(mockGet).toHaveBeenCalledWith('/health/admin/points/patients/p-001/account') + }) + + it('listPatientTransactions 应调用 GET 并传递分页参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await pointsAdminApi.listPatientTransactions('p-001', { page: 2, page_size: 15 }) + + expect(mockGet).toHaveBeenCalledWith('/health/admin/points/patients/p-001/transactions', { + params: { page: 2, page_size: 15 }, + }) + }) +}) + +describe('pointsApi - Rules', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listRules 应调用 GET /health/admin/points/rules', async () => { + mockGet.mockResolvedValue(fakeRes) + await pointsApi.listRules() + + expect(mockGet).toHaveBeenCalledWith('/health/admin/points/rules') + }) + + it('createRule 应调用 POST /health/admin/points/rules', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { event_type: 'daily_checkin', name: '每日签到', points_value: 10, daily_cap: 1 } + await pointsApi.createRule(req) + + expect(mockPost).toHaveBeenCalledWith('/health/admin/points/rules', req) + }) + + it('updateRule 应调用 PUT /health/admin/points/rules/:id', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { points_value: 20, version: 1 } + await pointsApi.updateRule('rule-001', req) + + expect(mockPut).toHaveBeenCalledWith('/health/admin/points/rules/rule-001', { + data: req, + version: req.version, + }) + }) + + it('deleteRule 应调用 DELETE /health/admin/points/rules/:id', async () => { + mockDelete.mockResolvedValue(undefined) + await pointsApi.deleteRule('rule-001', 2) + + expect(mockDelete).toHaveBeenCalledWith('/health/admin/points/rules/rule-001', { + data: { version: 2 }, + }) + }) +}) + +describe('pointsApi - Products', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listProducts 应调用 GET /health/points/products', async () => { + mockGet.mockResolvedValue(fakeRes) + await pointsApi.listProducts() + + expect(mockGet).toHaveBeenCalledWith('/health/points/products', { params: undefined }) + }) + + it('createProduct 应调用 POST /health/admin/points/products', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { name: '体检优惠券', product_type: 'service', points_cost: 500, stock: 100 } + await pointsApi.createProduct(req) + + expect(mockPost).toHaveBeenCalledWith('/health/admin/points/products', req) + }) + + it('updateProduct 应调用 PUT /health/admin/points/products/:id', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { points_cost: 600, version: 1 } + await pointsApi.updateProduct('prod-001', req) + + expect(mockPut).toHaveBeenCalledWith('/health/admin/points/products/prod-001', { + data: req, + version: req.version, + }) + }) + + it('deleteProduct 应调用 DELETE /health/admin/points/products/:id', async () => { + mockDelete.mockResolvedValue(undefined) + await pointsApi.deleteProduct('prod-001', 1) + + expect(mockDelete).toHaveBeenCalledWith('/health/admin/points/products/prod-001', { + data: { version: 1 }, + }) + }) +}) + +describe('pointsApi - Orders', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listOrders 应调用 GET /health/admin/points/orders', async () => { + mockGet.mockResolvedValue(fakeRes) + await pointsApi.listOrders() + + expect(mockGet).toHaveBeenCalledWith('/health/admin/points/orders', { params: undefined }) + }) + + it('verifyOrder 应调用 POST /health/points/verify', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { qr_code: 'QR-123456' } + await pointsApi.verifyOrder(req) + + expect(mockPost).toHaveBeenCalledWith('/health/points/verify', req) + }) +}) + +describe('pointsApi - Offline Events', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listOfflineEvents 应调用 GET /health/admin/offline-events', async () => { + mockGet.mockResolvedValue(fakeRes) + await pointsApi.listOfflineEvents() + + expect(mockGet).toHaveBeenCalledWith('/health/admin/offline-events', { params: undefined }) + }) + + it('createOfflineEvent 应调用 POST /health/admin/offline-events', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { title: '健康讲座', event_date: '2026-05-20', points_reward: 50 } + await pointsApi.createOfflineEvent(req) + + expect(mockPost).toHaveBeenCalledWith('/health/admin/offline-events', req) + }) + + it('updateOfflineEvent 应调用 PUT /health/admin/offline-events/:id', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { title: '健康讲座(更新)', version: 1 } + await pointsApi.updateOfflineEvent('evt-001', req) + + expect(mockPut).toHaveBeenCalledWith('/health/admin/offline-events/evt-001', req) + }) + + it('deleteOfflineEvent 应调用 DELETE /health/admin/offline-events/:id', async () => { + mockDelete.mockResolvedValue(undefined) + await pointsApi.deleteOfflineEvent('evt-001', 1) + + expect(mockDelete).toHaveBeenCalledWith('/health/admin/offline-events/evt-001', { + data: { version: 1 }, + }) + }) +}) + +describe('pointsApi - Statistics', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('getStatistics 应调用 GET /health/admin/points/statistics', async () => { + mockGet.mockResolvedValue(fakeRes) + await pointsApi.getStatistics() + + expect(mockGet).toHaveBeenCalledWith('/health/admin/points/statistics') + }) + + it('getPatientStats 应调用 GET /health/admin/statistics/patients', async () => { + mockGet.mockResolvedValue(fakeRes) + await pointsApi.getPatientStats() + + expect(mockGet).toHaveBeenCalledWith('/health/admin/statistics/patients') + }) + + it('getConsultationStats 应调用 GET /health/admin/statistics/consultations', async () => { + mockGet.mockResolvedValue(fakeRes) + await pointsApi.getConsultationStats() + + expect(mockGet).toHaveBeenCalledWith('/health/admin/statistics/consultations') + }) + + it('getFollowUpStats 应调用 GET /health/admin/statistics/follow-ups', async () => { + mockGet.mockResolvedValue(fakeRes) + await pointsApi.getFollowUpStats() + + expect(mockGet).toHaveBeenCalledWith('/health/admin/statistics/follow-ups') + }) + + it('getHealthDataStats 应调用 GET /health/admin/statistics/health-data', async () => { + mockGet.mockResolvedValue(fakeRes) + await pointsApi.getHealthDataStats() + + expect(mockGet).toHaveBeenCalledWith('/health/admin/statistics/health-data') + }) + + it('getDialysisStats 应调用 GET /health/admin/statistics/dialysis', async () => { + mockGet.mockResolvedValue(fakeRes) + await pointsApi.getDialysisStats() + + expect(mockGet).toHaveBeenCalledWith('/health/admin/statistics/dialysis') + }) + + it('getPersonalStats 应调用 GET /health/admin/statistics/personal-stats', async () => { + mockGet.mockResolvedValue(fakeRes) + await pointsApi.getPersonalStats() + + expect(mockGet).toHaveBeenCalledWith('/health/admin/statistics/personal-stats') + }) +}) diff --git a/apps/web/src/api/health/points.ts b/apps/web/src/api/health/points.ts new file mode 100644 index 0000000..0265cbf --- /dev/null +++ b/apps/web/src/api/health/points.ts @@ -0,0 +1,446 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --- Types --- + +export interface PointsRule { + id: string; + event_type: string; + name: string; + description: string | null; + points_value: number; + daily_cap: number; + streak_7d_bonus: number; + streak_14d_bonus: number; + streak_30d_bonus: number; + is_active: boolean; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreatePointsRuleReq { + event_type: string; + name: string; + description?: string; + points_value: number; + daily_cap?: number; + streak_7d_bonus?: number; + streak_14d_bonus?: number; + streak_30d_bonus?: number; +} + +export interface PointsProduct { + id: string; + name: string; + product_type: string; // physical / service / privilege + points_cost: number; + stock: number; + image_url: string | null; + description: string | null; + is_active: boolean; + sort_order: number; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreatePointsProductReq { + name: string; + product_type: string; + points_cost: number; + stock: number; + description?: string; + image_url?: string; + sort_order?: number; +} + +export interface PointsOrder { + id: string; + patient_id: string; + product_id: string; + product_name: string | null; + points_cost: number; + status: string; // pending / verified / cancelled / expired + qr_code: string; + verified_by: string | null; + verified_at: string | null; + expires_at: string | null; + notes: string | null; + created_at: string; + updated_at: string; + version: number; +} + +export interface VerifyOrderReq { + qr_code: string; +} + +export interface OfflineEvent { + id: string; + title: string; + description: string | null; + event_date: string; + start_time: string | null; + end_time: string | null; + location: string | null; + points_reward: number; + max_participants: number; + current_participants: number; + status: string; // draft / published / ongoing / completed / cancelled + image_url: string | null; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateOfflineEventReq { + title: string; + description?: string; + event_date: string; + start_time?: string; + end_time?: string; + location?: string; + points_reward?: number; + max_participants?: number; + status?: string; + image_url?: string; +} + +export interface PointsStatistics { + total_issued: number; + total_spent: number; + total_expired: number; + active_accounts: number; + top_earners: Array<{ + account_id: string; + patient_id: string; + patient_name: string; + total_earned: number; + }>; +} + +export interface PatientStatistics { + total_patients: number; + new_this_month: number; + new_this_week: number; + active_this_month: number; +} + +export interface ConsultationStatistics { + total_sessions: number; + pending_reply: number; + avg_response_time_minutes: number | null; + this_month: number; +} + +export interface FollowUpStatistics { + total_tasks: number; + completed: number; + pending: number; + overdue: number; + completion_rate: number; +} + +export interface PersonalStats { + my_patients: number; + new_patients_this_month: number; + follow_up_rate: number; + consultations_this_month: number; + pending_consultations: number; + vital_signs_report_rate: number; + today_appointments: number; + overdue_follow_ups: number; + today_follow_ups: number; + abnormal_vital_signs: number; + vital_signs_reported: number; + vital_signs_total: number; + pending_lab_reviews: number; + yesterday_my_patients?: number; + yesterday_today_appointments?: number; + yesterday_consultations_this_month?: number; + yesterday_follow_up_rate?: number; + yesterday_today_follow_ups?: number; + yesterday_overdue_follow_ups?: number; +} + +export interface OverviewStatistics { + patients: PatientStatistics; + consultations: ConsultationStatistics; + follow_ups: FollowUpStatistics; + points: PointsStatistics; +} + +// --- Health Data Statistics Types --- + +export interface NameValue { + name: string; + value: number; +} + +export interface DialysisStatistics { + total_records: number; + this_month: number; + type_distribution: NameValue[]; + complication_rate: number; + avg_ultrafiltration: number | null; + avg_duration: number | null; + pending_review: number; +} + +export interface LabReportStatistics { + total_reports: number; + this_month: number; + type_distribution: NameValue[]; + abnormal_items: number; + pending_review: number; + reviewed: number; +} + +export interface AppointmentStatistics { + total_appointments: number; + this_month: number; + status_distribution: NameValue[]; + type_distribution: NameValue[]; + cancel_rate: number; +} + +export interface DailyReportRate { + date: string; + reported: number; + total: number; + rate: number; +} + +export interface VitalSignsReportRate { + total_patients: number; + reported_patients: number; + report_rate: number; + total_records: number; + daily_trend: DailyReportRate[]; +} + +export interface HealthDataStats { + lab_reports: LabReportStatistics; + appointments: AppointmentStatistics; + vital_signs_report_rate: VitalSignsReportRate; +} + +// --- API --- + +export interface PointsAccountDetail { + id: string; + patient_id: string; + balance: number; + total_earned: number; + total_spent: number; + total_expired: number; +} + +export interface PointsTransactionDetail { + id: string; + account_id: string; + transaction_type: string; + amount: number; + remaining_amount: number; + status: string; + expires_at: string | null; + balance_after: number; + description: string | null; + created_at: string; +} + +export const pointsAdminApi = { + getPatientAccount: async (patientId: string) => { + const { data } = await client.get<{ + success: boolean; + data: PointsAccountDetail; + }>(`/health/admin/points/patients/${patientId}/account`); + return data.data; + }, + + listPatientTransactions: async ( + patientId: string, + params: { page?: number; page_size?: number }, + ) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>(`/health/admin/points/patients/${patientId}/transactions`, { params }); + return data.data; + }, +}; + +// --- API (original) --- + +export const pointsApi = { + // Rules + listRules: async () => { + const { data } = await client.get<{ + success: boolean; + data: PointsRule[]; + }>('/health/admin/points/rules'); + return data.data; + }, + + createRule: async (req: CreatePointsRuleReq) => { + const { data } = await client.post<{ + success: boolean; + data: PointsRule; + }>('/health/admin/points/rules', req); + return data.data; + }, + + updateRule: async (id: string, req: Partial & { is_active?: boolean; version: number }) => { + const { data } = await client.put<{ + success: boolean; + data: PointsRule; + }>(`/health/admin/points/rules/${id}`, req); + return data.data; + }, + + deleteRule: async (id: string, version: number) => { + await client.delete(`/health/admin/points/rules/${id}`, { + data: { version }, + }); + }, + + // Products + listProducts: async (params?: Record) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/points/products', { params }); + return data.data; + }, + + createProduct: async (req: CreatePointsProductReq) => { + const { data } = await client.post<{ + success: boolean; + data: PointsProduct; + }>('/health/admin/points/products', req); + return data.data; + }, + + updateProduct: async (id: string, req: Partial & { is_active?: boolean; version: number }) => { + const { version, ...fields } = req; + const { data } = await client.put<{ + success: boolean; + data: PointsProduct; + }>(`/health/admin/points/products/${id}`, { data: fields, version }); + return data.data; + }, + + deleteProduct: async (id: string, version: number) => { + await client.delete(`/health/admin/points/products/${id}`, { + data: { version }, + }); + }, + + // Orders + listOrders: async (params?: Record) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/admin/points/orders', { params }); + return data.data; + }, + + verifyOrder: async (req: VerifyOrderReq) => { + const { data } = await client.post<{ + success: boolean; + data: PointsOrder; + }>('/health/points/verify', req); + return data.data; + }, + + // Offline Events + listOfflineEvents: async (params?: Record) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/admin/offline-events', { params }); + return data.data; + }, + + createOfflineEvent: async (req: CreateOfflineEventReq) => { + const { data } = await client.post<{ + success: boolean; + data: OfflineEvent; + }>('/health/admin/offline-events', req); + return data.data; + }, + + updateOfflineEvent: async (id: string, req: Partial & { version: number }) => { + const { data } = await client.put<{ + success: boolean; + data: OfflineEvent; + }>(`/health/admin/offline-events/${id}`, req); + return data.data; + }, + + deleteOfflineEvent: async (id: string, version: number) => { + await client.delete(`/health/admin/offline-events/${id}`, { + data: { version }, + }); + }, + + // Points Statistics + getStatistics: async (opts?: { silent?: boolean }) => { + const { data } = await client.get<{ + success: boolean; + data: PointsStatistics; + }>('/health/admin/points/statistics', { skipGlobalError: opts?.silent }); + return data.data; + }, + + // --- Dashboard Statistics --- + + getPatientStats: async (opts?: { silent?: boolean }): Promise => { + const { data } = await client.get<{ + success: boolean; + data: PatientStatistics; + }>('/health/admin/statistics/patients', { skipGlobalError: opts?.silent }); + return data.data; + }, + + getConsultationStats: async (opts?: { silent?: boolean }): Promise => { + const { data } = await client.get<{ + success: boolean; + data: ConsultationStatistics; + }>('/health/admin/statistics/consultations', { skipGlobalError: opts?.silent }); + return data.data; + }, + + getFollowUpStats: async (opts?: { silent?: boolean }): Promise => { + const { data } = await client.get<{ + success: boolean; + data: FollowUpStatistics; + }>('/health/admin/statistics/follow-ups', { skipGlobalError: opts?.silent }); + return data.data; + }, + + getHealthDataStats: async (opts?: { silent?: boolean }): Promise => { + const { data } = await client.get<{ + success: boolean; + data: HealthDataStats; + }>('/health/admin/statistics/health-data', { skipGlobalError: opts?.silent }); + return data.data; + }, + + getDialysisStats: async (opts?: { silent?: boolean }): Promise => { + const { data } = await client.get<{ + success: boolean; + data: DialysisStatistics; + }>('/health/admin/statistics/dialysis', { skipGlobalError: opts?.silent }); + return data.data; + }, + + getPersonalStats: async (opts?: { silent?: boolean }): Promise => { + const { data } = await client.get<{ + success: boolean; + data: PersonalStats; + }>('/health/admin/statistics/personal-stats', { skipGlobalError: opts?.silent }); + return data.data; + }, +}; diff --git a/apps/web/src/api/health/shifts.ts b/apps/web/src/api/health/shifts.ts new file mode 100644 index 0000000..05f2fbe --- /dev/null +++ b/apps/web/src/api/health/shifts.ts @@ -0,0 +1,247 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --- Types --- + +export interface Shift { + id: string; + tenant_id: string; + shift_date: string; + period: string; + nurse_id?: string; + status: string; + notes?: string; + created_at: string; + updated_at: string; + version: number; + patient_count?: number; + critical_count?: number; + attention_count?: number; +} + +export interface PatientAssignment { + id: string; + tenant_id: string; + shift_id: string; + patient_id: string; + care_level: string; + notes?: string; + created_at: string; + updated_at: string; + version: number; + patient_name?: string; +} + +export interface HandoffLog { + id: string; + tenant_id: string; + from_shift_id: string; + to_shift_id: string; + patient_id: string; + notes?: string; + pending_items?: Record; + created_at: string; + updated_at: string; + version: number; + patient_name?: string; +} + +export interface CreateShiftReq { + shift_date: string; + period: string; + nurse_id?: string; + notes?: string; +} + +export interface UpdateShiftReq { + shift_date?: string; + period?: string; + nurse_id?: string; + status?: string; + notes?: string; +} + +export interface ListShiftsParams { + page?: number; + page_size?: number; + shift_date?: string; + period?: string; + nurse_id?: string; + status?: string; +} + +export interface CreatePatientAssignmentReq { + patient_id: string; + care_level?: string; + notes?: string; +} + +export interface BatchAssignReq { + patient_ids: string[]; + care_level?: string; +} + +export interface UpdatePatientAssignmentReq { + care_level?: string; + notes?: string; +} + +export interface CreateHandoffReq { + from_shift_id: string; + to_shift_id: string; + patient_id: string; + notes?: string; + pending_items?: Record; +} + +export interface ListHandoffParams { + page?: number; + page_size?: number; + from_shift_id?: string; + to_shift_id?: string; +} + +// --- Constants --- + +export const PERIOD_OPTIONS = [ + { label: '上午班', value: 'morning' }, + { label: '下午班', value: 'afternoon' }, + { label: '晚班', value: 'evening' }, + { label: '夜班', value: 'night' }, +]; + +export const SHIFT_STATUS_OPTIONS = [ + { label: '待开始', value: 'scheduled' }, + { label: '进行中', value: 'in_progress' }, + { label: '已完成', value: 'completed' }, + { label: '已取消', value: 'cancelled' }, +]; + +export const CARE_LEVEL_OPTIONS = [ + { label: '稳定', value: 'stable' }, + { label: '需关注', value: 'attention' }, + { label: '危重', value: 'critical' }, +]; + +export const PERIOD_LABEL: Record = Object.fromEntries( + PERIOD_OPTIONS.map((o) => [o.value, o.label]), +); + +export const SHIFT_STATUS_LABEL: Record = Object.fromEntries( + SHIFT_STATUS_OPTIONS.map((o) => [o.value, o.label]), +); + +export const SHIFT_STATUS_COLOR: Record = { + scheduled: 'default', + in_progress: 'processing', + completed: 'success', + cancelled: 'error', +}; + +export const CARE_LEVEL_LABEL: Record = Object.fromEntries( + CARE_LEVEL_OPTIONS.map((o) => [o.value, o.label]), +); + +export const CARE_LEVEL_COLOR: Record = { + stable: 'green', + attention: 'orange', + critical: 'red', +}; + +// --- API --- + +export const shiftApi = { + // --- Shifts --- + + list: async (params: ListShiftsParams) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/shifts', { params }); + return data.data; + }, + + get: async (shiftId: string) => { + const { data } = await client.get<{ + success: boolean; + data: Shift; + }>(`/health/shifts/${shiftId}`); + return data.data; + }, + + create: async (req: CreateShiftReq) => { + const { data } = await client.post<{ + success: boolean; + data: Shift; + }>('/health/shifts', req); + return data.data; + }, + + update: async (shiftId: string, req: UpdateShiftReq & { version: number }) => { + const { data } = await client.put<{ + success: boolean; + data: Shift; + }>(`/health/shifts/${shiftId}`, req); + return data.data; + }, + + delete: async (shiftId: string, version: number) => { + await client.delete(`/health/shifts/${shiftId}`, { data: { version } }); + }, + + // --- Assignments --- + + listAssignments: async (shiftId: string, params?: { page?: number; page_size?: number }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>(`/health/shifts/${shiftId}/assignments`, { params }); + return data.data; + }, + + createAssignment: async (shiftId: string, req: CreatePatientAssignmentReq) => { + const { data } = await client.post<{ + success: boolean; + data: PatientAssignment; + }>(`/health/shifts/${shiftId}/assignments`, req); + return data.data; + }, + + batchAssign: async (shiftId: string, req: BatchAssignReq) => { + const { data } = await client.post<{ + success: boolean; + data: PatientAssignment[]; + }>(`/health/shifts/${shiftId}/assignments/batch`, req); + return data.data; + }, + + updateAssignment: async (shiftId: string, assignmentId: string, req: UpdatePatientAssignmentReq & { version: number }) => { + const { data } = await client.put<{ + success: boolean; + data: PatientAssignment; + }>(`/health/shifts/${shiftId}/assignments/${assignmentId}`, req); + return data.data; + }, + + deleteAssignment: async (shiftId: string, assignmentId: string, version: number) => { + await client.delete(`/health/shifts/${shiftId}/assignments/${assignmentId}`, { data: { version } }); + }, + + // --- Handoff Logs --- + + listHandoffs: async (params?: ListHandoffParams) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/handoff-logs', { params }); + return data.data; + }, + + createHandoff: async (req: CreateHandoffReq) => { + const { data } = await client.post<{ + success: boolean; + data: HandoffLog; + }>('/health/handoff-logs', req); + return data.data; + }, +}; diff --git a/apps/web/src/api/languages.ts b/apps/web/src/api/languages.ts new file mode 100644 index 0000000..ba61fdf --- /dev/null +++ b/apps/web/src/api/languages.ts @@ -0,0 +1,34 @@ +import client from './client'; + +// --- Types --- + +export interface LanguageInfo { + code: string; + name: string; + is_active: boolean; +} + +export interface UpdateLanguageRequest { + is_active: boolean; + name?: string; +} + +// --- API Functions --- + +export async function listLanguages(): Promise { + const { data } = await client.get<{ success: boolean; data: LanguageInfo[] }>( + '/config/languages', + ); + return data.data; +} + +export async function updateLanguage( + code: string, + req: UpdateLanguageRequest, +): Promise { + const { data } = await client.put<{ success: boolean; data: LanguageInfo }>( + `/config/languages/${code}`, + req, + ); + return data.data; +} diff --git a/apps/web/src/api/menus.ts b/apps/web/src/api/menus.ts new file mode 100644 index 0000000..f159088 --- /dev/null +++ b/apps/web/src/api/menus.ts @@ -0,0 +1,63 @@ +import client from './client'; + +export interface MenuInfo { + id: string; + parent_id?: string; + title: string; + path?: string; + icon?: string; + sort_order: number; + visible: boolean; + menu_type: string; + permission?: string; + children?: MenuInfo[]; + version: number; +} + +export interface MenuItemReq { + id?: string; + parent_id?: string; + title: string; + path?: string; + icon?: string; + sort_order?: number; + visible?: boolean; + menu_type?: string; + permission?: string; + role_ids?: string[]; + version?: number; +} + +export async function getMenus() { + const { data } = await client.get<{ success: boolean; data: MenuInfo[] }>('/config/menus'); + return data.data; +} + +export async function getMenusForUser() { + const { data } = await client.get<{ success: boolean; data: MenuInfo[] }>('/menus/user'); + return data.data; +} + +export async function batchSaveMenus(menus: MenuItemReq[]) { + await client.put('/config/menus', { menus }); +} + +export async function createMenu(req: MenuItemReq) { + const { data } = await client.post<{ success: boolean; data: MenuInfo }>( + '/config/menus', + req, + ); + return data.data; +} + +export async function updateMenu(id: string, req: MenuItemReq) { + const { data } = await client.put<{ success: boolean; data: MenuInfo }>( + `/config/menus/${id}`, + req, + ); + return data.data; +} + +export async function deleteMenu(id: string, version: number) { + await client.delete(`/config/menus/${id}`, { data: { version } }); +} diff --git a/apps/web/src/api/messageTemplates.ts b/apps/web/src/api/messageTemplates.ts new file mode 100644 index 0000000..0cfc4d2 --- /dev/null +++ b/apps/web/src/api/messageTemplates.ts @@ -0,0 +1,40 @@ +import client from './client'; +import type { PaginatedResponse } from './types'; + +export interface MessageTemplateInfo { + id: string; + tenant_id: string; + name: string; + code: string; + channel: string; + title_template: string; + body_template: string; + language: string; + created_at: string; + updated_at: string; +} + +export interface CreateTemplateRequest { + name: string; + code: string; + channel?: string; + title_template: string; + body_template: string; + language?: string; +} + +export async function listTemplates(page = 1, pageSize = 20) { + const { data } = await client.get<{ success: boolean; data: PaginatedResponse }>( + '/message-templates', + { params: { page, page_size: pageSize } }, + ); + return data.data; +} + +export async function createTemplate(req: CreateTemplateRequest) { + const { data } = await client.post<{ success: boolean; data: MessageTemplateInfo }>( + '/message-templates', + req, + ); + return data.data; +} diff --git a/apps/web/src/api/messages.test.ts b/apps/web/src/api/messages.test.ts new file mode 100644 index 0000000..b8ed9cd --- /dev/null +++ b/apps/web/src/api/messages.test.ts @@ -0,0 +1,100 @@ +/** + * messages + messageTemplates API 契约测试 + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('./client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import * as messagesApi from './messages' +import * as templateApi from './messageTemplates' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('messages API', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listMessages 应调用 GET /messages 并传递查询参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await messagesApi.listMessages({ page: 2, page_size: 10, is_read: false, priority: 'high' }) + + expect(mockGet).toHaveBeenCalledWith('/messages', { + params: expect.objectContaining({ + page: 2, + page_size: 10, + is_read: false, + priority: 'high', + }), + }) + }) + + it('getUnreadCount 应调用 GET /messages/unread-count', async () => { + mockGet.mockResolvedValue(fakeRes) + await messagesApi.getUnreadCount() + + expect(mockGet).toHaveBeenCalledWith('/messages/unread-count') + }) + + it('markRead 应调用 PUT /messages/:id/read', async () => { + mockPut.mockResolvedValue({ data: { success: true } }) + await messagesApi.markRead('msg-001') + + expect(mockPut).toHaveBeenCalledWith('/messages/msg-001/read') + }) + + it('markAllRead 应调用 PUT /messages/read-all', async () => { + mockPut.mockResolvedValue({ data: { success: true } }) + await messagesApi.markAllRead() + + expect(mockPut).toHaveBeenCalledWith('/messages/read-all') + }) + + it('deleteMessage 应调用 DELETE /messages/:id', async () => { + mockDelete.mockResolvedValue({ data: { success: true } }) + await messagesApi.deleteMessage('msg-001') + + expect(mockDelete).toHaveBeenCalledWith('/messages/msg-001') + }) + + it('sendMessage 应调用 POST /messages 并传递请求体', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { title: '通知', body: '内容', recipient_id: 'u-001' } + await messagesApi.sendMessage(req) + + expect(mockPost).toHaveBeenCalledWith('/messages', req) + }) +}) + +describe('messageTemplates API', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listTemplates 应调用 GET /message-templates 并传递分页参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await templateApi.listTemplates(1, 10) + + expect(mockGet).toHaveBeenCalledWith('/message-templates', { + params: { page: 1, page_size: 10 }, + }) + }) + + it('createTemplate 应调用 POST /message-templates', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { name: '预约提醒', code: 'appointment_reminder', title_template: '预约提醒', body_template: '您有预约' } + await templateApi.createTemplate(req) + + expect(mockPost).toHaveBeenCalledWith('/message-templates', req) + }) +}) diff --git a/apps/web/src/api/messages.ts b/apps/web/src/api/messages.ts new file mode 100644 index 0000000..7e74795 --- /dev/null +++ b/apps/web/src/api/messages.ts @@ -0,0 +1,98 @@ +import client from './client'; +import type { PaginatedResponse } from './types'; + +export interface MessageInfo { + id: string; + tenant_id: string; + template_id?: string; + sender_id?: string; + sender_type: string; + recipient_id: string; + recipient_type: string; + title: string; + body: string; + priority: string; + business_type?: string; + business_id?: string; + is_read: boolean; + read_at?: string; + is_archived: boolean; + status: string; + sent_at?: string; + created_at: string; + updated_at: string; +} + +export interface SendMessageRequest { + title: string; + body: string; + recipient_id: string; + recipient_type?: string; + priority?: string; + template_id?: string; + business_type?: string; + business_id?: string; +} + +export interface MessageQuery { + page?: number; + page_size?: number; + is_read?: boolean; + priority?: string; + business_type?: string; + status?: string; +} + +export async function listMessages(query: MessageQuery = {}) { + const { data } = await client.get<{ success: boolean; data: PaginatedResponse }>( + '/messages', + { params: { page: query.page ?? 1, page_size: query.page_size ?? 20, ...query } }, + ); + return data.data; +} + +export async function getUnreadCount() { + const { data } = await client.get<{ success: boolean; data: { count: number } }>( + '/messages/unread-count', + ); + return data.data; +} + +export async function markRead(id: string) { + const { data } = await client.put<{ success: boolean }>( + `/messages/${id}/read`, + ); + return data; +} + +export async function markAllRead() { + const { data } = await client.put<{ success: boolean }>( + '/messages/read-all', + ); + return data; +} + +export async function deleteMessage(id: string) { + const { data } = await client.delete<{ success: boolean }>( + `/messages/${id}`, + ); + return data; +} + +export async function sendMessage(req: SendMessageRequest) { + const { data } = await client.post<{ success: boolean; data: MessageInfo }>( + '/messages', + req, + ); + return data.data; +} + +export interface SubscriptionUpdateReq { + dnd_enabled: boolean; + dnd_start?: string; + dnd_end?: string; +} + +export async function updateSubscription(req: SubscriptionUpdateReq) { + await client.put('/message-subscriptions', req); +} diff --git a/apps/web/src/api/numberingRules.ts b/apps/web/src/api/numberingRules.ts new file mode 100644 index 0000000..fd29bfe --- /dev/null +++ b/apps/web/src/api/numberingRules.ts @@ -0,0 +1,73 @@ +import client from './client'; +import type { PaginatedResponse } from './types'; + +export interface NumberingRuleInfo { + id: string; + name: string; + code: string; + prefix: string; + date_format?: string; + seq_length: number; + seq_start: number; + seq_current: number; + separator: string; + reset_cycle: string; + last_reset_date?: string; + version: number; +} + +export interface CreateNumberingRuleRequest { + name: string; + code: string; + prefix?: string; + date_format?: string; + seq_length?: number; + seq_start?: number; + separator?: string; + reset_cycle?: string; +} + +export interface UpdateNumberingRuleRequest { + name?: string; + prefix?: string; + date_format?: string; + seq_length?: number; + separator?: string; + reset_cycle?: string; + version: number; +} + +export async function listNumberingRules(page = 1, pageSize = 20) { + const { data } = await client.get<{ success: boolean; data: PaginatedResponse }>( + '/config/numbering-rules', + { params: { page, page_size: pageSize } }, + ); + return data.data; +} + +export async function createNumberingRule(req: CreateNumberingRuleRequest) { + const { data } = await client.post<{ success: boolean; data: NumberingRuleInfo }>( + '/config/numbering-rules', + req, + ); + return data.data; +} + +export async function updateNumberingRule(id: string, req: UpdateNumberingRuleRequest) { + const { data } = await client.put<{ success: boolean; data: NumberingRuleInfo }>( + `/config/numbering-rules/${id}`, + req, + ); + return data.data; +} + +export async function generateNumber(id: string) { + const { data } = await client.post<{ success: boolean; data: { number: string } }>( + `/config/numbering-rules/${id}/generate`, + ); + return data.data; +} + +export async function deleteNumberingRule(id: string, version: number) { + await client.delete(`/config/numbering-rules/${id}`, { data: { version } }); +} diff --git a/apps/web/src/api/orgs.test.ts b/apps/web/src/api/orgs.test.ts new file mode 100644 index 0000000..d5718d7 --- /dev/null +++ b/apps/web/src/api/orgs.test.ts @@ -0,0 +1,126 @@ +/** + * orgs API 契约测试(组织/部门/岗位) + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('./client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import * as orgsApi from './orgs' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('organizations API', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listOrgTree 应调用 GET /organizations', async () => { + mockGet.mockResolvedValue(fakeRes) + await orgsApi.listOrgTree() + + expect(mockGet).toHaveBeenCalledWith('/organizations') + }) + + it('createOrg 应调用 POST /organizations', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { name: '总公司', code: 'HQ' } + await orgsApi.createOrg(req) + + expect(mockPost).toHaveBeenCalledWith('/organizations', req) + }) + + it('updateOrg 应调用 PUT /organizations/:id', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { name: '改名', version: 1 } + await orgsApi.updateOrg('org-001', req) + + expect(mockPut).toHaveBeenCalledWith('/organizations/org-001', req) + }) + + it('deleteOrg 应调用 DELETE /organizations/:id', async () => { + mockDelete.mockResolvedValue(undefined) + await orgsApi.deleteOrg('org-001') + + expect(mockDelete).toHaveBeenCalledWith('/organizations/org-001') + }) +}) + +describe('departments API', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listDeptTree 应调用 GET /organizations/:orgId/departments', async () => { + mockGet.mockResolvedValue(fakeRes) + await orgsApi.listDeptTree('org-001') + + expect(mockGet).toHaveBeenCalledWith('/organizations/org-001/departments') + }) + + it('createDept 应调用 POST /organizations/:orgId/departments', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { name: '内科', code: 'NK' } + await orgsApi.createDept('org-001', req) + + expect(mockPost).toHaveBeenCalledWith('/organizations/org-001/departments', req) + }) + + it('updateDept 应调用 PUT /departments/:id', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { name: '内科(更新)', version: 1 } + await orgsApi.updateDept('dept-001', req) + + expect(mockPut).toHaveBeenCalledWith('/departments/dept-001', req) + }) + + it('deleteDept 应调用 DELETE /departments/:id', async () => { + mockDelete.mockResolvedValue(undefined) + await orgsApi.deleteDept('dept-001') + + expect(mockDelete).toHaveBeenCalledWith('/departments/dept-001') + }) +}) + +describe('positions API', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listPositions 应调用 GET /departments/:deptId/positions', async () => { + mockGet.mockResolvedValue(fakeRes) + await orgsApi.listPositions('dept-001') + + expect(mockGet).toHaveBeenCalledWith('/departments/dept-001/positions') + }) + + it('createPosition 应调用 POST /departments/:deptId/positions', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { name: '主治医师', code: 'ZYS' } + await orgsApi.createPosition('dept-001', req) + + expect(mockPost).toHaveBeenCalledWith('/departments/dept-001/positions', req) + }) + + it('updatePosition 应调用 PUT /positions/:id', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { name: '主任医师', version: 1 } + await orgsApi.updatePosition('pos-001', req) + + expect(mockPut).toHaveBeenCalledWith('/positions/pos-001', req) + }) + + it('deletePosition 应调用 DELETE /positions/:id', async () => { + mockDelete.mockResolvedValue(undefined) + await orgsApi.deletePosition('pos-001') + + expect(mockDelete).toHaveBeenCalledWith('/positions/pos-001') + }) +}) diff --git a/apps/web/src/api/orgs.ts b/apps/web/src/api/orgs.ts new file mode 100644 index 0000000..d70afc3 --- /dev/null +++ b/apps/web/src/api/orgs.ts @@ -0,0 +1,174 @@ +import client from './client'; + +// --- Organization types --- + +export interface OrganizationInfo { + id: string; + name: string; + code?: string; + parent_id?: string; + path?: string; + level: number; + sort_order: number; + children: OrganizationInfo[]; + version: number; +} + +export interface CreateOrganizationRequest { + name: string; + code?: string; + parent_id?: string; + sort_order?: number; +} + +export interface UpdateOrganizationRequest { + name?: string; + code?: string; + sort_order?: number; + version: number; +} + +// --- Department types --- + +export interface DepartmentInfo { + id: string; + org_id: string; + name: string; + code?: string; + parent_id?: string; + manager_id?: string; + path?: string; + sort_order: number; + children: DepartmentInfo[]; + version: number; +} + +export interface CreateDepartmentRequest { + name: string; + code?: string; + parent_id?: string; + manager_id?: string; + sort_order?: number; +} + +export interface UpdateDepartmentRequest { + name?: string; + code?: string; + manager_id?: string; + sort_order?: number; + version: number; +} + +// --- Position types --- + +export interface PositionInfo { + id: string; + dept_id: string; + name: string; + code?: string; + level: number; + sort_order: number; + version: number; +} + +export interface CreatePositionRequest { + name: string; + code?: string; + level?: number; + sort_order?: number; +} + +export interface UpdatePositionRequest { + name?: string; + code?: string; + level?: number; + sort_order?: number; + version: number; +} + +// --- Organization API --- + +export async function listOrgTree() { + const { data } = await client.get<{ success: boolean; data: OrganizationInfo[] }>( + '/organizations', + ); + return data.data; +} + +export async function createOrg(req: CreateOrganizationRequest) { + const { data } = await client.post<{ success: boolean; data: OrganizationInfo }>( + '/organizations', + req, + ); + return data.data; +} + +export async function updateOrg(id: string, req: UpdateOrganizationRequest) { + const { data } = await client.put<{ success: boolean; data: OrganizationInfo }>( + `/organizations/${id}`, + req, + ); + return data.data; +} + +export async function deleteOrg(id: string) { + await client.delete(`/organizations/${id}`); +} + +// --- Department API --- + +export async function listDeptTree(orgId: string) { + const { data } = await client.get<{ success: boolean; data: DepartmentInfo[] }>( + `/organizations/${orgId}/departments`, + ); + return data.data; +} + +export async function createDept(orgId: string, req: CreateDepartmentRequest) { + const { data } = await client.post<{ success: boolean; data: DepartmentInfo }>( + `/organizations/${orgId}/departments`, + req, + ); + return data.data; +} + +export async function deleteDept(id: string) { + await client.delete(`/departments/${id}`); +} + +export async function updateDept(id: string, req: UpdateDepartmentRequest) { + const { data } = await client.put<{ success: boolean; data: DepartmentInfo }>( + `/departments/${id}`, + req, + ); + return data.data; +} + +// --- Position API --- + +export async function listPositions(deptId: string) { + const { data } = await client.get<{ success: boolean; data: PositionInfo[] }>( + `/departments/${deptId}/positions`, + ); + return data.data; +} + +export async function createPosition(deptId: string, req: CreatePositionRequest) { + const { data } = await client.post<{ success: boolean; data: PositionInfo }>( + `/departments/${deptId}/positions`, + req, + ); + return data.data; +} + +export async function deletePosition(id: string) { + await client.delete(`/positions/${id}`); +} + +export async function updatePosition(id: string, req: UpdatePositionRequest) { + const { data } = await client.put<{ success: boolean; data: PositionInfo }>( + `/positions/${id}`, + req, + ); + return data.data; +} diff --git a/apps/web/src/api/pluginData.test.ts b/apps/web/src/api/pluginData.test.ts new file mode 100644 index 0000000..378cefc --- /dev/null +++ b/apps/web/src/api/pluginData.test.ts @@ -0,0 +1,131 @@ +/** + * pluginData API 契约测试 + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockPatch = vi.fn() +const mockDelete = vi.fn() + +vi.mock('./client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + patch: (...args: unknown[]) => mockPatch(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import * as pluginDataApi from './pluginData' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('pluginData CRUD', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listPluginData 应调用 GET /plugins/:pid/:entity 并传递分页和过滤参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await pluginDataApi.listPluginData('crm', 'customer', 1, 20, { + filter: { status: 'active' }, + search: '张', + sort_by: 'name', + sort_order: 'asc', + }) + + expect(mockGet).toHaveBeenCalledWith('/plugins/crm/customer', { + params: expect.objectContaining({ + page: '1', + page_size: '20', + search: '张', + sort_by: 'name', + sort_order: 'asc', + }), + }) + }) + + it('getPluginData 应调用 GET /plugins/:pid/:entity/:id', async () => { + mockGet.mockResolvedValue(fakeRes) + await pluginDataApi.getPluginData('crm', 'customer', 'rec-001') + + expect(mockGet).toHaveBeenCalledWith('/plugins/crm/customer/rec-001') + }) + + it('createPluginData 应调用 POST /plugins/:pid/:entity 并包裹 data', async () => { + mockPost.mockResolvedValue(fakeRes) + const recordData = { name: '客户A', phone: '13800138000' } + await pluginDataApi.createPluginData('crm', 'customer', recordData) + + expect(mockPost).toHaveBeenCalledWith('/plugins/crm/customer', { data: recordData }) + }) + + it('updatePluginData 应调用 PUT /plugins/:pid/:entity/:id', async () => { + mockPut.mockResolvedValue(fakeRes) + const recordData = { name: '客户A(更新)' } + await pluginDataApi.updatePluginData('crm', 'customer', 'rec-001', recordData, 2) + + expect(mockPut).toHaveBeenCalledWith('/plugins/crm/customer/rec-001', { + data: recordData, + version: 2, + }) + }) + + it('deletePluginData 应调用 DELETE /plugins/:pid/:entity/:id', async () => { + mockDelete.mockResolvedValue(undefined) + await pluginDataApi.deletePluginData('crm', 'customer', 'rec-001') + + expect(mockDelete).toHaveBeenCalledWith('/plugins/crm/customer/rec-001') + }) +}) + +describe('pluginData 高级查询', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('countPluginData 应调用 GET /plugins/:pid/:entity/count', async () => { + mockGet.mockResolvedValue(fakeRes) + await pluginDataApi.countPluginData('crm', 'customer', { filter: { status: 'active' } }) + + expect(mockGet).toHaveBeenCalledWith('/plugins/crm/customer/count', { + params: expect.objectContaining({ + filter: '{"status":"active"}', + }), + }) + }) + + it('aggregatePluginData 应调用 GET /plugins/:pid/:entity/aggregate', async () => { + mockGet.mockResolvedValue(fakeRes) + await pluginDataApi.aggregatePluginData('crm', 'customer', 'status') + + expect(mockGet).toHaveBeenCalledWith('/plugins/crm/customer/aggregate', { + params: { group_by: 'status' }, + }) + }) + + it('batchPluginData 应调用 POST /plugins/:pid/:entity/batch', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { action: 'delete', ids: ['rec-1', 'rec-2'] } + await pluginDataApi.batchPluginData('crm', 'customer', req) + + expect(mockPost).toHaveBeenCalledWith('/plugins/crm/customer/batch', req) + }) + + it('resolveRefLabels 应调用 POST /plugins/:pid/:entity/resolve-labels', async () => { + mockPost.mockResolvedValue(fakeRes) + const fields = { customer_tag_id: ['tag-1', 'tag-2'] } + await pluginDataApi.resolveRefLabels('crm', 'customer', fields) + + expect(mockPost).toHaveBeenCalledWith('/plugins/crm/customer/resolve-labels', { fields }) + }) + + it('importPluginData 应调用 POST /plugins/:pid/:entity/import', async () => { + mockPost.mockResolvedValue(fakeRes) + const rows = [{ name: '客户A' }, { name: '客户B' }] + await pluginDataApi.importPluginData('crm', 'customer', rows) + + expect(mockPost).toHaveBeenCalledWith('/plugins/crm/customer/import', { rows }) + }) +}) diff --git a/apps/web/src/api/pluginData.ts b/apps/web/src/api/pluginData.ts new file mode 100644 index 0000000..302d42f --- /dev/null +++ b/apps/web/src/api/pluginData.ts @@ -0,0 +1,281 @@ +import client from './client'; + +export interface PluginDataRecord { + id: string; + data: Record; + created_at?: string; + updated_at?: string; + version?: number; +} + +interface PaginatedDataResponse { + data: PluginDataRecord[]; + total: number; + page: number; + page_size: number; + total_pages: number; +} + +export interface PluginDataListOptions { + filter?: Record; + search?: string; + sort_by?: string; + sort_order?: 'asc' | 'desc'; +} + +export async function listPluginData( + pluginId: string, + entity: string, + page = 1, + pageSize = 20, + options?: PluginDataListOptions, +) { + const params: Record = { + page: String(page), + page_size: String(pageSize), + }; + if (options?.filter) params.filter = JSON.stringify(options.filter); + if (options?.search) params.search = options.search; + if (options?.sort_by) params.sort_by = options.sort_by; + if (options?.sort_order) params.sort_order = options.sort_order; + + const { data } = await client.get<{ success: boolean; data: PaginatedDataResponse }>( + `/plugins/${pluginId}/${entity}`, + { params }, + ); + return data.data; +} + +export async function getPluginData(pluginId: string, entity: string, id: string) { + const { data } = await client.get<{ success: boolean; data: PluginDataRecord }>( + `/plugins/${pluginId}/${entity}/${id}`, + ); + return data.data; +} + +export async function createPluginData( + pluginId: string, + entity: string, + recordData: Record, +) { + const { data } = await client.post<{ success: boolean; data: PluginDataRecord }>( + `/plugins/${pluginId}/${entity}`, + { data: recordData }, + ); + return data.data; +} + +export async function updatePluginData( + pluginId: string, + entity: string, + id: string, + recordData: Record, + version: number, +) { + const { data } = await client.put<{ success: boolean; data: PluginDataRecord }>( + `/plugins/${pluginId}/${entity}/${id}`, + { data: recordData, version }, + ); + return data.data; +} + +export async function deletePluginData( + pluginId: string, + entity: string, + id: string, +) { + await client.delete(`/plugins/${pluginId}/${entity}/${id}`); +} + +export async function countPluginData( + pluginId: string, + entity: string, + options?: { filter?: Record; search?: string }, +) { + const params: Record = {}; + if (options?.filter) params.filter = JSON.stringify(options.filter); + if (options?.search) params.search = options.search; + + const { data } = await client.get<{ success: boolean; data: number }>( + `/plugins/${pluginId}/${entity}/count`, + { params }, + ); + return data.data; +} + +export interface AggregateItem { + key: string; + count: number; +} + +export async function aggregatePluginData( + pluginId: string, + entity: string, + groupBy: string, + filter?: Record, +) { + const params: Record = { group_by: groupBy }; + if (filter) params.filter = JSON.stringify(filter); + + const { data } = await client.get<{ success: boolean; data: AggregateItem[] }>( + `/plugins/${pluginId}/${entity}/aggregate`, + { params }, + ); + return data.data; +} + +// ── 批量操作 ── + +export async function batchPluginData( + pluginId: string, + entity: string, + req: { action: string; ids: string[]; data?: Record }, +) { + const { data } = await client.post<{ success: boolean; data: unknown }>( + `/plugins/${pluginId}/${entity}/batch`, + req, + ); + return data.data; +} + +// ── 部分更新 ── + +export async function patchPluginData( + pluginId: string, + entity: string, + id: string, + req: { data: Record; version: number }, +) { + const { data } = await client.patch<{ success: boolean; data: PluginDataRecord }>( + `/plugins/${pluginId}/${entity}/${id}`, + req, + ); + return data.data; +} + +// ── 时间序列 ── + +export async function getPluginTimeseries( + pluginId: string, + entity: string, + params: { + time_field: string; + time_grain: string; + start?: string; + end?: string; + }, +) { + const { data } = await client.get<{ success: boolean; data: unknown }>( + `/plugins/${pluginId}/${entity}/timeseries`, + { params }, + ); + return data.data; +} + +// ─── 跨插件引用 API ────────────────────────────────────────────────── + +export interface ResolveLabelsResult { + labels: Record>; + meta: Record; +} + +export async function resolveRefLabels( + pluginId: string, + entity: string, + fields: Record, +): Promise { + const { data } = await client.post<{ success: boolean; data: ResolveLabelsResult }>( + `/plugins/${pluginId}/${entity}/resolve-labels`, + { fields }, + ); + return data.data; +} + +export interface PublicEntity { + manifest_id: string; + plugin_id: string; + entity_name: string; + display_name: string; +} + +export async function getPluginEntityRegistry(): Promise { + const { data } = await client.get<{ success: boolean; data: PublicEntity[] }>( + '/plugin-registry/entities', + ); + return data.data; +} + +// ─── 数据导入导出 API ────────────────────────────────────────────────── + +export interface ExportOptions { + filter?: Record; + search?: string; + sort_by?: string; + sort_order?: 'asc' | 'desc'; + format?: 'json' | 'csv' | 'xlsx'; +} + +export async function exportPluginData( + pluginId: string, + entity: string, + options?: ExportOptions, +): Promise[]> { + const params: Record = {}; + if (options?.filter) params.filter = JSON.stringify(options.filter); + if (options?.search) params.search = options.search; + if (options?.sort_by) params.sort_by = options.sort_by; + if (options?.sort_order) params.sort_order = options.sort_order; + + const { data } = await client.get<{ success: boolean; data: Record[] }>( + `/plugins/${pluginId}/${entity}/export`, + { params }, + ); + return data.data; +} + +export async function exportPluginDataAsBlob( + pluginId: string, + entity: string, + format: 'csv' | 'xlsx', + options?: Omit, +): Promise { + const params: Record = { format }; + if (options?.filter) params.filter = JSON.stringify(options.filter); + if (options?.search) params.search = options.search; + if (options?.sort_by) params.sort_by = options.sort_by; + if (options?.sort_order) params.sort_order = options.sort_order; + + const response = await client.get( + `/plugins/${pluginId}/${entity}/export`, + { params, responseType: 'blob' }, + ); + return response.data as Blob; +} + +export interface ImportRowError { + row: number; + errors: string[]; +} + +export interface ImportResult { + success_count: number; + error_count: number; + errors: ImportRowError[]; +} + +export async function importPluginData( + pluginId: string, + entity: string, + rows: Record[], +): Promise { + const { data } = await client.post<{ success: boolean; data: ImportResult }>( + `/plugins/${pluginId}/${entity}/import`, + { rows }, + ); + return data.data; +} diff --git a/apps/web/src/api/plugins.test.ts b/apps/web/src/api/plugins.test.ts new file mode 100644 index 0000000..cd5b6e8 --- /dev/null +++ b/apps/web/src/api/plugins.test.ts @@ -0,0 +1,132 @@ +/** + * plugins API 契约测试(插件管理 + 市场) + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('./client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import * as pluginsApi from './plugins' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('plugins management API', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listPlugins 应调用 GET /admin/plugins 并传递分页和状态', async () => { + mockGet.mockResolvedValue(fakeRes) + await pluginsApi.listPlugins(1, 10, 'enabled') + + expect(mockGet).toHaveBeenCalledWith('/admin/plugins', { + params: { page: 1, page_size: 10, status: 'enabled' }, + }) + }) + + it('getPlugin 应调用 GET /admin/plugins/:id', async () => { + mockGet.mockResolvedValue(fakeRes) + await pluginsApi.getPlugin('plug-001') + + expect(mockGet).toHaveBeenCalledWith('/admin/plugins/plug-001') + }) + + it('installPlugin 应调用 POST /admin/plugins/:id/install', async () => { + mockPost.mockResolvedValue(fakeRes) + await pluginsApi.installPlugin('plug-001') + + expect(mockPost).toHaveBeenCalledWith('/admin/plugins/plug-001/install') + }) + + it('enablePlugin 应调用 POST /admin/plugins/:id/enable', async () => { + mockPost.mockResolvedValue(fakeRes) + await pluginsApi.enablePlugin('plug-001') + + expect(mockPost).toHaveBeenCalledWith('/admin/plugins/plug-001/enable') + }) + + it('disablePlugin 应调用 POST /admin/plugins/:id/disable', async () => { + mockPost.mockResolvedValue(fakeRes) + await pluginsApi.disablePlugin('plug-001') + + expect(mockPost).toHaveBeenCalledWith('/admin/plugins/plug-001/disable') + }) + + it('purgePlugin 应调用 DELETE /admin/plugins/:id', async () => { + mockDelete.mockResolvedValue(undefined) + await pluginsApi.purgePlugin('plug-001') + + expect(mockDelete).toHaveBeenCalledWith('/admin/plugins/plug-001') + }) + + it('getPluginHealth 应调用 GET /admin/plugins/:id/health', async () => { + mockGet.mockResolvedValue(fakeRes) + await pluginsApi.getPluginHealth('plug-001') + + expect(mockGet).toHaveBeenCalledWith('/admin/plugins/plug-001/health') + }) + + it('updatePluginConfig 应调用 PUT /admin/plugins/:id/config', async () => { + mockPut.mockResolvedValue(fakeRes) + const config = { theme: 'dark' } + await pluginsApi.updatePluginConfig('plug-001', config, 1) + + expect(mockPut).toHaveBeenCalledWith('/admin/plugins/plug-001/config', { + config, + version: 1, + }) + }) + + it('getPluginSchema 应调用 GET /admin/plugins/:id/schema', async () => { + mockGet.mockResolvedValue(fakeRes) + await pluginsApi.getPluginSchema('plug-001') + + expect(mockGet).toHaveBeenCalledWith('/admin/plugins/plug-001/schema') + }) +}) + +describe('plugin market API', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listMarketEntries 应调用 GET /market/entries', async () => { + mockGet.mockResolvedValue(fakeRes) + await pluginsApi.listMarketEntries({ page: 1, page_size: 10, category: 'crm' }) + + expect(mockGet).toHaveBeenCalledWith('/market/entries', { + params: { page: 1, page_size: 10, category: 'crm' }, + }) + }) + + it('getMarketEntry 应调用 GET /market/entries/:id', async () => { + mockGet.mockResolvedValue(fakeRes) + await pluginsApi.getMarketEntry('mkt-001') + + expect(mockGet).toHaveBeenCalledWith('/market/entries/mkt-001') + }) + + it('installFromMarket 应调用 POST /market/entries/:id/install', async () => { + mockPost.mockResolvedValue(fakeRes) + await pluginsApi.installFromMarket('mkt-001') + + expect(mockPost).toHaveBeenCalledWith('/market/entries/mkt-001/install') + }) + + it('submitMarketReview 应调用 POST /market/entries/:id/reviews', async () => { + mockPost.mockResolvedValue(fakeRes) + const review = { rating: 5, review_text: '很好用' } + await pluginsApi.submitMarketReview('mkt-001', review) + + expect(mockPost).toHaveBeenCalledWith('/market/entries/mkt-001/reviews', review) + }) +}) diff --git a/apps/web/src/api/plugins.ts b/apps/web/src/api/plugins.ts new file mode 100644 index 0000000..8450082 --- /dev/null +++ b/apps/web/src/api/plugins.ts @@ -0,0 +1,375 @@ +import client from './client'; +import type { PaginatedResponse } from './types'; + +export interface PluginEntityInfo { + name: string; + display_name: string; + table_name: string; +} + +export interface PluginPermissionInfo { + code: string; + name: string; + description: string; +} + +export type PluginStatus = 'uploaded' | 'installed' | 'enabled' | 'running' | 'disabled' | 'uninstalled'; + +export interface PluginInfo { + id: string; + name: string; + version: string; + description?: string; + author?: string; + status: PluginStatus; + config: Record; + installed_at?: string; + enabled_at?: string; + entities: PluginEntityInfo[]; + permissions?: PluginPermissionInfo[]; + record_version: number; +} + +export async function listPlugins(page = 1, pageSize = 20, status?: string) { + const { data } = await client.get<{ success: boolean; data: PaginatedResponse }>( + '/admin/plugins', + { params: { page, page_size: pageSize, status: status || undefined } }, + ); + return data.data; +} + +export async function getPlugin(id: string) { + const { data } = await client.get<{ success: boolean; data: PluginInfo }>( + `/admin/plugins/${id}`, + ); + return data.data; +} + +export async function uploadPlugin(wasmFile: File, manifestToml: string) { + const formData = new FormData(); + formData.append('wasm', wasmFile); + formData.append('manifest', manifestToml); + + const { data } = await client.post<{ success: boolean; data: PluginInfo }>( + '/admin/plugins/upload', + formData, + { headers: { 'Content-Type': 'multipart/form-data' }, timeout: 60000 }, + ); + return data.data; +} + +export async function installPlugin(id: string) { + const { data } = await client.post<{ success: boolean; data: PluginInfo }>( + `/admin/plugins/${id}/install`, + ); + return data.data; +} + +export async function enablePlugin(id: string) { + const { data } = await client.post<{ success: boolean; data: PluginInfo }>( + `/admin/plugins/${id}/enable`, + ); + return data.data; +} + +export async function disablePlugin(id: string) { + const { data } = await client.post<{ success: boolean; data: PluginInfo }>( + `/admin/plugins/${id}/disable`, + ); + return data.data; +} + +export async function uninstallPlugin(id: string) { + const { data } = await client.post<{ success: boolean; data: PluginInfo }>( + `/admin/plugins/${id}/uninstall`, + ); + return data.data; +} + +export async function purgePlugin(id: string) { + await client.delete(`/admin/plugins/${id}`); +} + +export async function getPluginHealth(id: string) { + const { data } = await client.get<{ + success: boolean; + data: { plugin_id: string; status: string; details: Record }; + }>(`/admin/plugins/${id}/health`); + return data.data; +} + +export async function updatePluginConfig(id: string, config: Record, version: number) { + const { data } = await client.put<{ success: boolean; data: PluginInfo }>( + `/admin/plugins/${id}/config`, + { config, version }, + ); + return data.data; +} + +export async function getPluginSchema(id: string): Promise { + const { data } = await client.get<{ success: boolean; data: PluginSchemaResponse }>( + `/admin/plugins/${id}/schema`, + ); + return data.data; +} + +// ── Schema 类型定义 ── + +export interface PluginFieldValidation { + pattern?: string; + message?: string; + min_length?: number; + max_length?: number; + min_value?: number; + max_value?: number; +} + +export interface PluginFieldSchema { + name: string; + field_type: string; + required: boolean; + display_name?: string; + ui_widget?: string; + options?: { label: string; value: string }[]; + searchable?: boolean; + filterable?: boolean; + sortable?: boolean; + visible_when?: string; + unique?: boolean; + ref_entity?: string; + ref_label_field?: string; + ref_search_fields?: string[]; + ref_plugin?: string; + ref_fallback_label?: string; + cascade_from?: string; + cascade_filter?: string; + validation?: PluginFieldValidation; +} + +export interface PluginRelationSchema { + entity: string; + foreign_key: string; + on_delete: 'cascade' | 'nullify' | 'restrict'; + name?: string; + type?: 'one_to_many' | 'many_to_one' | 'many_to_many'; + display_field?: string; +} + +export interface PluginEntitySchema { + name: string; + display_name: string; + fields: PluginFieldSchema[]; + relations?: PluginRelationSchema[]; + data_scope?: boolean; + is_public?: boolean; + importable?: boolean; + exportable?: boolean; +} + +export interface PluginSchemaResponse { + entities: PluginEntitySchema[]; + ui?: PluginUiSchema; + settings?: PluginSettings; + numbering?: PluginNumbering[]; + trigger_events?: PluginTriggerEvent[]; +} + +export interface PluginUiSchema { + pages: PluginPageSchema[]; +} + +export type PluginPageSchema = + | { type: 'crud'; entity: string; label: string; icon?: string; enable_search?: boolean; enable_views?: string[] } + | { type: 'tree'; entity: string; label: string; icon?: string; id_field: string; parent_field: string; label_field: string } + | { type: 'detail'; entity: string; label: string; sections: PluginSectionSchema[] } + | { type: 'tabs'; label: string; icon?: string; tabs: PluginPageSchema[] } + | { type: 'graph'; entity: string; label: string; relationship_entity: string; source_field: string; target_field: string; edge_label_field: string; node_label_field: string } + | { type: 'dashboard'; label: string; widgets?: DashboardWidget[] } + | { + type: 'kanban'; + entity: string; + label: string; + icon?: string; + lane_field: string; + lane_order?: string[]; + card_title_field: string; + card_subtitle_field?: string; + card_fields?: string[]; + enable_drag?: boolean; + }; + +export interface DashboardWidget { + type: 'stat_card' | 'bar_chart' | 'pie_chart' | 'funnel_chart' | 'line_chart' + | 'stat_cards' | 'action_list' | 'funnel' | 'card_list'; + entity: string; + title: string; + icon?: string; + color?: string; + dimension_field?: string; + dimension_order?: string[]; + metric?: string; + // stat_cards + cards?: StatCardDef[]; + // action_list + max_items?: number; + queries?: ActionQueryDef[]; + // funnel + lane_field?: string; + value_field?: string; + lane_order?: string[]; + // card_list + filter?: string; + title_field?: string; + subtitle_field?: string; + tags?: string[]; + label?: string; + label_field?: string; + action?: string; + sort?: string; +} + +export interface StatCardDef { + entity: string; + aggregate?: string; + field?: string; + filter?: string; + label: string; + icon?: string; + color?: string; +} + +export interface ActionQueryDef { + entity: string; + filter?: string; + sort?: string; + label_field: string; + subtitle_field?: string; + action: string; + icon?: string; +} + +export type PluginSectionSchema = + | { type: 'fields'; label: string; fields: string[] } + | { type: 'crud'; label: string; entity: string; filter_field?: string; enable_views?: string[] }; + +// ── P2 平台通用服务 — Settings 类型 ── + +export type PluginSettingType = + | 'text' | 'number' | 'boolean' | 'select' | 'multiselect' + | 'color' | 'date' | 'datetime' | 'json'; + +export interface PluginSettingField { + name: string; + display_name: string; + field_type: PluginSettingType; + default_value?: unknown; + required: boolean; + description?: string; + options?: { label: string; value: string }[]; + range?: [number, number]; + group?: string; +} + +export interface PluginSettings { + fields: PluginSettingField[]; +} + +// ── P2 平台通用服务 — Numbering 类型 ── + +export interface PluginNumbering { + entity: string; + field: string; + prefix: string; + format: string; + reset_rule: 'never' | 'daily' | 'monthly' | 'yearly'; + seq_length: number; + separator?: string; +} + +// ── P2 平台通用服务 — TriggerEvent 类型 ── + +export interface PluginTriggerEvent { + name: string; + display_name: string; + description: string; + entity: string; + on: 'create' | 'update' | 'delete' | 'create_or_update'; +} + +// ── 插件市场 API ── + +export interface MarketEntry { + id: string; + plugin_id: string; + name: string; + version: string; + description?: string; + author?: string; + category?: string; + tags?: string[]; + icon_url?: string; + screenshots?: string[]; + min_platform_version?: string; + status: string; + download_count: number; + rating_avg: number; + rating_count: number; + changelog?: string; + created_at?: string; + updated_at?: string; +} + +export interface MarketEntryDetail extends MarketEntry { + dependency_warnings: string[]; +} + +export interface MarketReview { + id: string; + user_id: string; + market_entry_id: string; + rating: number; + review_text?: string; + created_at?: string; +} + +export async function listMarketEntries(params?: { + page?: number; + page_size?: number; + category?: string; + search?: string; +}) { + const { data } = await client.get<{ success: boolean; data: PaginatedResponse }>( + '/market/entries', + { params }, + ); + return data.data; +} + +export async function getMarketEntry(id: string) { + const { data } = await client.get<{ success: boolean; data: MarketEntryDetail }>( + `/market/entries/${id}`, + ); + return data.data; +} + +export async function installFromMarket(id: string) { + const { data } = await client.post<{ success: boolean; data: PluginInfo }>( + `/market/entries/${id}/install`, + ); + return data.data; +} + +export async function listMarketReviews(id: string) { + const { data } = await client.get<{ success: boolean; data: MarketReview[] }>( + `/market/entries/${id}/reviews`, + ); + return data.data; +} + +export async function submitMarketReview(id: string, review: { rating: number; review_text?: string }) { + const { data } = await client.post<{ success: boolean; data: MarketReview }>( + `/market/entries/${id}/reviews`, + review, + ); + return data.data; +} diff --git a/apps/web/src/api/roles.test.ts b/apps/web/src/api/roles.test.ts new file mode 100644 index 0000000..ce58021 --- /dev/null +++ b/apps/web/src/api/roles.test.ts @@ -0,0 +1,86 @@ +/** + * roles API 契约测试 + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('./client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import * as rolesApi from './roles' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('roles API', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listRoles 应调用 GET /roles 并传递分页参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await rolesApi.listRoles(1, 10) + + expect(mockGet).toHaveBeenCalledWith('/roles', { params: { page: 1, page_size: 10 } }) + }) + + it('getRole 应调用 GET /roles/:id', async () => { + mockGet.mockResolvedValue(fakeRes) + await rolesApi.getRole('r-001') + + expect(mockGet).toHaveBeenCalledWith('/roles/r-001') + }) + + it('createRole 应调用 POST /roles', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { name: '医生', code: 'doctor', description: '医生角色' } + await rolesApi.createRole(req) + + expect(mockPost).toHaveBeenCalledWith('/roles', req) + }) + + it('updateRole 应调用 PUT /roles/:id', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { name: '高级医生', version: 1 } + await rolesApi.updateRole('r-001', req) + + expect(mockPut).toHaveBeenCalledWith('/roles/r-001', req) + }) + + it('deleteRole 应调用 DELETE /roles/:id', async () => { + mockDelete.mockResolvedValue(undefined) + await rolesApi.deleteRole('r-001') + + expect(mockDelete).toHaveBeenCalledWith('/roles/r-001') + }) + + it('assignPermissions 应调用 POST /roles/:id/permissions', async () => { + mockPost.mockResolvedValue(undefined) + await rolesApi.assignPermissions('r-001', ['p-1', 'p-2']) + + expect(mockPost).toHaveBeenCalledWith('/roles/r-001/permissions', { permission_ids: ['p-1', 'p-2'] }) + }) + + it('getRolePermissions 应调用 GET /roles/:id/permissions', async () => { + mockGet.mockResolvedValue(fakeRes) + await rolesApi.getRolePermissions('r-001') + + expect(mockGet).toHaveBeenCalledWith('/roles/r-001/permissions') + }) + + it('listPermissions 应调用 GET /permissions', async () => { + mockGet.mockResolvedValue(fakeRes) + await rolesApi.listPermissions() + + expect(mockGet).toHaveBeenCalledWith('/permissions') + }) +}) diff --git a/apps/web/src/api/roles.ts b/apps/web/src/api/roles.ts new file mode 100644 index 0000000..d1fd2cb --- /dev/null +++ b/apps/web/src/api/roles.ts @@ -0,0 +1,75 @@ +import client from './client'; +import type { PaginatedResponse } from './types'; + +export interface RoleInfo { + id: string; + name: string; + code: string; + description?: string; + is_system: boolean; + version: number; +} + +export interface PermissionInfo { + id: string; + code: string; + name: string; + resource: string; + action: string; + description?: string; +} + +export interface CreateRoleRequest { + name: string; + code: string; + description?: string; +} + +export interface UpdateRoleRequest { + name?: string; + description?: string; + version: number; +} + +export async function listRoles(page = 1, pageSize = 20) { + const { data } = await client.get<{ success: boolean; data: PaginatedResponse }>( + '/roles', + { params: { page, page_size: pageSize } }, + ); + return data.data; +} + +export async function getRole(id: string) { + const { data } = await client.get<{ success: boolean; data: RoleInfo }>(`/roles/${id}`); + return data.data; +} + +export async function createRole(req: CreateRoleRequest) { + const { data } = await client.post<{ success: boolean; data: RoleInfo }>('/roles', req); + return data.data; +} + +export async function updateRole(id: string, req: UpdateRoleRequest) { + const { data } = await client.put<{ success: boolean; data: RoleInfo }>(`/roles/${id}`, req); + return data.data; +} + +export async function deleteRole(id: string) { + await client.delete(`/roles/${id}`); +} + +export async function assignPermissions(roleId: string, permissionIds: string[]) { + await client.post(`/roles/${roleId}/permissions`, { permission_ids: permissionIds }); +} + +export async function getRolePermissions(roleId: string) { + const { data } = await client.get<{ success: boolean; data: PermissionInfo[] }>( + `/roles/${roleId}/permissions`, + ); + return data.data; +} + +export async function listPermissions() { + const { data } = await client.get<{ success: boolean; data: PermissionInfo[] }>('/permissions'); + return data.data; +} diff --git a/apps/web/src/api/settings.ts b/apps/web/src/api/settings.ts new file mode 100644 index 0000000..82539bc --- /dev/null +++ b/apps/web/src/api/settings.ts @@ -0,0 +1,30 @@ +import client from './client'; + +export interface SettingInfo { + id: string; + scope: string; + scope_id?: string; + setting_key: string; + setting_value: unknown; + version: number; +} + +export async function getSetting(key: string, scope?: string, scopeId?: string) { + const { data } = await client.get<{ success: boolean; data: SettingInfo }>( + `/config/settings/${encodeURIComponent(key)}`, + { params: { scope, scope_id: scopeId } }, + ); + return data.data; +} + +export async function updateSetting(key: string, settingValue: unknown, version?: number) { + const { data } = await client.put<{ success: boolean; data: SettingInfo }>( + `/config/settings/${encodeURIComponent(key)}`, + { setting_value: settingValue, version }, + ); + return data.data; +} + +export async function deleteSetting(key: string, version: number) { + await client.delete(`/config/settings/${encodeURIComponent(key)}`, { data: { version } }); +} diff --git a/apps/web/src/api/themes.ts b/apps/web/src/api/themes.ts new file mode 100644 index 0000000..4ab075e --- /dev/null +++ b/apps/web/src/api/themes.ts @@ -0,0 +1,49 @@ +import client from './client'; + +export interface ThemeConfig { + primary_color?: string; + logo_url?: string; + sidebar_style?: 'light' | 'dark'; + brand_name?: string; + brand_slogan?: string; + brand_features?: string; + brand_copyright?: string; +} + +export async function getTheme() { + const { data } = await client.get<{ success: boolean; data: ThemeConfig }>( + '/config/themes', + ); + return data.data; +} + +export async function updateTheme(theme: ThemeConfig) { + const { data } = await client.put<{ success: boolean; data: ThemeConfig }>( + '/config/themes', + theme, + ); + return data.data; +} + +export interface BrandConfig { + brand_name: string; + brand_slogan: string; + brand_features: string; + brand_copyright: string; +} + +const BRAND_DEFAULTS: BrandConfig = { + brand_name: 'HMS 健康管理平台', + brand_slogan: '新一代健康管理平台', + brand_features: '患者管理 · 健康监测 · 随访管理 · AI 智能分析', + brand_copyright: 'HMS 健康管理平台 · ©汕头市智界科技有限公司', +}; + +export async function getPublicBrand(): Promise { + try { + const res = await fetch('/api/v1/public/brand'); + const json = await res.json(); + if (json?.success && json?.data) return json.data; + } catch {} + return BRAND_DEFAULTS; +} diff --git a/apps/web/src/api/types.ts b/apps/web/src/api/types.ts new file mode 100644 index 0000000..8605eae --- /dev/null +++ b/apps/web/src/api/types.ts @@ -0,0 +1,7 @@ +export interface PaginatedResponse { + data: T[]; + total: number; + page: number; + page_size: number; + total_pages: number; +} diff --git a/apps/web/src/api/upload.ts b/apps/web/src/api/upload.ts new file mode 100644 index 0000000..1aa99ce --- /dev/null +++ b/apps/web/src/api/upload.ts @@ -0,0 +1,16 @@ +import client from './client'; + +export interface UploadResult { + url: string; + filename?: string; + size?: number; +} + +export async function uploadFile(file: File): Promise { + const formData = new FormData(); + formData.append('file', file); + const { data: result } = await client.post('/upload', formData, { + headers: { 'Content-Type': 'multipart/form-data' }, + }); + return result.data; +} diff --git a/apps/web/src/api/users.test.ts b/apps/web/src/api/users.test.ts new file mode 100644 index 0000000..31f86b1 --- /dev/null +++ b/apps/web/src/api/users.test.ts @@ -0,0 +1,83 @@ +/** + * users API 契约测试 + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('./client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import * as usersApi from './users' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('users API', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listUsers 应调用 GET /users 并传递分页和搜索参数', async () => { + mockGet.mockResolvedValue(fakeRes) + await usersApi.listUsers(2, 10, '张') + + expect(mockGet).toHaveBeenCalledWith('/users', { + params: { page: 2, page_size: 10, search: '张' }, + }) + }) + + it('listUsers 空搜索时应传 search 为 undefined', async () => { + mockGet.mockResolvedValue(fakeRes) + await usersApi.listUsers(1, 20, '') + + expect(mockGet).toHaveBeenCalledWith('/users', { + params: { page: 1, page_size: 20, search: undefined }, + }) + }) + + it('getUser 应调用 GET /users/:id', async () => { + mockGet.mockResolvedValue(fakeRes) + await usersApi.getUser('u-001') + + expect(mockGet).toHaveBeenCalledWith('/users/u-001') + }) + + it('createUser 应调用 POST /users', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { username: 'newuser', password: 'pass123', display_name: '新用户' } + await usersApi.createUser(req) + + expect(mockPost).toHaveBeenCalledWith('/users', req) + }) + + it('updateUser 应调用 PUT /users/:id', async () => { + mockPut.mockResolvedValue(fakeRes) + const req = { display_name: '改名', version: 1 } + await usersApi.updateUser('u-001', req) + + expect(mockPut).toHaveBeenCalledWith('/users/u-001', req) + }) + + it('deleteUser 应调用 DELETE /users/:id', async () => { + mockDelete.mockResolvedValue(undefined) + await usersApi.deleteUser('u-001') + + expect(mockDelete).toHaveBeenCalledWith('/users/u-001') + }) + + it('assignRoles 应调用 POST /users/:id/roles', async () => { + mockPost.mockResolvedValue(undefined) + await usersApi.assignRoles('u-001', ['role-1', 'role-2']) + + expect(mockPost).toHaveBeenCalledWith('/users/u-001/roles', { role_ids: ['role-1', 'role-2'] }) + }) +}) diff --git a/apps/web/src/api/users.ts b/apps/web/src/api/users.ts new file mode 100644 index 0000000..64b93c6 --- /dev/null +++ b/apps/web/src/api/users.ts @@ -0,0 +1,54 @@ +import client from './client'; +import type { UserInfo } from './auth'; +import type { PaginatedResponse } from './types'; + +export interface CreateUserRequest { + username: string; + password: string; + email?: string; + phone?: string; + display_name?: string; +} + +export interface UpdateUserRequest { + email?: string; + phone?: string; + display_name?: string; + status?: string; + version: number; +} + +export async function listUsers(page = 1, pageSize = 20, search = '') { + const { data } = await client.get<{ success: boolean; data: PaginatedResponse }>( + '/users', + { params: { page, page_size: pageSize, search: search || undefined } } + ); + return data.data; +} + +export async function getUser(id: string) { + const { data } = await client.get<{ success: boolean; data: UserInfo }>(`/users/${id}`); + return data.data; +} + +export async function createUser(req: CreateUserRequest) { + const { data } = await client.post<{ success: boolean; data: UserInfo }>('/users', req); + return data.data; +} + +export async function updateUser(id: string, req: UpdateUserRequest) { + const { data } = await client.put<{ success: boolean; data: UserInfo }>(`/users/${id}`, req); + return data.data; +} + +export async function deleteUser(id: string) { + await client.delete(`/users/${id}`); +} + +export async function assignRoles(userId: string, roleIds: string[]) { + await client.post(`/users/${userId}/roles`, { role_ids: roleIds }); +} + +export async function resetPassword(id: string, req: { new_password: string; version: number }) { + await client.post(`/users/${id}/reset-password`, req); +} diff --git a/apps/web/src/api/workflow.test.ts b/apps/web/src/api/workflow.test.ts new file mode 100644 index 0000000..d2c262a --- /dev/null +++ b/apps/web/src/api/workflow.test.ts @@ -0,0 +1,141 @@ +/** + * workflow API 契约测试(definitions + instances + tasks) + */ +import { describe, it, expect, vi, beforeEach } from 'vitest' + +const mockGet = vi.fn() +const mockPost = vi.fn() +const mockPut = vi.fn() +const mockDelete = vi.fn() + +vi.mock('./client', () => ({ + default: { + get: (...args: unknown[]) => mockGet(...args), + post: (...args: unknown[]) => mockPost(...args), + put: (...args: unknown[]) => mockPut(...args), + delete: (...args: unknown[]) => mockDelete(...args), + }, +})) + +import * as defApi from './workflowDefinitions' +import * as instApi from './workflowInstances' +import * as taskApi from './workflowTasks' + +beforeEach(() => { + vi.clearAllMocks() +}) + +describe('workflowDefinitions API', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listProcessDefinitions 应调用 GET /workflow/definitions', async () => { + mockGet.mockResolvedValue(fakeRes) + await defApi.listProcessDefinitions(1, 10) + + expect(mockGet).toHaveBeenCalledWith('/workflow/definitions', { + params: { page: 1, page_size: 10 }, + }) + }) + + it('getProcessDefinition 应调用 GET /workflow/definitions/:id', async () => { + mockGet.mockResolvedValue(fakeRes) + await defApi.getProcessDefinition('wf-001') + + expect(mockGet).toHaveBeenCalledWith('/workflow/definitions/wf-001') + }) + + it('createProcessDefinition 应调用 POST /workflow/definitions', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { name: '审批流程', key: 'approval', nodes: [], edges: [] } + await defApi.createProcessDefinition(req) + + expect(mockPost).toHaveBeenCalledWith('/workflow/definitions', req) + }) + + it('publishProcessDefinition 应调用 POST /workflow/definitions/:id/publish', async () => { + mockPost.mockResolvedValue(fakeRes) + await defApi.publishProcessDefinition('wf-001') + + expect(mockPost).toHaveBeenCalledWith('/workflow/definitions/wf-001/publish') + }) +}) + +describe('workflowInstances API', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('startInstance 应调用 POST /workflow/instances', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { definition_id: 'wf-001', business_key: 'BIZ-001' } + await instApi.startInstance(req) + + expect(mockPost).toHaveBeenCalledWith('/workflow/instances', req) + }) + + it('listInstances 应调用 GET /workflow/instances 并传递分页', async () => { + mockGet.mockResolvedValue(fakeRes) + await instApi.listInstances(1, 10) + + expect(mockGet).toHaveBeenCalledWith('/workflow/instances', { + params: { page: 1, page_size: 10 }, + }) + }) + + it('suspendInstance 应调用 POST /workflow/instances/:id/suspend', async () => { + mockPost.mockResolvedValue(fakeRes) + await instApi.suspendInstance('inst-001') + + expect(mockPost).toHaveBeenCalledWith('/workflow/instances/inst-001/suspend') + }) + + it('resumeInstance 应调用 POST /workflow/instances/:id/resume', async () => { + mockPost.mockResolvedValue(fakeRes) + await instApi.resumeInstance('inst-001') + + expect(mockPost).toHaveBeenCalledWith('/workflow/instances/inst-001/resume') + }) + + it('terminateInstance 应调用 POST /workflow/instances/:id/terminate', async () => { + mockPost.mockResolvedValue(fakeRes) + await instApi.terminateInstance('inst-001') + + expect(mockPost).toHaveBeenCalledWith('/workflow/instances/inst-001/terminate') + }) +}) + +describe('workflowTasks API', () => { + const fakeRes = { data: { success: true, data: {} } } + + it('listPendingTasks 应调用 GET /workflow/tasks/pending', async () => { + mockGet.mockResolvedValue(fakeRes) + await taskApi.listPendingTasks(1, 10) + + expect(mockGet).toHaveBeenCalledWith('/workflow/tasks/pending', { + params: { page: 1, page_size: 10 }, + }) + }) + + it('listCompletedTasks 应调用 GET /workflow/tasks/completed', async () => { + mockGet.mockResolvedValue(fakeRes) + await taskApi.listCompletedTasks(1, 10) + + expect(mockGet).toHaveBeenCalledWith('/workflow/tasks/completed', { + params: { page: 1, page_size: 10 }, + }) + }) + + it('completeTask 应调用 POST /workflow/tasks/:id/complete', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { outcome: 'approved', form_data: { comment: '同意' } } + await taskApi.completeTask('task-001', req) + + expect(mockPost).toHaveBeenCalledWith('/workflow/tasks/task-001/complete', req) + }) + + it('delegateTask 应调用 POST /workflow/tasks/:id/delegate', async () => { + mockPost.mockResolvedValue(fakeRes) + const req = { delegate_to: 'u-002' } + await taskApi.delegateTask('task-001', req) + + expect(mockPost).toHaveBeenCalledWith('/workflow/tasks/task-001/delegate', req) + }) +}) diff --git a/apps/web/src/api/workflowDefinitions.ts b/apps/web/src/api/workflowDefinitions.ts new file mode 100644 index 0000000..4490fd3 --- /dev/null +++ b/apps/web/src/api/workflowDefinitions.ts @@ -0,0 +1,90 @@ +import client from './client'; +import type { PaginatedResponse } from './types'; + +export interface NodeDef { + id: string; + type: 'StartEvent' | 'EndEvent' | 'UserTask' | 'ServiceTask' | 'ExclusiveGateway' | 'ParallelGateway'; + name: string; + assignee_id?: string; + candidate_groups?: string[]; + service_type?: string; + position?: { x: number; y: number }; +} + +export interface EdgeDef { + id: string; + source: string; + target: string; + condition?: string; + label?: string; +} + +export interface ProcessDefinitionInfo { + id: string; + name: string; + key: string; + version: number; + category?: string; + description?: string; + nodes: NodeDef[]; + edges: EdgeDef[]; + status: string; + created_at: string; + updated_at: string; +} + +export interface CreateProcessDefinitionRequest { + name: string; + key: string; + category?: string; + description?: string; + nodes: NodeDef[]; + edges: EdgeDef[]; +} + +export interface UpdateProcessDefinitionRequest { + name?: string; + category?: string; + description?: string; + nodes?: NodeDef[]; + edges?: EdgeDef[]; + version: number; +} + +export async function listProcessDefinitions(page = 1, pageSize = 20) { + const { data } = await client.get<{ success: boolean; data: PaginatedResponse }>( + '/workflow/definitions', + { params: { page, page_size: pageSize } }, + ); + return data.data; +} + +export async function getProcessDefinition(id: string) { + const { data } = await client.get<{ success: boolean; data: ProcessDefinitionInfo }>( + `/workflow/definitions/${id}`, + ); + return data.data; +} + +export async function createProcessDefinition(req: CreateProcessDefinitionRequest) { + const { data } = await client.post<{ success: boolean; data: ProcessDefinitionInfo }>( + '/workflow/definitions', + req, + ); + return data.data; +} + +export async function updateProcessDefinition(id: string, req: UpdateProcessDefinitionRequest) { + const { data } = await client.put<{ success: boolean; data: ProcessDefinitionInfo }>( + `/workflow/definitions/${id}`, + req, + ); + return data.data; +} + +export async function publishProcessDefinition(id: string) { + const { data } = await client.post<{ success: boolean; data: ProcessDefinitionInfo }>( + `/workflow/definitions/${id}/publish`, + ); + return data.data; +} diff --git a/apps/web/src/api/workflowInstances.ts b/apps/web/src/api/workflowInstances.ts new file mode 100644 index 0000000..7b99f47 --- /dev/null +++ b/apps/web/src/api/workflowInstances.ts @@ -0,0 +1,72 @@ +import client from './client'; +import type { PaginatedResponse } from './types'; + +export interface TokenInfo { + id: string; + node_id: string; + status: string; + created_at: string; +} + +export interface ProcessInstanceInfo { + id: string; + definition_id: string; + definition_name?: string; + business_key?: string; + status: string; + started_by: string; + started_at: string; + completed_at?: string; + created_at: string; + active_tokens: TokenInfo[]; +} + +export interface StartInstanceRequest { + definition_id: string; + business_key?: string; + variables?: Array<{ name: string; var_type?: string; value: unknown }>; +} + +export async function startInstance(req: StartInstanceRequest) { + const { data } = await client.post<{ success: boolean; data: ProcessInstanceInfo }>( + '/workflow/instances', + req, + ); + return data.data; +} + +export async function listInstances(page = 1, pageSize = 20) { + const { data } = await client.get<{ success: boolean; data: PaginatedResponse }>( + '/workflow/instances', + { params: { page, page_size: pageSize } }, + ); + return data.data; +} + +export async function getInstance(id: string) { + const { data } = await client.get<{ success: boolean; data: ProcessInstanceInfo }>( + `/workflow/instances/${id}`, + ); + return data.data; +} + +export async function suspendInstance(id: string) { + const { data } = await client.post<{ success: boolean; data: null }>( + `/workflow/instances/${id}/suspend`, + ); + return data.data; +} + +export async function resumeInstance(id: string) { + const { data } = await client.post<{ success: boolean; data: null }>( + `/workflow/instances/${id}/resume`, + ); + return data.data; +} + +export async function terminateInstance(id: string) { + const { data } = await client.post<{ success: boolean; data: null }>( + `/workflow/instances/${id}/terminate`, + ); + return data.data; +} diff --git a/apps/web/src/api/workflowTasks.ts b/apps/web/src/api/workflowTasks.ts new file mode 100644 index 0000000..c5b47cb --- /dev/null +++ b/apps/web/src/api/workflowTasks.ts @@ -0,0 +1,61 @@ +import client from './client'; +import type { PaginatedResponse } from './types'; + +export interface TaskInfo { + id: string; + instance_id: string; + token_id: string; + node_id: string; + node_name?: string; + assignee_id?: string; + candidate_groups?: unknown; + status: string; + outcome?: string; + form_data?: unknown; + due_date?: string; + completed_at?: string; + created_at: string; + definition_name?: string; + business_key?: string; +} + +export interface CompleteTaskRequest { + outcome: string; + form_data?: Record; +} + +export interface DelegateTaskRequest { + delegate_to: string; +} + +export async function listPendingTasks(page = 1, pageSize = 20) { + const { data } = await client.get<{ success: boolean; data: PaginatedResponse }>( + '/workflow/tasks/pending', + { params: { page, page_size: pageSize } }, + ); + return data.data; +} + +export async function listCompletedTasks(page = 1, pageSize = 20) { + const { data } = await client.get<{ success: boolean; data: PaginatedResponse }>( + '/workflow/tasks/completed', + { params: { page, page_size: pageSize } }, + ); + return data.data; +} + +export async function completeTask(id: string, req: CompleteTaskRequest) { + const { data } = await client.post<{ success: boolean; data: TaskInfo }>( + `/workflow/tasks/${id}/complete`, + req, + ); + return data.data; +} + +export async function delegateTask(id: string, req: DelegateTaskRequest) { + const { data } = await client.post<{ success: boolean; data: TaskInfo }>( + `/workflow/tasks/${id}/delegate`, + req, + ); + return data.data; +} diff --git a/apps/web/src/components/ActionThreadDrawer.tsx b/apps/web/src/components/ActionThreadDrawer.tsx new file mode 100644 index 0000000..0f986c9 --- /dev/null +++ b/apps/web/src/components/ActionThreadDrawer.tsx @@ -0,0 +1,226 @@ +import { useCallback, useEffect, useState } from 'react'; +import { Drawer, Timeline, Button, Spin, Result, Space, Tag } from 'antd'; +import { + CheckCircleOutlined, + ClockCircleOutlined, + MinusCircleOutlined, + CloseCircleOutlined, + UserOutlined, +} from '@ant-design/icons'; +import { useNavigate } from 'react-router-dom'; +import { + actionInboxApi, + type ActionItem, + type ThreadResponse, + type ActionPriority, +} from '../api/health/actionInbox'; +import client from '../api/client'; + +interface Props { + open: boolean; + item: ActionItem | null; + onClose: () => void; + onActionComplete?: () => void; +} + +const PRIORITY_COLOR: Record = { + urgent: 'red', + high: 'orange', + medium: 'blue', + low: 'default', +}; + +const PRIORITY_LABEL: Record = { + urgent: '紧急', + high: '高', + medium: '中', + low: '低', +}; + +export default function ActionThreadDrawer({ + open, + item, + onClose, + onActionComplete, +}: Props) { + const navigate = useNavigate(); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [threadData, setThreadData] = useState(null); + const [actionLoading, setActionLoading] = useState(null); + + const fetchThread = useCallback(async () => { + if (!item) return; + setLoading(true); + setError(null); + try { + const data = await actionInboxApi.getThread(item.source_ref); + setThreadData(data); + } catch (e: unknown) { + const msg = e instanceof Error ? e.message : '获取线程失败'; + setError(msg); + } finally { + setLoading(false); + } + }, [item]); + + useEffect(() => { + if (open && item) fetchThread(); + if (!open) setThreadData(null); + }, [open, item, fetchThread]); + + const handleAction = async (endpoint: string, key: string) => { + setActionLoading(key); + try { + await client.post(endpoint, { action: key }); + onActionComplete?.(); + fetchThread(); + } catch { + // 全局拦截器处理 + } finally { + setActionLoading(null); + } + }; + + const handleLinkClick = (linkTo?: string) => { + if (linkTo) { + onClose(); + navigate(linkTo); + } + }; + + const timelineDot = (status: string) => { + switch (status) { + case 'completed': + return ; + case 'in_progress': + return ; + case 'dismissed': + return ; + default: + return ; + } + }; + + return ( + + {loading && ( +
+ +
+ )} + + {error && ( +
+ +
+ )} + + {threadData && !loading && ( + <> +
+
+ {threadData.action_item.title} +
+
+ {threadData.action_item.patient_name} + + {PRIORITY_LABEL[threadData.action_item.priority]} + +
+
+ +
+
+ 处理进度 +
+ ({ + color: + evt.status === 'completed' + ? 'green' + : evt.status === 'in_progress' + ? 'blue' + : 'gray', + dot: timelineDot(evt.status), + children: ( +
+
+ {evt.label} +
+ {evt.detail && ( +
+ {evt.detail} +
+ )} + {evt.timestamp && ( +
+ {new Date(evt.timestamp).toLocaleString('zh-CN')} +
+ )} + {evt.link_to && ( + handleLinkClick(evt.link_to)} + style={{ fontSize: 12 }} + > + 查看详情 → + + )} +
+ ), + }))} + /> +
+ + {threadData.available_actions.length > 0 && ( +
+ + {threadData.available_actions.map((action) => ( + + ))} + +
+ )} + + )} +
+ ); +} diff --git a/apps/web/src/components/AuthButton.tsx b/apps/web/src/components/AuthButton.tsx new file mode 100644 index 0000000..31b9c81 --- /dev/null +++ b/apps/web/src/components/AuthButton.tsx @@ -0,0 +1,13 @@ +import type { ReactNode } from 'react'; +import { usePermission } from '../hooks/usePermission'; + +interface AuthButtonProps { + code: string; + children: ReactNode; +} + +export function AuthButton({ code, children }: AuthButtonProps) { + const { hasPermission } = usePermission(code); + if (!hasPermission) return null; + return <>{children}; +} diff --git a/apps/web/src/components/Copilot/CopilotAlert.tsx b/apps/web/src/components/Copilot/CopilotAlert.tsx new file mode 100644 index 0000000..b61a69a --- /dev/null +++ b/apps/web/src/components/Copilot/CopilotAlert.tsx @@ -0,0 +1,106 @@ +import { useState, useEffect, useCallback } from 'react'; +import { Alert, Badge, List, Button, Space, Typography, Spin } from 'antd'; +import { CheckOutlined } from '@ant-design/icons'; +import { listAlerts, dismissInsight } from '../../api/copilot'; +import type { CopilotInsight } from '../../api/copilot'; + +const severityConfig: Record = { + critical: { type: 'error', label: '危急' }, + warning: { type: 'warning', label: '警告' }, + info: { type: 'processing', label: '提示' }, +}; + +export function CopilotAlert() { + const [alerts, setAlerts] = useState([]); + const [loading, setLoading] = useState(false); + const [dismissing, setDismissing] = useState(null); + + const fetchAlerts = useCallback(async () => { + setLoading(true); + try { + const res = await listAlerts({ page_size: 50 }); + const payload = (res.data as { data?: CopilotInsight[] }).data ?? []; + setAlerts(payload); + } catch { + // 静默 + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + fetchAlerts(); + const timer = setInterval(fetchAlerts, 30_000); + return () => clearInterval(timer); + }, [fetchAlerts]); + + const handleDismiss = async (id: string) => { + setDismissing(id); + try { + await dismissInsight(id); + setAlerts((prev) => prev.filter((a) => a.id !== id)); + } finally { + setDismissing(null); + } + }; + + if (!alerts.length && !loading) return null; + + const criticalCount = alerts.filter((a) => a.severity === 'critical').length; + + return ( +
+ {criticalCount > 0 && ( + + )} + {loading && alerts.length === 0 ? ( + + ) : ( + { + const config = severityConfig[item.severity] ?? severityConfig.info; + return ( + } + loading={dismissing === item.id} + onClick={() => handleDismiss(item.id)} + > + 已知悉 + , + ]} + > + + + {item.title} + + } + description={ + item.content?.suggestion ? ( + + {item.content.suggestion as string} + + ) : undefined + } + /> + + ); + }} + /> + )} +
+ ); +} diff --git a/apps/web/src/components/Copilot/CopilotBadge.tsx b/apps/web/src/components/Copilot/CopilotBadge.tsx new file mode 100644 index 0000000..827a50f --- /dev/null +++ b/apps/web/src/components/Copilot/CopilotBadge.tsx @@ -0,0 +1,28 @@ +import { Tag, Tooltip } from 'antd'; +import type { RiskLevel } from '../../api/copilot'; +import { useCopilotRisk } from './useCopilotRisk'; + +const levelConfig: Record = { + low: { color: 'green', label: '低风险' }, + medium: { color: 'orange', label: '中风险' }, + high: { color: 'red', label: '高风险' }, + critical: { color: '#cf1322', label: '危急' }, +}; + +interface CopilotBadgeProps { + patientId: string | undefined; +} + +export function CopilotBadge({ patientId }: CopilotBadgeProps) { + const { data, loading } = useCopilotRisk(patientId); + + if (!data || loading) return null; + + const config = levelConfig[data.level as RiskLevel] ?? levelConfig.low; + + return ( + + {config.label} {data.score}/10 + + ); +} diff --git a/apps/web/src/components/Copilot/CopilotCard.tsx b/apps/web/src/components/Copilot/CopilotCard.tsx new file mode 100644 index 0000000..24abc5f --- /dev/null +++ b/apps/web/src/components/Copilot/CopilotCard.tsx @@ -0,0 +1,76 @@ +import { Card, List, Tag, Button, Empty, Spin, Typography } from 'antd'; +import { CloseOutlined } from '@ant-design/icons'; +import { useState } from 'react'; +import { dismissInsight } from '../../api/copilot'; +import type { CopilotInsight } from '../../api/copilot'; +import { useCopilotInsights } from './useCopilotInsights'; + +const severityColor: Record = { + info: 'blue', + warning: 'orange', + critical: 'red', +}; + +interface CopilotCardProps { + patientId: string | undefined; +} + +export function CopilotCard({ patientId }: CopilotCardProps) { + const { data, loading, refresh } = useCopilotInsights(patientId); + const [dismissing, setDismissing] = useState(null); + + const handleDismiss = async (id: string) => { + setDismissing(id); + try { + await dismissInsight(id); + refresh(); + } finally { + setDismissing(null); + } + }; + + return ( + + {loading ? ( + + ) : data.length === 0 ? ( + + ) : ( + ( + } + loading={dismissing === item.id} + onClick={() => handleDismiss(item.id)} + />, + ]} + > + + {item.severity} + {item.title} + + } + description={ + item.llm_supplement ? ( + + {item.llm_supplement} + + ) : undefined + } + /> + + )} + /> + )} + + ); +} diff --git a/apps/web/src/components/Copilot/index.ts b/apps/web/src/components/Copilot/index.ts new file mode 100644 index 0000000..888a35b --- /dev/null +++ b/apps/web/src/components/Copilot/index.ts @@ -0,0 +1,5 @@ +export { CopilotBadge } from './CopilotBadge'; +export { CopilotCard } from './CopilotCard'; +export { CopilotAlert } from './CopilotAlert'; +export { useCopilotRisk } from './useCopilotRisk'; +export { useCopilotInsights } from './useCopilotInsights'; diff --git a/apps/web/src/components/Copilot/useCopilotInsights.ts b/apps/web/src/components/Copilot/useCopilotInsights.ts new file mode 100644 index 0000000..5f319b5 --- /dev/null +++ b/apps/web/src/components/Copilot/useCopilotInsights.ts @@ -0,0 +1,31 @@ +import { useState, useEffect, useCallback } from 'react'; +import { listInsights } from '../../api/copilot'; +import type { CopilotInsight } from '../../api/copilot'; + +export function useCopilotInsights(patientId: string | undefined) { + const [data, setData] = useState([]); + const [total, setTotal] = useState(0); + const [loading, setLoading] = useState(false); + + const fetch = useCallback(async () => { + if (!patientId) return; + setLoading(true); + try { + const res = await listInsights({ patient_id: patientId, page_size: 20 }); + const payload = (res.data as { data?: CopilotInsight[]; total?: number }).data ?? []; + const total = (res.data as { total?: number }).total ?? 0; + setData(payload); + setTotal(total); + } catch { + // 静默失败 + } finally { + setLoading(false); + } + }, [patientId]); + + useEffect(() => { + fetch(); + }, [fetch]); + + return { data, total, loading, refresh: fetch }; +} diff --git a/apps/web/src/components/Copilot/useCopilotRisk.ts b/apps/web/src/components/Copilot/useCopilotRisk.ts new file mode 100644 index 0000000..38243c8 --- /dev/null +++ b/apps/web/src/components/Copilot/useCopilotRisk.ts @@ -0,0 +1,29 @@ +import { useState, useEffect, useCallback } from 'react'; +import { getPatientRisk } from '../../api/copilot'; +import type { RiskScore } from '../../api/copilot'; + +export function useCopilotRisk(patientId: string | undefined) { + const [data, setData] = useState(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const fetch = useCallback(async () => { + if (!patientId) return; + setLoading(true); + setError(null); + try { + const res = await getPatientRisk(patientId); + setData(res ?? null); + } catch (err) { + setError(err instanceof Error ? err.message : '加载风险评分失败'); + } finally { + setLoading(false); + } + }, [patientId]); + + useEffect(() => { + fetch(); + }, [fetch]); + + return { data, loading, error, refresh: fetch }; +} diff --git a/apps/web/src/components/DrawerForm.tsx b/apps/web/src/components/DrawerForm.tsx new file mode 100644 index 0000000..dbb65cd --- /dev/null +++ b/apps/web/src/components/DrawerForm.tsx @@ -0,0 +1,107 @@ +import React from 'react'; +import { Drawer, Form, Typography, Divider, Button, Space } from 'antd'; +import { useThemeMode } from '../hooks/useThemeMode'; + +export interface FormSection { + title: string; + fields: React.ReactNode; + defaultCollapsed?: boolean; +} + +interface DrawerFormProps { + title: string; + open: boolean; + onClose: () => void; + onSubmit: (values: Record) => Promise; + initialValues?: Record; + loading?: boolean; + width?: number | string; + sections?: FormSection[]; + children?: React.ReactNode; + columns?: 1 | 2; + form?: ReturnType[0]; + onValuesChange?: (changedValues: Record, allValues: Record) => void; +} + +export function DrawerForm({ + title, + open, + onClose, + onSubmit, + initialValues, + loading, + width = 640, + sections, + children, + columns = 2, + form: externalForm, + onValuesChange, +}: DrawerFormProps) { + const [internalForm] = Form.useForm(); + const form = externalForm ?? internalForm; + const isDark = useThemeMode(); + + React.useEffect(() => { + if (open) { + form.resetFields(); + if (initialValues) { + form.setFieldsValue(initialValues); + } + } + }, [open, initialValues, form]); + + const handleSubmit = async () => { + try { + const values = await form.validateFields(); + await onSubmit(values); + } catch (error: unknown) { + // validateFields 失败时 error 包含 errorFields(预期行为,不记录) + // 其他类型的错误才记录 + if (error && typeof error === 'object' && !('errorFields' in error)) { + console.error('[DrawerForm] submit error:', error); + } + } + }; + + const gridStyle: React.CSSProperties = + columns === 2 + ? { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0 16px' } + : {}; + + return ( + + + + + } + > +
+ {sections + ? sections.map((s, i) => ( +
+ {i > 0 && } + + {s.title} + +
{s.fields}
+
+ )) + : children &&
{children}
} +
+
+ ); +} diff --git a/apps/web/src/components/EntityName.tsx b/apps/web/src/components/EntityName.tsx new file mode 100644 index 0000000..d3c8562 --- /dev/null +++ b/apps/web/src/components/EntityName.tsx @@ -0,0 +1,23 @@ +import { Tooltip, Typography } from 'antd'; + +interface EntityNameProps { + name?: string | null; + id?: string; + fallbackLabel?: string; +} + +export function EntityName({ name, id, fallbackLabel = '未知' }: EntityNameProps) { + if (name !== undefined && name !== null && name !== '') return {name}; + + if (id) { + return ( + + + {fallbackLabel} + + + ); + } + + return {fallbackLabel}; +} diff --git a/apps/web/src/components/EntitySelect.tsx b/apps/web/src/components/EntitySelect.tsx new file mode 100644 index 0000000..b5f1795 --- /dev/null +++ b/apps/web/src/components/EntitySelect.tsx @@ -0,0 +1,141 @@ +import { Select, Spin, Input, Tooltip } from 'antd'; +import { QuestionCircleOutlined } from '@ant-design/icons'; +import { useState, useEffect, useCallback } from 'react'; +import { listPluginData, getPluginEntityRegistry } from '../api/pluginData'; + +interface EntitySelectProps { + pluginId: string; + entity: string; + labelField: string; + searchFields?: string[]; + /** 跨插件引用的目标插件 manifest ID(如 "erp-crm") */ + refPlugin?: string; + /** 目标插件未安装时的降级显示文本 */ + fallbackLabel?: string; + value?: string; + onChange?: (value: string, label: string) => void; + cascadeFrom?: string; + cascadeFilter?: string; + cascadeValue?: string; + placeholder?: string; +} + +export default function EntitySelect({ + pluginId, + entity, + labelField, + searchFields: _searchFields, + refPlugin, + fallbackLabel, + value, + onChange, + cascadeFrom, + cascadeFilter, + cascadeValue, + placeholder, +}: EntitySelectProps) { + const [options, setOptions] = useState<{ value: string; label: string }[]>([]); + const [loading, setLoading] = useState(false); + const [targetUnavailable, setTargetUnavailable] = useState(false); + const [resolvedPluginId, setResolvedPluginId] = useState(null); + + // 跨插件时:先解析 manifest_id → plugin UUID + useEffect(() => { + if (!refPlugin) { + setResolvedPluginId(pluginId); + return; + } + let cancelled = false; + (async () => { + try { + const registry = await getPluginEntityRegistry(); + const match = registry.find((e) => e.manifest_id === refPlugin && e.entity_name === entity); + if (!cancelled) { + setResolvedPluginId(match ? match.plugin_id : null); + if (!match) setTargetUnavailable(true); + } + } catch { + if (!cancelled) { + setTargetUnavailable(true); + setResolvedPluginId(null); + } + } + })(); + return () => { cancelled = true; }; + }, [refPlugin, pluginId, entity]); + + const effectivePluginId = resolvedPluginId || pluginId; + + const fetchData = useCallback( + async (keyword?: string) => { + if (!resolvedPluginId && refPlugin) return; + setLoading(true); + try { + const filter: Record | undefined = + cascadeFrom && cascadeFilter && cascadeValue + ? { [cascadeFilter]: cascadeValue } + : undefined; + + const result = await listPluginData(effectivePluginId, entity, 1, 20, { + search: keyword, + filter, + }); + + const items = (result.data || []).map((item) => ({ + value: item.id, + label: String(item.data?.[labelField] ?? item.id), + })); + setOptions(items); + setTargetUnavailable(false); + } catch { + if (refPlugin) { + setTargetUnavailable(true); + setOptions([]); + } + } finally { + setLoading(false); + } + }, + [effectivePluginId, entity, labelField, cascadeFrom, cascadeFilter, cascadeValue, refPlugin, resolvedPluginId], + ); + + useEffect(() => { + if (resolvedPluginId || !refPlugin) { + fetchData(); + } + }, [fetchData, resolvedPluginId, refPlugin]); + + // 目标插件未安装 → 降级显示 + if (targetUnavailable) { + return ( + + + + } + /> + ); + } + + return ( + } + placeholder="搜索文件名..." + value={keyword} + onChange={(e) => { setKeyword(e.target.value); setPage(1); }} + allowClear + style={{ flex: 1 }} + /> + { handleUpload(file); return false; }} + > + + + + + {loading ? ( +
+ ) : items.length === 0 ? ( + + ) : ( + <> +
+ {items.map((item) => ( +
handleSelect(item)} + style={{ + cursor: 'pointer', + borderRadius: 8, + overflow: 'hidden', + border: '1px solid #f0f0f0', + background: '#fafafa', + transition: 'border-color 0.2s', + }} + onMouseEnter={(e) => { (e.currentTarget.style.borderColor = '#1677ff'); }} + onMouseLeave={(e) => { (e.currentTarget.style.borderColor = '#f0f0f0'); }} + > +
+ {item.content_type.startsWith('image/') ? ( + {item.alt_text + ) : ( + {item.content_type.split('/')[1]} + )} +
+
+ {item.filename} +
+
+ ))} +
+ {totalPages > 1 && ( +
+ + {page} / {totalPages} + +
+ )} + + )} + + ); +} diff --git a/apps/web/src/components/NotificationPanel.tsx b/apps/web/src/components/NotificationPanel.tsx new file mode 100644 index 0000000..82f9013 --- /dev/null +++ b/apps/web/src/components/NotificationPanel.tsx @@ -0,0 +1,248 @@ +import { useCallback, useEffect, useRef, useState } from 'react'; +import { Badge, Divider, List, Popover, Button, Empty, Typography } from 'antd'; +import { BellOutlined } from '@ant-design/icons'; +import { useNavigate } from 'react-router-dom'; +import { useMessageStore } from '../stores/message'; +import { useThemeMode } from '../hooks/useThemeMode'; +import { actionInboxApi, type ActionItem } from '../api/health/actionInbox'; + +const { Text } = Typography; + +export default function NotificationPanel() { + const navigate = useNavigate(); + const unreadCount = useMessageStore((s) => s.unreadCount); + const recentMessages = useMessageStore((s) => s.recentMessages); + const markAsRead = useMessageStore((s) => s.markAsRead); + const isDark = useThemeMode(); + const initializedRef = useRef(false); + + const [pendingActions, setPendingActions] = useState([]); + + const fetchPendingActions = useCallback(async () => { + try { + const resp = await actionInboxApi.list({ status: 'pending', page_size: 3 }); + setPendingActions(resp.data); + } catch { + // 静默失败,不影响通知面板 + } + }, []); + + useEffect(() => { + if (initializedRef.current) return; + initializedRef.current = true; + + const { fetchUnreadCount, fetchRecentMessages, connectSSE } = useMessageStore.getState(); + fetchUnreadCount(); + fetchRecentMessages(); + fetchPendingActions(); + + const disconnectSSE = connectSSE(); + + const interval = setInterval(() => { + fetchUnreadCount(); + fetchRecentMessages(); + fetchPendingActions(); + }, 60000); + + return () => { + clearInterval(interval); + disconnectSSE(); + initializedRef.current = false; + }; + }, [fetchPendingActions]); + + const totalBadge = unreadCount + pendingActions.length; + + const content = ( +
+
+ 通知 + {unreadCount > 0 && ( + + )} +
+ + {recentMessages.length === 0 ? ( + + ) : ( + ( + { + if (!item.is_read) { + markAsRead(item.id); + } + navigate('/messages'); + }} + onMouseEnter={(e) => { + if (item.is_read) { + e.currentTarget.style.background = isDark ? '#0f172a' : '#f1f5f9'; + } + }} + onMouseLeave={(e) => { + if (item.is_read) { + e.currentTarget.style.background = 'transparent'; + } + }} + > +
+
+ + {item.title} + + {!item.is_read && ( + + )} +
+ + {item.body} + +
+
+ )} + /> + )} + + {recentMessages.length > 0 && ( +
+ +
+ )} + + {/* 待办预览区域 */} + +
+
+ 待办事项 + +
+ {pendingActions.length === 0 ? ( + + 暂无待办 + + ) : ( + ( + navigate('/health/action-inbox')} + > + {item.title}} + description={ + + {item.patient_name} + + } + /> + + )} + /> + )} +
+
+ ); + + return ( + +
{ + e.currentTarget.style.background = isDark ? '#0f172a' : '#f8fafc'; + }} + onMouseLeave={(e) => { + e.currentTarget.style.background = 'transparent'; + }} + > + + + +
+
+ ); +} diff --git a/apps/web/src/components/PageContainer.tsx b/apps/web/src/components/PageContainer.tsx new file mode 100644 index 0000000..af08451 --- /dev/null +++ b/apps/web/src/components/PageContainer.tsx @@ -0,0 +1,79 @@ +import { Card, Flex, Space, Typography, Button } from 'antd'; +import { useThemeMode } from '../hooks/useThemeMode'; +import { FilterBar } from './FilterBar'; + +interface PageContainerProps { + title: string; + subtitle?: string; + filters?: React.ReactNode; + onResetFilters?: () => void; + filterExtra?: React.ReactNode; + actions?: React.ReactNode; + batchActions?: React.ReactNode; + selectedCount?: number; + onClearSelection?: () => void; + onBack?: () => void; + children: React.ReactNode; + loading?: boolean; +} + +export function PageContainer({ + title, + subtitle, + filters, + onResetFilters, + filterExtra, + actions, + batchActions, + selectedCount, + onClearSelection, + onBack, + children, + loading, +}: PageContainerProps) { + const isDark = useThemeMode(); + + return ( +
+ +
+ + {onBack && ( + + )} + {title} + + {subtitle && ( + + {subtitle} + + )} +
+ + {selectedCount ? batchActions : actions} + {selectedCount ? ( + + ) : null} + +
+ + {filters && ( + + {filters} + + )} + + + {children} + +
+ ); +} diff --git a/apps/web/src/components/PluginSettingsForm.tsx b/apps/web/src/components/PluginSettingsForm.tsx new file mode 100644 index 0000000..1ed8477 --- /dev/null +++ b/apps/web/src/components/PluginSettingsForm.tsx @@ -0,0 +1,232 @@ +import React, { useCallback, useMemo } from 'react'; +import { + Form, + Input, + InputNumber, + Switch, + Select, + DatePicker, + Button, + message, + Divider, + Typography, + Tooltip, +} from 'antd'; +import { QuestionCircleOutlined, SaveOutlined } from '@ant-design/icons'; +import type { + PluginSettingField, + PluginSettingType, +} from '../api/plugins'; + +const { Text } = Typography; + +interface PluginSettingsFormProps { + /** manifest 中声明的 settings 字段 */ + fields: PluginSettingField[]; + /** 当前存储的配置值 */ + values: Record; + /** 插件版本(乐观锁) */ + recordVersion: number; + /** 保存回调 */ + onSave: (config: Record, version: number) => Promise; + /** 是否只读 */ + readOnly?: boolean; +} + +/** 根据 manifest settings 声明自动渲染配置表单 */ +const PluginSettingsForm: React.FC = ({ + fields, + values, + recordVersion, + onSave, + readOnly = false, +}) => { + const [form] = Form.useForm(); + const [saving, setSaving] = React.useState(false); + + const initialValues = useMemo(() => { + const merged: Record = {}; + for (const f of fields) { + merged[f.name] = values[f.name] ?? f.default_value ?? getDefaultForType(f.field_type); + } + return merged; + }, [fields, values]); + + const handleSave = useCallback(async () => { + try { + const formValues = await form.validateFields(); + setSaving(true); + await onSave(formValues, recordVersion); + message.success('配置已保存'); + } catch (err: unknown) { + if (err && typeof err === 'object' && 'errorFields' in err) { + // antd 表单校验错误,无需额外提示 + return; + } + message.error(err instanceof Error ? err.message : '保存失败'); + } finally { + setSaving(false); + } + }, [form, onSave, recordVersion]); + + const grouped = useMemo(() => { + const groups = new Map(); + for (const f of fields) { + const group = f.group ?? ''; + const list = groups.get(group) ?? []; + list.push(f); + groups.set(group, list); + } + return groups; + }, [fields]); + + const renderField = (field: PluginSettingField) => { + const label = ( + + {field.display_name} + {field.description && ( + + + + )} + + ); + + const rules: Array<{ required: boolean; message?: string; type?: 'string' | 'number' | 'boolean' | 'url' | 'email' }> = []; + if (field.required) { + rules.push({ required: true, message: `请输入${field.display_name}` }); + } + + const widget = renderWidget(field, readOnly); + + return ( + + {widget} + + ); + }; + + const groupEntries = Array.from(grouped.entries()); + + return ( +
+ {groupEntries.map(([group, groupFields], gi) => ( + + {group ? ( + + {group} + + ) : null} + {groupFields.map(renderField)} + + ))} + + {!readOnly && ( + + + + )} +
+ ); +}; + +function renderWidget(field: PluginSettingField, readOnly: boolean): React.ReactNode { + switch (field.field_type) { + case 'text': + return ; + case 'number': { + const props: Record = { + disabled: readOnly, + placeholder: `请输入${field.display_name}`, + style: { width: '100%' }, + }; + if (field.range) { + props.min = field.range[0]; + props.max = field.range[1]; + } + return ; + } + case 'boolean': + return ; + case 'select': + return ( + { + if (typeof o === 'object' && o !== null && 'label' in o && 'value' in o) { + return o as { label: string; value: string }; + } + return { label: String(o), value: String(o) }; + })} + /> + ); + case 'color': + return ; + case 'date': + return ; + case 'datetime': + return ; + case 'json': + return ; + default: + return ; + } +} + +function getDefaultForType(type: PluginSettingType): unknown { + switch (type) { + case 'text': + case 'color': + return ''; + case 'number': + return 0; + case 'boolean': + return false; + case 'select': + return undefined; + case 'multiselect': + return []; + case 'date': + case 'datetime': + return undefined; + case 'json': + return ''; + default: + return ''; + } +} + +export default PluginSettingsForm; diff --git a/apps/web/src/components/ThemeSwitcher.tsx b/apps/web/src/components/ThemeSwitcher.tsx new file mode 100644 index 0000000..1c5ab98 --- /dev/null +++ b/apps/web/src/components/ThemeSwitcher.tsx @@ -0,0 +1,64 @@ +import { Dropdown } from 'antd'; +import { BgColorsOutlined } from '@ant-design/icons'; +import { useAppStore, THEME_OPTIONS } from '../stores/app'; + +export default function ThemeSwitcher() { + const theme = useAppStore((s) => s.theme); + const setTheme = useAppStore((s) => s.setTheme); + + const content = ( +
+ {THEME_OPTIONS.map((opt) => { + const active = theme === opt.key; + return ( +
setTheme(opt.key)} + style={{ + display: 'flex', + alignItems: 'center', + gap: 12, + padding: '10px 12px', + borderRadius: 8, + cursor: 'pointer', + border: `2px solid ${active ? opt.preview.primary : 'transparent'}`, + background: active ? `${opt.preview.primary}08` : 'transparent', + transition: 'all 0.15s ease', + }} + > + {/* 色块预览 */} +
+
+
+
+
+
+
{opt.label}
+
{opt.desc}
+
+ {active && ( +
+ )} +
+ ); + })} +
+ ); + + return ( + content} trigger={['click']} placement="bottomRight"> +
+ +
+
+ ); +} diff --git a/apps/web/src/components/ai/AiAnalysisCard.tsx b/apps/web/src/components/ai/AiAnalysisCard.tsx new file mode 100644 index 0000000..a975f77 --- /dev/null +++ b/apps/web/src/components/ai/AiAnalysisCard.tsx @@ -0,0 +1,145 @@ +import { useState, useCallback } from 'react'; +import { Button, Card, Spin, Empty, Alert } from 'antd'; +import { ThunderboltOutlined, ReloadOutlined } from '@ant-design/icons'; +import { startAnalysis, type AnalysisType } from '../../api/ai/analysisSse'; +import { AuthButton } from '../AuthButton'; + +export interface AiAnalysisCardProps { + analysisType: AnalysisType; + sourceRef: string; + patientId?: string; + triggerLabel?: string; + permission?: string; + metrics?: string[]; + taskId?: string; +} + +type AnalysisState = 'idle' | 'loading' | 'success' | 'error'; + +export function AiAnalysisCard({ + analysisType, + sourceRef, + triggerLabel = 'AI 分析', + permission = 'ai.analysis.manage', + metrics, + taskId, +}: AiAnalysisCardProps) { + const [state, setState] = useState('idle'); + const [content, setContent] = useState(''); + const [errorMsg, setErrorMsg] = useState(''); + + const handleStart = useCallback(async () => { + setState('loading'); + setContent(''); + setErrorMsg(''); + + const body: Record = {}; + if (analysisType === 'lab-report' || analysisType === 'report-summary') { + body.report_id = sourceRef; + } + if (analysisType === 'trends' || analysisType === 'checkup-plan') { + body.patient_id = sourceRef; + } + if (analysisType === 'follow-up-summary') { + body.source_id = taskId || sourceRef; + } + if (metrics) { + body.metrics = metrics; + } + + try { + await startAnalysis(analysisType, body, { + onChunk: (chunk) => setContent(prev => prev + chunk), + onError: (msg) => { + setErrorMsg(msg); + setState('error'); + }, + onDone: () => setState('success'), + }); + } catch { + setErrorMsg('分析请求失败'); + setState('error'); + } + }, [analysisType, sourceRef, metrics, taskId]); + + const handleReset = useCallback(() => { + setState('idle'); + setContent(''); + setErrorMsg(''); + }, []); + + const TriggerButton = permission ? ( + + + + ) : ( + + ); + + if (state === 'idle') { + return TriggerButton; + } + + if (state === 'loading') { + return ( + +
+ } /> +
+ AI 正在分析... +
+
+
+ ); + } + + if (state === 'error') { + return ( + + } onClick={handleStart}> + 重试 + + } + /> + + ); + } + + if (!content) { + return ; + } + + return ( + {triggerLabel}结果} + size="small" + style={{ marginTop: 12 }} + extra={ + + } + > +
+ {content} +
+
+ ); +} diff --git a/apps/web/src/components/ai/AiSidebar.tsx b/apps/web/src/components/ai/AiSidebar.tsx new file mode 100644 index 0000000..006bde1 --- /dev/null +++ b/apps/web/src/components/ai/AiSidebar.tsx @@ -0,0 +1,415 @@ +import { useState, useRef, useEffect, useCallback } from 'react'; +import { + Drawer, + Input, + Button, + Space, + Typography, + Spin, + Tag, + Card, + theme, +} from 'antd'; +import { + SendOutlined, + RobotOutlined, + DeleteOutlined, + WarningOutlined, + SafetyCertificateOutlined, +} from '@ant-design/icons'; +import { useLocation } from 'react-router-dom'; +import { aiChatApi, type ChatHistoryItem, type DisplayHint } from '../../api/ai/chat'; +import { analysisApi, type HealthSummaryResponse } from '../../api/ai/analysis'; +import { useAuthStore } from '../../stores/auth'; +import RichMessage from './RichMessage'; + +const { Text } = Typography; +const { TextArea } = Input; + +interface ChatMessage { + id: string; + role: 'user' | 'assistant'; + content: string; + displayHints?: DisplayHint[]; +} + +function extractPatientId(pathname: string): string | null { + const match = pathname.match(/\/health\/patients\/([0-9a-f-]+)/i); + return match?.[1] ?? null; +} + +const RISK_CONFIG: Record = { + low: { color: 'green', label: '低风险' }, + medium: { color: 'orange', label: '中风险' }, + high: { color: 'red', label: '高风险' }, + critical: { color: '#cf1322', label: '严重' }, +}; + +export default function AiSidebar({ + open, + onClose, +}: { + open: boolean; + onClose: () => void; +}) { + const [messages, setMessages] = useState([]); + const [input, setInput] = useState(''); + const [loading, setLoading] = useState(false); + const [summary, setSummary] = useState(null); + const [summaryLoading, setSummaryLoading] = useState(false); + const messagesEndRef = useRef(null); + const location = useLocation(); + const { token } = theme.useToken(); + + const patientId = extractPatientId(location.pathname); + const permissions = useAuthStore((s) => s.permissions); + const canChat = permissions.includes('ai.chat.send'); + const canViewSummary = permissions.includes('ai.analysis.list'); + + // 欢迎消息 + useEffect(() => { + if (open && messages.length === 0) { + setMessages([ + { + id: 'welcome', + role: 'assistant', + content: patientId + ? '你好!我是 AI 健康助手。当前已关联患者档案,你可以问我关于该患者的体征、化验报告、用药等信息。' + : '你好!我是 AI 健康助手。你可以向我咨询健康相关问题,或打开患者详情页查看患者数据。', + }, + ]); + } + }, [open]); // eslint-disable-line react-hooks/exhaustive-deps + + // 自动加载患者健康摘要 + useEffect(() => { + if (!open || !patientId || !canViewSummary) { + setSummary(null); + return; + } + let cancelled = false; + setSummaryLoading(true); + analysisApi + .getHealthSummary(patientId) + .then((data) => { + if (!cancelled) setSummary(data); + }) + .catch(() => { + if (!cancelled) setSummary(null); + }) + .finally(() => { + if (!cancelled) setSummaryLoading(false); + }); + return () => { + cancelled = true; + }; + }, [open, patientId, canViewSummary]); + + const scrollToBottom = useCallback(() => { + setTimeout(() => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }, 100); + }, []); + + useEffect(() => { + scrollToBottom(); + }, [messages, scrollToBottom]); + + const handleSend = async () => { + const text = input.trim(); + if (!text || loading || !canChat) return; + + const userMsg: ChatMessage = { + id: `u-${Date.now()}`, + role: 'user', + content: text, + }; + + const newMessages = [...messages, userMsg]; + setMessages(newMessages); + setInput(''); + setLoading(true); + + try { + const history: ChatHistoryItem[] = newMessages + .filter((m) => m.id !== 'welcome') + .map((m) => ({ + role: m.role, + content: m.content, + })); + + const resp = await aiChatApi.sendMessage( + text, + history, + patientId ?? undefined + ); + + setMessages((prev) => [ + ...prev, + { + id: resp.message_id, + role: 'assistant' as const, + content: resp.reply, + displayHints: resp.display_hints, + }, + ]); + } catch { + setMessages((prev) => [ + ...prev, + { + id: `err-${Date.now()}`, + role: 'assistant', + content: '抱歉,AI 服务暂时不可用,请稍后再试。', + }, + ]); + } finally { + setLoading(false); + } + }; + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + handleSend(); + } + }; + + const handleClear = () => { + setMessages([ + { + id: 'welcome', + role: 'assistant', + content: '对话已清空。有什么可以帮你的?', + }, + ]); + }; + + const riskInfo = summary ? RISK_CONFIG[summary.risk_level] ?? RISK_CONFIG.low : null; + + return ( + + + AI 健康助手 + {patientId && ( + + 已关联患者 + + )} + + } + placement="right" + size={400} + open={open} + onClose={onClose} + styles={{ + body: { display: 'flex', flexDirection: 'column', padding: 0 }, + }} + extra={ +