From 93e375dc7de7e3eebd8bcebc932d0078bd83d34e Mon Sep 17 00:00:00 2001 From: Sidharth Prabhu Date: Fri, 24 Jul 2026 10:27:30 +0530 Subject: [PATCH] API Dashboard added --- api-dashboard/.gitignore | 24 + api-dashboard/README.md | 49 + api-dashboard/eslint.config.js | 22 + api-dashboard/index.html | 13 + api-dashboard/package-lock.json | 2771 +++++++++++++++++ api-dashboard/package.json | 30 + api-dashboard/public/favicon.svg | 1 + api-dashboard/public/icons.svg | 24 + api-dashboard/src/App.css | 1240 ++++++++ api-dashboard/src/App.tsx | 1266 ++++++++ api-dashboard/src/assets/Tillo.png | Bin 0 -> 17582 bytes api-dashboard/src/assets/hero.png | Bin 0 -> 13057 bytes api-dashboard/src/assets/logo.png | Bin 0 -> 18429 bytes api-dashboard/src/assets/react.svg | 1 + api-dashboard/src/assets/vite.svg | 1 + api-dashboard/src/index.css | 73 + api-dashboard/src/main.tsx | 10 + api-dashboard/tsconfig.app.json | 26 + api-dashboard/tsconfig.json | 7 + api-dashboard/tsconfig.node.json | 23 + api-dashboard/vite.config.ts | 7 + .../canteen/sales/config/SecurityConfig.java | 2 + .../controller/DeveloperApiController.java | 515 +++ .../controller/DeveloperApiKeyController.java | 120 + .../canteen/sales/model/DeveloperApiKey.java | 137 + .../repository/DeveloperApiKeyRepository.java | 12 + .../sales/service/DeveloperApiKeyService.java | 74 + 27 files changed, 6448 insertions(+) create mode 100644 api-dashboard/.gitignore create mode 100644 api-dashboard/README.md create mode 100644 api-dashboard/eslint.config.js create mode 100644 api-dashboard/index.html create mode 100644 api-dashboard/package-lock.json create mode 100644 api-dashboard/package.json create mode 100644 api-dashboard/public/favicon.svg create mode 100644 api-dashboard/public/icons.svg create mode 100644 api-dashboard/src/App.css create mode 100644 api-dashboard/src/App.tsx create mode 100644 api-dashboard/src/assets/Tillo.png create mode 100644 api-dashboard/src/assets/hero.png create mode 100644 api-dashboard/src/assets/logo.png create mode 100644 api-dashboard/src/assets/react.svg create mode 100644 api-dashboard/src/assets/vite.svg create mode 100644 api-dashboard/src/index.css create mode 100644 api-dashboard/src/main.tsx create mode 100644 api-dashboard/tsconfig.app.json create mode 100644 api-dashboard/tsconfig.json create mode 100644 api-dashboard/tsconfig.node.json create mode 100644 api-dashboard/vite.config.ts create mode 100644 backend/src/main/java/com/rit/canteen/sales/controller/DeveloperApiController.java create mode 100644 backend/src/main/java/com/rit/canteen/sales/controller/DeveloperApiKeyController.java create mode 100644 backend/src/main/java/com/rit/canteen/sales/model/DeveloperApiKey.java create mode 100644 backend/src/main/java/com/rit/canteen/sales/repository/DeveloperApiKeyRepository.java create mode 100644 backend/src/main/java/com/rit/canteen/sales/service/DeveloperApiKeyService.java diff --git a/api-dashboard/.gitignore b/api-dashboard/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/api-dashboard/.gitignore @@ -0,0 +1,24 @@ +# 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? diff --git a/api-dashboard/README.md b/api-dashboard/README.md new file mode 100644 index 00000000..34b91ede --- /dev/null +++ b/api-dashboard/README.md @@ -0,0 +1,49 @@ +# Tillo Developer Console (API Dashboard) + +Welcome to the **Tillo Developer Console**, a premium management dashboard and integration interface for creating, managing, and testing developer API Keys for external system widgets and program modules. + +## 🏗️ Architecture & Security Model + +To protect sensitive canteen financials and limit key usage footprint, this module operates under a robust security architecture: + +1. **Usage Quota Limits**: Each system administrator or customer user is strictly limited to obtaining a maximum of **3 active API keys**. +2. **Access Control Scopes**: API Keys obtain a specialized **Read-Only** access scope. Write operations, wallet top-ups, session creations, or master configurations are rejected with a HTTP `401 Unauthorized` or `403 Forbidden` response. +3. **Financial Exclusion Guard**: All developer API key requests are barred from viewing financial statistics (e.g. daily store revenue, hourly billing graphs, transaction tables, master ledgers, and vendor settlement logs). General non-sensitive counters like active product pricing lists, public store stalls, and order statuses are open for querying. +4. **Wallet Safeguard**: + - **Customer API Keys** can query the active wallet balance of the owner user account. + - **System API Keys** can query general system token circulation statistics, but cannot access individual customer transaction lines or private ledger balances. + +--- + +## 🚀 Getting Started + +### 1. Run the Dev Server +Navigate to this directory and boot Vite: +```bash +npm run dev +``` + +### 2. Authentication +Log in with either: +- **System Admin / Staff Credentials**: email/password credentials. +- **Customer Mobile Credentials**: 10-digit mobile number and 4-digit PIN. + +*Credentials matches your core Tillo application database.* + +--- + +## 📡 API Reference Directory + +Include the custom header `X-Developer-Key: DEV-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX` in all requests to query: + +| Method | Path | Description | Access Level | +| :--- | :--- | :--- | :--- | +| `GET` | `/api/developer/v1/validate` | Verify API key health and view owner details. | Key validated | +| `GET` | `/api/developer/v1/stalls` | Fetch catalog listings of active canteen stalls. | Key validated | +| `GET` | `/api/developer/v1/products` | Retrieve catalog list of products and current stock. | Key validated | +| `GET` | `/api/developer/v1/wallet` | Fetch wallet balance (owner only) or total circulation volume. | Owner scope | +| `GET` | `/api/developer/v1/orders` | Retrieve list of active orders (owner only) or order statuses. | Owner scope | + +--- + +*Developed by the Canteen Automation Team.* diff --git a/api-dashboard/eslint.config.js b/api-dashboard/eslint.config.js new file mode 100644 index 00000000..ef614d25 --- /dev/null +++ b/api-dashboard/eslint.config.js @@ -0,0 +1,22 @@ +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: { + globals: globals.browser, + }, + }, +]) diff --git a/api-dashboard/index.html b/api-dashboard/index.html new file mode 100644 index 00000000..71905655 --- /dev/null +++ b/api-dashboard/index.html @@ -0,0 +1,13 @@ + + + + + + + api-dashboard + + +
+ + + diff --git a/api-dashboard/package-lock.json b/api-dashboard/package-lock.json new file mode 100644 index 00000000..350f0165 --- /dev/null +++ b/api-dashboard/package-lock.json @@ -0,0 +1,2771 @@ +{ + "name": "api-dashboard", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "api-dashboard", + "version": "0.0.0", + "dependencies": { + "react": "^19.2.7", + "react-dom": "^19.2.7" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "@types/node": "^24.13.2", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.3", + "eslint": "^10.6.0", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.3", + "globals": "^17.7.0", + "typescript": "~6.0.2", + "typescript-eslint": "^8.62.0", + "vite": "^8.1.1" + } + }, + "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/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/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "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.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", + "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "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/@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/@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.139.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", + "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", + "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", + "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.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", + "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", + "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.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", + "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", + "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.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", + "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", + "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.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", + "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", + "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.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", + "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", + "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", + "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", + "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", + "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", + "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", + "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", + "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.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", + "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", + "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", + "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.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", + "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", + "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/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "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/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.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", + "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", + "dev": 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==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.65.0.tgz", + "integrity": "sha512-IEgob78X12rHpUmtcwFsXhZdVGJtwTVP8FiCLZkR6GlYVrl2PcuB+KhCE5BlVC/eQpQnu8WXRtkHZuPar+gCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/type-utils": "8.65.0", + "@typescript-eslint/utils": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", + "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.65.0", + "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.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.65.0.tgz", + "integrity": "sha512-CZ4nMxWwgu1HEEFNkeaCptra9QCtkmKdgf3sWh1rl1trIhmxLilgTV4cwcbQ4wemnT4sWQN8CaKOmdYx+g2gMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", + "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.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.65.0.tgz", + "integrity": "sha512-SxnPhbTsGahizDgbu7oqFH/xVtzIqMd/s+WtnSxNxJZJpLbdT5IPdzg8EZxO3+PoKahXmwJLeNQOpKJb3/bi7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.65.0", + "@typescript-eslint/types": "^8.65.0", + "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.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.65.0.tgz", + "integrity": "sha512-Esbl8OSYiVxBokYgWPf7VVWg/BE798wXhimnn9ML9Pt5qoDf8bfQlgjlKXR/k98+AcNzlLKYrpCcrcuZ9DZLgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.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/tsconfig-utils": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.65.0.tgz", + "integrity": "sha512-j6GzGqCiRdA7Qhur2VVmKZAkBLfnHFQfx4TaJGL9RMveZqCo48jSHHO0DTgizEnGhtWnqmbtCUSrqSkdiY/0Hg==", + "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.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.65.0.tgz", + "integrity": "sha512-YjaZ7PRI5qY7ax2L3PbvX0rRyGtipAReCWs0mhhDBHjH/vl0g0BonaGXrKdKpMbIIsMIwDgbk/xzkBTyAltS5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/utils": "8.65.0", + "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.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.65.0.tgz", + "integrity": "sha512-JSSwWNy+H0E/01jJEM+hrX6N0OFDzFzeIhHFSAS01tlVaevpG8cFyYRPhS5yjGOvBUx3sqQHVMjCL1CAZZMxBg==", + "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.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.65.0.tgz", + "integrity": "sha512-JboAE2swaYt4tb1fHhHTABE2K+OLy09XfcTbhnk4Pw96f9dd2e9iYsJ28gBggHlo5z5x1rkyWvcPoTuNTd4oGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.65.0", + "@typescript-eslint/tsconfig-utils": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/visitor-keys": "8.65.0", + "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/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.65.0.tgz", + "integrity": "sha512-gXiwIHsYreboxeJucHKPvgwl7dXt50mF8s1/c00cP/WoVTyWKFdtfhRWwZiXYFU5H2O8vVoSLNrexFZjYS/SGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.65.0", + "@typescript-eslint/types": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.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/visitor-keys": { + "version": "8.65.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.65.0.tgz", + "integrity": "sha512-8C71BQkGjiMmXtop7pHVJu1l2NNShFdkCyD6a2ezzs5vU/L3LRtb69EtcteFwz0mYMPzIgOw0n6OV4VBUWZd7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.65.0", + "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/@vitejs/plugin-react": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.4.tgz", + "integrity": "sha512-XcCQz0TBpBgljhj0gMuuDj49i6Ytqh5q1osT/Gp5uAVJUCTWxyskk/l1jwYYiu2xcNHHipdMz40EGfM1VdamVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "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/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "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/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/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/baseline-browser-mapping": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.1.tgz", + "integrity": "sha512-HYXq73DDpCtNzOmrFsm9eSwCvWCql0RzqjpDzXN9EadiLJ4DNat0nsZ/Bzmy+Ud12mb4/zKDY0cQ805ZzN+i0A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/browserslist": { + "version": "4.28.7", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.7.tgz", + "integrity": "sha512-JxV13hNrFxqjOc8alRbq9dK1MM79NEXYpma2B2J4wAtpWS5zIEIKqWPGCl7N4o7Uc7B7itylh7SuDujATRyyTw==", + "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.44", + "caniuse-lite": "^1.0.30001806", + "electron-to-chromium": "^1.5.393", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", + "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/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/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/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "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/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/electron-to-chromium": { + "version": "1.5.396", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.396.tgz", + "integrity": "sha512-yHiw2Y3C3H9U6TMbOfoWK/BPreiOPXRfTWPBwQBoZG6/8TB6eOPnsy5oaRYuatR7Fw2SJ4kKforgufeo7fq0EQ==", + "dev": true, + "license": "ISC" + }, + "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": "10.7.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.7.0.tgz", + "integrity": "sha512-GVTD7s1vdIl6UYvAfriOPeY1Df8LIZjfofLvHwde+erDHGGuHyuM6xoxRxmHiebhYuD2p1vN4wWh0XzPARSGDQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "packages/*" + ], + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.6.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@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", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.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", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "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.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.3.tgz", + "integrity": "sha512-5EMmLCV98Pi4o/f/3DP/v/tNqLHMIc9I8LKClNDWhZ9JTho89/kQcitCXQBMG7sAfVRK0Ie3T2EDOzp1YXYiVA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "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/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/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==", + "dev": true, + "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/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/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.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.3.tgz", + "integrity": "sha512-/zipXxyO6rGvuNGDiULY9MvEGSkb2gaG4GGH4ygMi0ZZzyMHdUZBmntJmx5x1G2VuPytCwGN4xsJP6cw+sK+vQ==", + "dev": true, + "license": "ISC" + }, + "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/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "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.7.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz", + "integrity": "sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/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/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/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-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/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/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/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/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.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/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/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/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/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "dev": true, + "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/node-releases": { + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "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/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/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/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.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.22", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.22.tgz", + "integrity": "sha512-KBDEIpLrvpv16pp3K0Fw+UCoZfopFjjgeB+0tA/aaThfEE74kKDLrgg603YvOWJyg3+WYtyq3xYsQWsIyZlPqQ==", + "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.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "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/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.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.8" + } + }, + "node_modules/rolldown": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", + "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.139.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.1.5", + "@rolldown/binding-darwin-arm64": "1.1.5", + "@rolldown/binding-darwin-x64": "1.1.5", + "@rolldown/binding-freebsd-x64": "1.1.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", + "@rolldown/binding-linux-arm64-gnu": "1.1.5", + "@rolldown/binding-linux-arm64-musl": "1.1.5", + "@rolldown/binding-linux-ppc64-gnu": "1.1.5", + "@rolldown/binding-linux-s390x-gnu": "1.1.5", + "@rolldown/binding-linux-x64-gnu": "1.1.5", + "@rolldown/binding-linux-x64-musl": "1.1.5", + "@rolldown/binding-openharmony-arm64": "1.1.5", + "@rolldown/binding-wasm32-wasi": "1.1.5", + "@rolldown/binding-win32-arm64-msvc": "1.1.5", + "@rolldown/binding-win32-x64-msvc": "1.1.5" + } + }, + "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/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/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/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/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/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==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "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/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.65.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.65.0.tgz", + "integrity": "sha512-/ggrHAwyjENDusvyxbuqxAC2dTnZg/Z8F+fgQtYIz+L6n/9HfSlEZcFGV/NsMNa6CkGk0xUjUAFwC0vHOflvIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.65.0", + "@typescript-eslint/parser": "8.65.0", + "@typescript-eslint/typescript-estree": "8.65.0", + "@typescript-eslint/utils": "8.65.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/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "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/vite": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz", + "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.17", + "rolldown": "~1.1.5", + "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.3.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 + } + } + }, + "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/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/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/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" + } + } + } +} diff --git a/api-dashboard/package.json b/api-dashboard/package.json new file mode 100644 index 00000000..f6b24818 --- /dev/null +++ b/api-dashboard/package.json @@ -0,0 +1,30 @@ +{ + "name": "api-dashboard", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.2.7", + "react-dom": "^19.2.7" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "@types/node": "^24.13.2", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.3", + "eslint": "^10.6.0", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.3", + "globals": "^17.7.0", + "typescript": "~6.0.2", + "typescript-eslint": "^8.62.0", + "vite": "^8.1.1" + } +} diff --git a/api-dashboard/public/favicon.svg b/api-dashboard/public/favicon.svg new file mode 100644 index 00000000..6893eb13 --- /dev/null +++ b/api-dashboard/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/api-dashboard/public/icons.svg b/api-dashboard/public/icons.svg new file mode 100644 index 00000000..e9522193 --- /dev/null +++ b/api-dashboard/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/api-dashboard/src/App.css b/api-dashboard/src/App.css new file mode 100644 index 00000000..420f30e4 --- /dev/null +++ b/api-dashboard/src/App.css @@ -0,0 +1,1240 @@ +/* Main Container & Viewport Layouts */ +.api-dashboard-container.login-mode { + display: flex; + align-items: center; + justify-content: center; + min-height: 100vh; + background-color: var(--color-bg-main); + padding: 1rem; +} + +.api-dashboard-layout { + display: flex; + min-height: 100vh; + background-color: var(--color-bg-main); +} + +/* Glassmorphism & Cards */ +.glass-effect { + background: rgba(255, 255, 255, 0.85); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + border: 1px solid rgba(255, 255, 255, 0.5); + box-shadow: var(--shadow-md); +} + +.premium-card { + background: var(--color-card-bg); + border-radius: 1.5rem; + border: 1px solid var(--color-border); + padding: 2rem; + box-shadow: var(--shadow-sm); + transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1); +} + +.premium-card:hover { + box-shadow: var(--shadow-xl); + transform: translateY(-2px); +} + +/* Header styling */ +.dashboard-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 1.25rem 2rem; + border-radius: 1.5rem; + border: 1px solid var(--color-border); + box-shadow: var(--shadow-sm); +} + +.header-brand { + display: flex; + align-items: center; + gap: 1rem; +} + +.tillo-logo-img { + height: 56px; + width: auto; + object-fit: contain; +} + +.brand-texts h1 { + font-size: 1.5rem; + font-weight: 800; + color: var(--color-text-dark); + line-height: 1.2; +} + +.brand-texts .subtitle { + font-size: 0.8rem; + font-weight: 600; + color: var(--color-primary); + letter-spacing: 0.05em; + text-transform: uppercase; +} + +/* User Badge */ +.user-profile-badge { + display: flex; + align-items: center; + gap: 0.85rem; + background: rgba(0, 24, 40, 0.03); + padding: 0.5rem 0.5rem 0.5rem 1rem; + border-radius: 1rem; + border: 1px solid var(--color-border-light); +} + +.user-profile-badge .avatar { + background: var(--color-primary); + color: white; + width: 32px; + height: 32px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-weight: 700; + font-size: 0.9rem; +} + +.user-info { + display: flex; + flex-direction: column; +} + +.user-info .username { + font-size: 0.875rem; + font-weight: 700; + color: var(--color-text-dark); + line-height: 1.2; +} + +.user-info .user-role { + font-size: 0.725rem; + color: var(--color-text-secondary); + font-weight: 500; +} + +.btn-logout { + background: transparent; + border: none; + color: var(--color-text-muted); + width: 32px; + height: 32px; + border-radius: 0.5rem; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.2s; +} + +.btn-logout:hover { + background: rgba(239, 68, 68, 0.08); + color: var(--color-danger); +} + +/* Toast Messages */ +.toast { + position: fixed; + top: 1.5rem; + right: 2rem; + padding: 1rem 1.5rem; + border-radius: 1rem; + color: white; + font-weight: 600; + font-size: 0.875rem; + box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1); + z-index: 1000; + animation: slideIn 0.3s cubic-bezier(0.16, 1, 0.3, 1) forwards; +} + +@keyframes slideIn { + from { transform: translateX(100%) translateY(-10px); opacity: 0; } + to { transform: translateX(0) translateY(0); opacity: 1; } +} + +.toast-success { + background: #08a850; + border: 1px solid #068d42; +} + +.toast-error { + background: #ef4444; + border: 1px solid #dc2626; +} + +/* Login Screen */ +.login-screen-wrapper { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + padding: 2rem 0; +} + +.login-card { + width: 100%; + max-width: 480px; + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.login-card h2 { + font-size: 1.75rem; + font-weight: 800; + color: var(--color-text-dark); + text-align: center; +} + +.login-desc { + color: var(--color-text-secondary); + font-size: 0.925rem; + text-align: center; + margin-top: -0.75rem; +} + +.login-type-switch { + display: flex; + background: rgba(0, 24, 40, 0.04); + padding: 0.25rem; + border-radius: 0.85rem; +} + +.login-type-switch button { + flex: 1; + background: transparent; + border: none; + padding: 0.75rem; + border-radius: 0.65rem; + font-weight: 700; + font-size: 0.875rem; + color: var(--color-text-secondary); + cursor: pointer; + transition: all 0.2s; +} + +.login-type-switch button.active { + background: white; + color: var(--color-text-dark); + box-shadow: var(--shadow-sm); +} + +.login-form { + display: flex; + flex-direction: column; + gap: 1.25rem; +} + +/* Forms & Inputs */ +.input-group { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.input-group label { + font-size: 0.825rem; + font-weight: 700; + color: var(--color-text-secondary); + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.input-group input, +.input-group select { + font-family: var(--font-sans); + font-size: 0.95rem; + padding: 0.85rem 1.1rem; + border-radius: 0.85rem; + border: 1px solid var(--color-border); + background-color: #fafdfb; + color: var(--color-text-primary); + outline: none; + transition: all 0.2s; + width: 100%; +} + +.input-group input:focus, +.input-group select:focus { + border-color: var(--color-primary); + background-color: white; + box-shadow: 0 0 0 3px var(--color-primary-glow); +} + +.input-group select { + appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%23475569'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 1.1rem center; + background-size: 1.1rem; + padding-right: 2.5rem; +} + +/* Buttons */ +.btn-primary { + background: var(--color-primary); + color: white; + font-weight: 700; + font-size: 0.95rem; + padding: 0.95rem 1.5rem; + border-radius: 1rem; + border: none; + cursor: pointer; + transition: all 0.2s; + display: inline-flex; + align-items: center; + justify-content: center; + box-shadow: 0 8px 16px -4px rgba(8, 168, 80, 0.25); +} + +.btn-primary:hover:not(:disabled) { + background: var(--color-primary-dark); + box-shadow: 0 10px 20px -4px rgba(8, 168, 80, 0.35); + transform: translateY(-1px); +} + +.btn-primary:active:not(:disabled) { + transform: translateY(0); +} + +.btn-primary:disabled { + background: var(--color-text-muted); + box-shadow: none; + cursor: not-allowed; + opacity: 0.6; +} + +.btn-postman { + background: #ff6c37; + color: white; + font-family: var(--font-sans); + font-weight: 700; + font-size: 0.875rem; + padding: 0.85rem 1.25rem; + border-radius: 1rem; + border: none; + cursor: pointer; + transition: all 0.2s; + display: inline-flex; + align-items: center; + justify-content: center; + box-shadow: 0 8px 16px -4px rgba(255, 108, 55, 0.25); +} + +.btn-postman:hover { + background: #ef5b25; + box-shadow: 0 10px 20px -4px rgba(255, 108, 55, 0.35); + transform: translateY(-1px); +} + +.btn-postman:active { + transform: translateY(0); +} + +.w-full { + width: 100%; +} + +.demo-credentials-box { + background: rgba(8, 168, 80, 0.05); + border: 1px dashed rgba(8, 168, 80, 0.2); + border-radius: 1rem; + padding: 1.1rem; + margin-top: 0.5rem; +} + +.demo-credentials-box h4 { + font-size: 0.85rem; + font-weight: 800; + color: var(--color-primary-dark); + margin-bottom: 0.25rem; +} + +.demo-credentials-box p { + font-size: 0.8rem; + color: var(--color-text-secondary); +} + +/* Main Dashboard Workspace Layout */ +.dashboard-sidebar { + position: fixed; + left: 0; + top: 0; + bottom: 0; + width: 280px; + height: 100vh; + border-radius: 0; + border: none; + border-right: 1px solid var(--color-border); + padding: 2.25rem 1.5rem; + display: flex; + flex-direction: column; + gap: 1.5rem; + z-index: 50; + background-color: var(--color-bg-sidebar); +} + +.sidebar-brand { + display: flex; + justify-content: center; + align-items: center; + margin-bottom: 1.5rem; +} + +.sidebar-footer { + margin-top: auto; + padding-top: 1rem; + border-top: 1px solid var(--color-border-light); +} + +.sidebar-nav { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.nav-item { + display: flex; + align-items: center; + gap: 0.75rem; + width: 100%; + padding: 0.85rem 1.1rem; + border-radius: 1rem; + border: none; + background: transparent; + color: var(--color-text-secondary); + font-weight: 700; + font-size: 0.875rem; + cursor: pointer; + text-align: left; + transition: all 0.2s; +} + +.nav-item:hover { + background: rgba(0, 24, 40, 0.03); + color: var(--color-text-dark); +} + +.nav-item.active { + background: var(--color-primary-glow); + color: var(--color-primary-dark); +} + +.nav-icon { + flex-shrink: 0; +} + +.nav-item-group { + display: flex; + flex-direction: column; + gap: 0.15rem; +} + +.submenu { + display: flex; + flex-direction: column; + gap: 0.15rem; + padding-left: 0.75rem; + margin-top: 0.25rem; + border-left: 2px solid var(--color-border); + margin-left: 1.1rem; +} + +.submenu-item { + background: transparent; + border: none; + color: var(--color-text-secondary); + font-family: var(--font-sans); + font-weight: 600; + font-size: 0.775rem; + padding: 0.45rem 0.75rem; + border-radius: 0.5rem; + cursor: pointer; + text-align: left; + transition: all 0.2s; + width: auto; +} + +.submenu-item:hover { + background: rgba(0, 24, 40, 0.03); + color: var(--color-text-dark); +} + +.security-guarantee-box { + background: var(--color-primary-glow); + border: 1px solid rgba(8, 168, 80, 0.1); + border-radius: 1.25rem; + padding: 1.25rem; + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.shield-icon { + margin-bottom: 0.25rem; +} + +.security-guarantee-box h4 { + font-size: 0.875rem; + font-weight: 800; + color: var(--color-primary-dark); +} + +.security-guarantee-box p { + font-size: 0.775rem; + color: var(--color-text-secondary); + line-height: 1.4; +} + +/* Main Content Area */ +.dashboard-main-content { + display: flex; + flex-direction: column; + margin-left: 280px; + flex: 1; + padding: 3rem; + min-height: 100vh; + overflow-y: auto; +} + +.tab-pane { + display: flex; + flex-direction: column; + gap: 2rem; +} + +.panel-header h2 { + font-size: 1.5rem; + font-weight: 800; + color: var(--color-text-dark); +} + +.panel-header p { + color: var(--color-text-secondary); + font-size: 0.95rem; +} + +/* Key Management Grid */ +.keys-grid { + display: grid; + grid-template-columns: 360px 1fr; + gap: 2rem; + align-items: start; +} + +@media (max-width: 1200px) { + .keys-grid { + grid-template-columns: 1fr; + } +} + +.key-creator-card { + display: flex; + flex-direction: column; + gap: 1.25rem; +} + +.key-creator-card h3 { + font-size: 1.15rem; + font-weight: 800; + color: var(--color-text-dark); +} + +.card-desc { + font-size: 0.85rem; + color: var(--color-text-secondary); + margin-top: -0.75rem; +} + +.create-key-form { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.limit-indicator { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 0.8rem; + color: var(--color-text-secondary); + background: rgba(0, 24, 40, 0.03); + padding: 0.6rem 1rem; + border-radius: 0.75rem; + align-self: flex-start; +} + +.limit-indicator .dot { + width: 8px; + height: 8px; + border-radius: 50%; + display: inline-block; +} + +.keys-list-container { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.empty-state { + text-align: center; + padding: 4rem 2rem; + border: 2px dashed var(--color-border); + border-radius: 1.5rem; + display: flex; + flex-direction: column; + align-items: center; + gap: 1rem; +} + +.empty-icon { + font-size: 2.5rem; +} + +.empty-state h3 { + font-size: 1.25rem; + font-weight: 800; + color: var(--color-text-dark); +} + +.empty-state p { + font-size: 0.9rem; + color: var(--color-text-secondary); + max-width: 320px; +} + +.api-key-card { + display: flex; + flex-direction: column; + gap: 1.25rem; +} + +.key-card-header { + display: flex; + justify-content: space-between; + align-items: start; +} + +.key-card-header h4 { + font-size: 1.1rem; + font-weight: 800; + color: var(--color-text-dark); +} + +.badge-user-type { + font-size: 0.7rem; + font-weight: 800; + color: var(--color-primary-dark); + background: var(--color-primary-glow); + padding: 0.2rem 0.5rem; + border-radius: 0.5rem; + display: inline-block; + margin-top: 0.25rem; + letter-spacing: 0.03em; + text-transform: uppercase; +} + +.btn-revoke { + background: var(--color-danger-glow); + color: var(--color-danger); + border: 1px solid rgba(239, 68, 68, 0.15); + font-weight: 700; + font-size: 0.775rem; + padding: 0.4rem 0.85rem; + border-radius: 0.6rem; + cursor: pointer; + transition: all 0.2s; +} + +.btn-revoke:hover { + background: var(--color-danger); + color: white; +} + +.key-display-box { + display: flex; + background: #fafdfb; + border: 1px solid var(--color-border); + border-radius: 0.85rem; + padding: 0.75rem 1rem; + align-items: center; + justify-content: space-between; + gap: 1rem; +} + +.key-code { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 0.875rem; + color: var(--color-text-primary); + font-weight: 600; + word-break: break-all; +} + +.key-actions { + display: flex; + gap: 0.35rem; + flex-shrink: 0; +} + +.btn-icon-only { + background: transparent; + border: none; + color: var(--color-text-secondary); + width: 32px; + height: 32px; + border-radius: 0.5rem; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.2s; +} + +.btn-icon-only:hover { + background: rgba(0, 24, 40, 0.04); + color: var(--color-text-dark); +} + +.key-metadata { + display: flex; + justify-content: space-between; + font-size: 0.775rem; + color: var(--color-text-secondary); + border-top: 1px solid var(--color-border-light); + padding-top: 0.85rem; +} + +/* Sandbox Playground Workspace */ +.sandbox-workspace { + display: grid; + grid-template-columns: 380px 1fr; + gap: 2rem; + align-items: start; +} + +@media (max-width: 1100px) { + .sandbox-workspace { + grid-template-columns: 1fr; + } +} + +.sandbox-controls { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.security-audit-card { + border-color: rgba(234, 88, 12, 0.2); +} + +.audit-card-title { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.5rem; +} + +.audit-card-title h3 { + font-size: 1.15rem; + font-weight: 800; + color: var(--color-text-dark); +} + +.attack-buttons-grid { + display: flex; + flex-direction: column; + gap: 0.75rem; + margin-top: 1rem; +} + +.btn-attack { + background: #fafdfb; + border: 1px solid rgba(234, 88, 12, 0.2); + color: var(--color-warning); + font-family: var(--font-sans); + font-weight: 700; + font-size: 0.825rem; + padding: 0.75rem 1rem; + border-radius: 0.85rem; + cursor: pointer; + text-align: left; + transition: all 0.2s; + display: flex; + align-items: center; + justify-content: space-between; +} + +.btn-attack:hover { + background: var(--color-warning-glow); + border-color: var(--color-warning); +} + +.btn-attack::after { + content: "→"; + font-size: 1.1rem; + font-weight: 900; +} + +/* Sandbox Outputs Terminal */ +.sandbox-outputs { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.output-card { + display: flex; + flex-direction: column; + gap: 1.25rem; +} + +.output-card-header { + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid var(--color-border-light); + padding-bottom: 0.85rem; +} + +.output-card-header h3 { + font-size: 1.1rem; + font-weight: 800; + color: var(--color-text-dark); +} + +.status-badge { + font-size: 0.775rem; + font-weight: 800; + padding: 0.35rem 0.75rem; + border-radius: 2rem; + letter-spacing: 0.02em; +} + +.status-badge.success { + background: var(--color-primary-glow); + color: var(--color-primary-dark); + border: 1px solid rgba(8, 168, 80, 0.15); +} + +.status-badge.error { + background: var(--color-danger-glow); + color: var(--color-danger); + border: 1px solid rgba(239, 68, 68, 0.15); +} + +.status-badge.block { + background: var(--color-danger-glow); + color: var(--color-danger); + border: 1px solid rgba(239, 68, 68, 0.2); +} + +.status-badge.breach { + background: #fef3c7; + color: #d97706; + border: 1px solid #fcd34d; +} + +.terminal-body { + background: #011627; + border-radius: 1rem; + padding: 1.5rem; + min-height: 240px; + max-height: 480px; + overflow: auto; + box-shadow: inset 0 2px 8px rgba(0, 0, 0, 0.25); + display: flex; +} + +.json-output { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 0.8rem; + color: #addb67; /* beautiful neonish green */ + white-space: pre-wrap; + word-break: break-all; + width: 100%; +} + +.terminal-empty { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + color: var(--color-text-muted); + font-weight: 500; + font-size: 0.9rem; +} + +.terminal-loading { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + width: 100%; + gap: 1rem; + color: #8093f1; +} + +.spinner { + width: 24px; + height: 24px; + border: 3px solid rgba(128, 147, 241, 0.2); + border-top-color: #8093f1; + border-radius: 50%; + animation: loading-spin 0.8s linear infinite; +} + +@keyframes loading-spin { + to { transform: rotate(360deg); } +} + +/* Attack terminal custom designs */ +.attack-output-card { + border-color: rgba(234, 88, 12, 0.25); +} + +.text-orange { + color: var(--color-warning); +} + +.attack-terminal { + background: #110702; + border: 1px solid rgba(234, 88, 12, 0.15); +} + +.orange-spinner { + border-color: rgba(234, 88, 12, 0.2); + border-top-color: var(--color-warning); +} + +.firewall-audit-header { + font-family: ui-monospace, monospace; + font-size: 0.85rem; + color: #ffffff; + padding-bottom: 0.85rem; + margin-bottom: 0.85rem; + border-bottom: 1px dashed rgba(234, 88, 12, 0.2); +} + +.green-text { + color: #10b981; + font-weight: 800; +} + +.json-output.attack-output { + color: #f87171; /* soft red */ +} + +/* Documentation Reference View */ +.docs-card { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.docs-card h3 { + font-size: 1.25rem; + font-weight: 800; + color: var(--color-text-dark); +} + +.docs-card h3.section-title { + margin-top: 1rem; + border-top: 1px solid var(--color-border); + padding-top: 1.5rem; +} + +.code-block-wrapper { + background: #0f172a; + border-radius: 1rem; + padding: 1.25rem; + display: flex; + flex-direction: column; + gap: 0.75rem; +} + +.code-header { + display: flex; + justify-content: space-between; + align-items: center; + color: var(--color-text-muted); + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +.btn-copy-code { + background: rgba(255, 255, 255, 0.1); + color: white; + border: none; + font-weight: 700; + font-size: 0.7rem; + padding: 0.35rem 0.75rem; + border-radius: 0.5rem; + cursor: pointer; + transition: all 0.2s; +} + +.btn-copy-code:hover { + background: rgba(255, 255, 255, 0.2); +} + +.code-pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; + font-size: 0.85rem; + color: #38bdf8; /* light sky blue */ + margin: 0; + white-space: pre-wrap; + word-break: break-all; +} + +.docs-table { + width: 100%; + border-collapse: collapse; + margin-top: 0.5rem; +} + +.docs-table th, +.docs-table td { + padding: 1rem; + text-align: left; + border-bottom: 1px solid var(--color-border); +} + +.docs-table th { + font-weight: 800; + color: var(--color-text-dark); + text-transform: uppercase; + font-size: 0.75rem; + letter-spacing: 0.05em; + background: rgba(0, 24, 40, 0.02); +} + +.docs-table td { + font-size: 0.875rem; + vertical-align: top; +} + +.method-get { + background: rgba(16, 185, 129, 0.1); + color: #059669; + font-weight: 800; + font-size: 0.725rem; + padding: 0.25rem 0.5rem; + border-radius: 0.4rem; + display: inline-block; +} + +.security-notice-banner { + background: var(--color-primary-glow); + border: 1px solid rgba(8, 168, 80, 0.15); + border-radius: 1.25rem; + padding: 1.5rem; + margin-top: 1rem; +} + +.security-notice-banner h4 { + font-size: 1rem; + font-weight: 800; + color: var(--color-primary-dark); + margin-bottom: 0.5rem; +} + +.security-notice-banner p { + font-size: 0.85rem; + color: var(--color-text-secondary); + line-height: 1.5; +} + +/* Elevated permissions toggles and scopes layout */ +.permissions-config { + margin-top: 1.5rem; + padding-top: 1.25rem; + border-top: 1px solid var(--color-border); + display: flex; + flex-direction: column; + gap: 1rem; +} + +.toggle-label-header { + font-size: 0.775rem; + font-weight: 800; + color: var(--color-text-secondary); + letter-spacing: 0.05em; + text-transform: uppercase; + margin-bottom: 0.25rem; + display: block; +} + +.toggle-switch-wrapper { + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 0.75rem; +} + +/* Sliding switch container */ +.switch { + position: relative; + display: inline-block; + width: 44px; + height: 24px; + flex-shrink: 0; +} + +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #cbd5e1; + transition: .3s; +} + +.slider:before { + position: absolute; + content: ""; + height: 18px; + width: 18px; + left: 3px; + bottom: 3px; + background-color: white; + transition: .3s; +} + +input:checked + .slider { + background-color: #ef6c37; +} + +input:checked + .slider:before { + transform: translateX(20px); +} + +.slider.round { + border-radius: 24px; +} + +.slider.round:before { + border-radius: 50%; +} + +.toggle-desc { + font-size: 0.85rem; + font-weight: 700; + color: var(--color-text-dark); +} + +.permissions-checklist { + display: flex; + flex-direction: column; + gap: 0.65rem; + margin-bottom: 1rem; +} + +.permission-group-card { + background: rgba(0, 24, 40, 0.015); + border: 1px solid var(--color-border-light); + border-radius: 0.75rem; + padding: 0.65rem 0.85rem; + display: flex; + justify-content: space-between; + align-items: center; + gap: 1rem; +} + +.permission-group-card h4 { + font-size: 0.825rem; + font-weight: 700; + color: var(--color-text-dark); + margin: 0; +} + +.permission-checkboxes { + display: flex; + gap: 1rem; +} + +.checkbox-container { + display: flex; + align-items: center; + gap: 0.35rem; + font-size: 0.75rem; + font-weight: 700; + color: var(--color-text-secondary); + cursor: pointer; + user-select: none; +} + +.checkbox-container input { + cursor: pointer; +} + +.checkbox-container.disabled { + opacity: 0.4; + cursor: not-allowed; + pointer-events: none; +} + +/* API Key Card Scopes display */ +.badge-access-type { + font-size: 0.65rem; + font-weight: 800; + padding: 0.15rem 0.4rem; + border-radius: 0.25rem; + text-transform: uppercase; +} + +.badge-access-type.elevated { + background: rgba(239, 108, 55, 0.1); + color: #ef6c37; +} + +.badge-access-type.readonly { + background: rgba(8, 168, 80, 0.1); + color: #08a850; +} + +.key-permissions-list { + display: flex; + flex-direction: column; + gap: 0.35rem; + margin-top: 0.75rem; + border-top: 1px dashed var(--color-border); + padding-top: 0.75rem; +} + +.scopes-label { + font-size: 0.725rem; + font-weight: 800; + color: var(--color-text-secondary); +} + +.scopes-badges { + display: flex; + flex-wrap: wrap; + gap: 0.25rem; +} + +.scope-tag { + font-size: 0.625rem; + font-weight: 700; + background: rgba(0, 24, 40, 0.04); + color: var(--color-text-dark); + padding: 0.1rem 0.4rem; + border-radius: 0.25rem; + border: 1px solid rgba(0, 0, 0, 0.05); +} diff --git a/api-dashboard/src/App.tsx b/api-dashboard/src/App.tsx new file mode 100644 index 00000000..21ba5101 --- /dev/null +++ b/api-dashboard/src/App.tsx @@ -0,0 +1,1266 @@ +import React, { useState, useEffect } from 'react'; +import logo from './assets/logo.png'; +import './App.css'; + +interface DeveloperApiKey { + id: number; + apiKey: string; + name: string; + userId: number; + userType: string; + ownerIdentifier: string; + active: boolean; + createdAt: string; + lastUsedAt?: string; + writeAccess?: boolean; + permissions?: string; +} + +interface UserSession { + token: string; + id: number; + name: string; + emailOrMobile: string; + roleOrType: string; +} + +const BACKEND_URL = window.location.origin.includes('localhost') + ? 'http://localhost:8080' + : window.location.origin; + +function App() { + // Auth State + const [session, setSession] = useState(null); + const [loginType, setLoginType] = useState<'CUSTOMER' | 'SYSTEM'>('SYSTEM'); + const [loginMobile, setLoginMobile] = useState(''); + const [loginPin, setLoginPin] = useState(''); + const [loginEmail, setLoginEmail] = useState(''); + const [loginPassword, setLoginPassword] = useState(''); + + // Key Management State + const [keys, setKeys] = useState([]); + const [newKeyName, setNewKeyName] = useState(''); + const [showSecrets, setShowSecrets] = useState>({}); + const [writeAccess, setWriteAccess] = useState(false); + const [selectedPermissions, setSelectedPermissions] = useState([ + 'READ_PRODUCTS', 'READ_STALLS', 'READ_ORDERS', 'READ_WALLETS' + ]); + + // UI Tabs & Feedback + const [activeTab, setActiveTab] = useState<'keys' | 'playground' | 'docs'>('keys'); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + const [success, setSuccess] = useState(null); + + // Playground Sandbox State + const [selectedKey, setSelectedKey] = useState(''); + const [selectedEndpoint, setSelectedEndpoint] = useState('/api/developer/v1/validate'); + const [customKeyInput, setCustomKeyInput] = useState(''); + const [playgroundLoading, setPlaygroundLoading] = useState(false); + const [playgroundStatus, setPlaygroundStatus] = useState(null); + const [playgroundResponse, setPlaygroundResponse] = useState(null); + + // Simulated Attack State + const [attackLoading, setAttackLoading] = useState(false); + const [attackStatus, setAttackStatus] = useState(null); + const [attackResponse, setAttackResponse] = useState(null); + + // Restore session from localStorage on mount + useEffect(() => { + const saved = localStorage.getItem('developer_session'); + if (saved) { + try { + const parsed = JSON.parse(saved); + setSession(parsed); + } catch (e) { + localStorage.removeItem('developer_session'); + } + } + }, []); + + // Fetch keys when session changes + useEffect(() => { + if (session) { + fetchKeys(); + } else { + setKeys([]); + } + }, [session]); + + const clearMessages = () => { + setError(null); + setSuccess(null); + }; + + const showNotification = (type: 'success' | 'error', msg: string) => { + if (type === 'success') { + setSuccess(msg); + setTimeout(() => setSuccess(null), 4000); + } else { + setError(msg); + setTimeout(() => setError(null), 5000); + } + }; + + const handleLogin = async (e: React.FormEvent) => { + e.preventDefault(); + setLoading(true); + clearMessages(); + + try { + if (loginType === 'SYSTEM') { + const res = await fetch(`${BACKEND_URL}/api/system/login`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email: loginEmail, password: loginPassword }), + }); + + const data = await res.json(); + if (!res.ok) throw new Error(data.error || 'System Login failed'); + + const sessionData: UserSession = { + token: data.token, + id: data.id, + name: data.name, + emailOrMobile: data.email, + roleOrType: data.role, + }; + + setSession(sessionData); + localStorage.setItem('developer_session', JSON.stringify(sessionData)); + showNotification('success', `Welcome back, ${data.name}!`); + } else { + // Customer login + const res = await fetch(`${BACKEND_URL}/api/auth/login`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ mobileNumber: loginMobile, pin: loginPin }), + }); + + const data = await res.json(); + if (!res.ok || !data.success) throw new Error(data.message || 'Customer Login failed'); + + const sessionData: UserSession = { + token: data.token, + id: data.user.id, + name: data.user.name || 'Customer', + emailOrMobile: data.user.mobileNumber, + roleOrType: 'CUSTOMER', + }; + + setSession(sessionData); + localStorage.setItem('developer_session', JSON.stringify(sessionData)); + showNotification('success', `Welcome back, ${sessionData.name}!`); + } + } catch (err: any) { + showNotification('error', err.message || 'An error occurred during authentication'); + } finally { + setLoading(false); + } + }; + + const handleLogout = () => { + localStorage.removeItem('developer_session'); + setSession(null); + clearMessages(); + showNotification('success', 'Logged out successfully'); + }; + + const fetchKeys = async () => { + if (!session) return; + try { + const res = await fetch(`${BACKEND_URL}/api/developer-keys`, { + headers: { + 'Authorization': `Bearer ${session.token}`, + }, + }); + if (!res.ok) throw new Error('Failed to fetch API keys'); + const data = await res.json(); + setKeys(data); + if (data.length > 0 && !selectedKey) { + setSelectedKey(data[0].apiKey); + } + } catch (err: any) { + showNotification('error', err.message); + } + }; + + const handleCreateKey = async (e: React.FormEvent) => { + e.preventDefault(); + if (!session) return; + if (!newKeyName.trim()) { + showNotification('error', 'Please enter a name for the API key'); + return; + } + setLoading(true); + clearMessages(); + + try { + const isSystemUser = session.roleOrType === 'MASTER' || session.roleOrType === 'MANAGER'; + const permissionsPayload = isSystemUser + ? selectedPermissions.join(',') + : 'READ_PRODUCTS,READ_STALLS,READ_ORDERS,READ_WALLETS'; + const writeAccessPayload = isSystemUser ? writeAccess : false; + + const res = await fetch(`${BACKEND_URL}/api/developer-keys`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${session.token}`, + }, + body: JSON.stringify({ + name: newKeyName, + writeAccess: writeAccessPayload, + permissions: permissionsPayload + }), + }); + + const data = await res.json(); + if (!res.ok) throw new Error(data.error || 'Failed to generate key'); + + showNotification('success', 'API Key generated successfully'); + setNewKeyName(''); + fetchKeys(); + } catch (err: any) { + showNotification('error', err.message); + } finally { + setLoading(false); + } + }; + + const handleRevokeKey = async (id: number) => { + if (!session) return; + if (!window.confirm('Are you sure you want to revoke this API key? Applications using this key will immediately lose access.')) return; + setLoading(true); + clearMessages(); + + try { + const res = await fetch(`${BACKEND_URL}/api/developer-keys/${id}`, { + method: 'DELETE', + headers: { + 'Authorization': `Bearer ${session.token}`, + }, + }); + + const data = await res.json(); + if (!res.ok) throw new Error(data.error || 'Failed to revoke key'); + + showNotification('success', 'API Key revoked and deleted'); + fetchKeys(); + } catch (err: any) { + showNotification('error', err.message); + } finally { + setLoading(false); + } + }; + + const copyToClipboard = (text: string) => { + navigator.clipboard.writeText(text); + showNotification('success', 'Copied to clipboard!'); + }; + + const navigateToSection = (sectionId: string) => { + setActiveTab('docs'); + setTimeout(() => { + const element = document.getElementById(sectionId); + if (element) { + element.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } + }, 100); + }; + + const downloadPostmanCollection = () => { + const keyToUse = customKeyInput || selectedKey || 'YOUR_API_KEY_HERE'; + const collectionName = 'Tillo Developer API'; + + const postmanJson = { + info: { + name: collectionName, + schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json', + description: 'Postman Collection generated dynamically from Tillo Developer Console to test Developer APIs.' + }, + variable: [ + { + key: 'baseUrl', + value: BACKEND_URL, + type: 'string' + }, + { + key: 'apiKey', + value: keyToUse, + type: 'string' + } + ], + item: [ + { + name: 'Health Check / Validate Key', + request: { + method: 'GET', + header: [ + { + key: 'X-Developer-Key', + value: '{{apiKey}}', + type: 'text' + } + ], + url: { + raw: '{{baseUrl}}/api/developer/v1/validate', + host: ['{{baseUrl}}'], + path: ['api', 'developer', 'v1', 'validate'] + } + } + }, + { + name: 'Get Active Stalls', + request: { + method: 'GET', + header: [ + { + key: 'X-Developer-Key', + value: '{{apiKey}}', + type: 'text' + } + ], + url: { + raw: '{{baseUrl}}/api/developer/v1/stalls', + host: ['{{baseUrl}}'], + path: ['api', 'developer', 'v1', 'stalls'] + } + } + }, + { + name: 'Get Active Products', + request: { + method: 'GET', + header: [ + { + key: 'X-Developer-Key', + value: '{{apiKey}}', + type: 'text' + } + ], + url: { + raw: '{{baseUrl}}/api/developer/v1/products', + host: ['{{baseUrl}}'], + path: ['api', 'developer', 'v1', 'products'] + } + } + }, + { + name: 'Get Wallet Balance (Own)', + request: { + method: 'GET', + header: [ + { + key: 'X-Developer-Key', + value: '{{apiKey}}', + type: 'text' + } + ], + url: { + raw: '{{baseUrl}}/api/developer/v1/wallet', + host: ['{{baseUrl}}'], + path: ['api', 'developer', 'v1', 'wallet'] + } + } + }, + { + name: "Query User's Wallet Balance (By Mobile)", + request: { + method: 'GET', + header: [ + { + key: 'X-Developer-Key', + value: '{{apiKey}}', + type: 'text' + } + ], + url: { + raw: '{{baseUrl}}/api/developer/v1/wallet?mobileNumber=9876543210', + host: ['{{baseUrl}}'], + path: ['api', 'developer', 'v1', 'wallet'], + query: [ + { + key: 'mobileNumber', + value: '9876543210' + } + ] + } + } + }, + { + name: 'Get Active Orders', + request: { + method: 'GET', + header: [ + { + key: 'X-Developer-Key', + value: '{{apiKey}}', + type: 'text' + } + ], + url: { + raw: '{{baseUrl}}/api/developer/v1/orders', + host: ['{{baseUrl}}'], + path: ['api', 'developer', 'v1', 'orders'] + } + } + } + ] + }; + + const dataStr = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(postmanJson, null, 2)); + const downloadAnchor = document.createElement('a'); + downloadAnchor.setAttribute('href', dataStr); + downloadAnchor.setAttribute('download', 'Tillo_Developer_API.postman_collection.json'); + document.body.appendChild(downloadAnchor); + downloadAnchor.click(); + downloadAnchor.remove(); + showNotification('success', 'Postman Collection downloaded! Import it directly into Postman to start testing.'); + }; + + const toggleRevealKey = (id: number) => { + setShowSecrets(prev => ({ ...prev, [id]: !prev[id] })); + }; + + const executePlayground = async () => { + const apiKeyToUse = customKeyInput || selectedKey; + if (!apiKeyToUse) { + showNotification('error', 'Please select or enter a developer API Key'); + return; + } + + setPlaygroundLoading(true); + setPlaygroundStatus(null); + setPlaygroundResponse(null); + + try { + const res = await fetch(`${BACKEND_URL}${selectedEndpoint}`, { + headers: { + 'X-Developer-Key': apiKeyToUse, + }, + }); + setPlaygroundStatus(res.status); + const data = await res.json(); + setPlaygroundResponse(data); + } catch (err: any) { + setPlaygroundResponse({ error: err.message || 'Network request failed' }); + } finally { + setPlaygroundLoading(false); + } + }; + + const runSimulatedAttack = async (attackUrl: string, method: string = 'GET') => { + const apiKeyToUse = customKeyInput || selectedKey; + if (!apiKeyToUse) { + showNotification('error', 'Please select or enter an API Key to simulate the attack'); + return; + } + + setAttackLoading(true); + setAttackStatus(null); + setAttackResponse(null); + + try { + const options: RequestInit = { + method, + headers: { + 'X-Developer-Key': apiKeyToUse, + 'Content-Type': 'application/json', + }, + }; + + if (method === 'POST') { + options.body = JSON.stringify({ amount: '1000', userId: 1 }); + } + + const res = await fetch(`${BACKEND_URL}${attackUrl}`, options); + setAttackStatus(res.status); + const data = await res.json(); + setAttackResponse(data); + } catch (err: any) { + setAttackResponse({ error: err.message || 'Request blocked or failed' }); + } finally { + setAttackLoading(false); + } + }; + + const isSystemUser = session ? (session.roleOrType === 'MASTER' || session.roleOrType === 'MANAGER') : false; + const isLimitReached = !isSystemUser && keys.length >= 3; + + return ( +
+ {/* Notifications */} + {success &&
{success}
} + {error &&
{error}
} + + {!session ? ( + /* Login Panel */ +
+
+
+ Branding +
+

Access Developer Console

+

Use your existing Tillo user credentials or system administrator login to manage API keys.

+ +
+ + +
+ +
+ {loginType === 'SYSTEM' ? ( + <> +
+ + setLoginEmail(e.target.value)} + /> +
+
+ + setLoginPassword(e.target.value)} + /> +
+ + ) : ( + <> +
+ + setLoginMobile(e.target.value)} + /> +
+
+ + setLoginPin(e.target.value)} + /> +
+ + )} + + +
+ +
+

💡 Dev Sandbox Access:

+ {loginType === 'SYSTEM' ? ( +

Default Admin: admin / Password: admin123

+ ) : ( +

Create / use any customer login registered on the POS client. (e.g. mobile 9876543210, pin 123456)

+ )} +
+
+
+ ) : ( + <> + {/* Full Height left sidebar */} + + + {/* Main Content Area */} +
+ {activeTab === 'keys' && ( +
+
+

Manage Developer API Keys

+

Obtain authentication tokens for integrating program modules and external query widgets.

+
+ + {/* API Key list */} +
+
+

Generate New API Key

+

Assign a friendly name to describe your application module usage.

+ +
+
+ + setNewKeyName(e.target.value)} + /> +
+ + {isSystemUser && ( +
+ +
+ + Enable Writing & Mutation Access +
+ + +
+ {[ + { id: 'stalls', label: 'Stalls Catalog', read: 'READ_STALLS', write: 'WRITE_STALLS' }, + { id: 'products', label: 'Products Catalog', read: 'READ_PRODUCTS', write: 'WRITE_PRODUCTS' }, + { id: 'orders', label: 'Orders List', read: 'READ_ORDERS', write: 'WRITE_ORDERS' }, + { id: 'wallets', label: 'Wallet circulation / Lookup', read: 'READ_WALLETS', write: 'WRITE_WALLETS' } + ].map((group) => { + const hasRead = selectedPermissions.includes(group.read); + const hasWrite = selectedPermissions.includes(group.write); + + return ( +
+

{group.label}

+
+ + +
+
+ ); + })} +
+
+ )} + + +
+ + {isSystemUser ? ( +
+ + Unlimited Staff API Keys Allowed +
+ ) : ( +
+ = 3 ? '#ef4444' : '#08a850' }}> + Keys generated: {keys.length} of 3 limit +
+ )} +
+ +
+ {keys.length === 0 ? ( +
+
🔐
+

No Active API Keys

+

You haven't generated any API keys yet. Create one on the left to start accessing endpoints.

+
+ ) : ( + keys.map((key) => ( +
+
+
+

{key.name}

+
+ {key.userType} Key + + {key.writeAccess ? 'Elevated (Write)' : 'Read-Only'} + +
+
+ +
+ +
+ + {showSecrets[key.id] ? key.apiKey : `${key.apiKey.substring(0, 8)}••••••••••••••••••••••••••••`} + +
+ + + +
+
+ + {key.permissions && ( +
+ Scopes: +
+ {key.permissions.split(',').map(scope => ( + {scope} + ))} +
+
+ )} + +
+ Created: {new Date(key.createdAt).toLocaleDateString()} + Last Used: {key.lastUsedAt ? new Date(key.lastUsedAt).toLocaleTimeString() : 'Never'} +
+
+ )) + )} +
+
+
+ )} + + {activeTab === 'playground' && ( +
+
+

Developer Sandbox Playground

+

Query endpoints live and observe the robust read-only and anti-financial access controls.

+
+ +
+
+
+

Configure Sandbox Query

+ +
+ + +
+ + {!selectedKey && ( +
+ + setCustomKeyInput(e.target.value)} + /> +
+ )} + +
+ + +
+ + + + +
+ + {/* Simulated security audit / attack tests */} +
+
+ + + +

Simulate Security Audit Attacks

+
+

Try to breach financial or write layers using the developer key. Watch Tillo's robust guard mechanism reject it.

+ +
+ + + + + +
+
+
+ +
+
+
+

Sandbox Terminal Response

+ {playgroundStatus !== null && ( + = 200 && playgroundStatus < 300 ? 'success' : 'error'}`}> + HTTP {playgroundStatus} + + )} +
+ +
+ {playgroundLoading ? ( +
+
+ Request dispatched... waiting for backend frame +
+ ) : playgroundResponse ? ( +
+                            {JSON.stringify(playgroundResponse, null, 2)}
+                          
+ ) : ( +
+ Ready. Select an endpoint above and click "Send API Request". +
+ )} +
+
+ + {/* Attack response audit terminal */} + {(attackResponse || attackLoading) && ( +
+
+

Audit Firewall Log

+ {attackStatus !== null && ( + = 400 ? 'block' : 'breach'}`}> + {attackStatus === 401 || attackStatus === 403 ? 'BLOCKED - SECURE' : `STATUS ${attackStatus}`} + + )} +
+ +
+ {attackLoading ? ( +
+
+ Penetration query executing... +
+ ) : ( +
+
+ [SHIELD-ACTIVE] Detected access violation on restricted resource. Access Denied. +
+
+                                {JSON.stringify(attackResponse, null, 2)}
+                              
+
+ )} +
+
+ )} +
+
+
+ )} + + {activeTab === 'docs' && ( +
+
+

API Reference Integration Guide

+

How to use your Developer API keys to authenticate and query Tillo system details.

+
+ +
+

1. System Architecture & Scope Segregation

+

+ The Tillo Developer API acts as a read-only integration layer designed to support external widgets, active status screens, and profile integrations. + Authorization is separated based on the owner of the API Key: +

+ +
+
    +
  • Customer Key Scopes: Allows programmatic reading of the key owner's own orders and wallet balance. Perfect for building custom widgets or personal status trackers.
  • +
  • System Key Scopes: Allows canteen-wide catalog querying (stalls, product list, inventory levels) and overall statistics, but redacts specific customer transaction profiles.
  • +
+
+ +

2. Authentication & Header Specs

+

+ All endpoints require you to supply your custom key inside the X-Developer-Key HTTP header. + Traditional JWT authorization headers (e.g., Authorization: Bearer ...) are not required and will be ignored when querying developer paths. +

+ +
+
+ HTTP Request Example +
+ + +
+
+
+{`curl -X GET \\
+  -H "X-Developer-Key: DEV-YOUR_API_KEY_HERE" \\
+  ${BACKEND_URL}/api/developer/v1/stalls`}
+                    
+
+ +

3. Endpoint Directory (Read-Only)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HTTP MethodPathDescription / UsagePermissions / Scope
GET/api/developer/v1/validateVerifies key health, active status, and returns key metadata.Key validated
GET/api/developer/v1/stallsLists active canteen stalls, descriptions, and operational status.Key validated
GET/api/developer/v1/productsReturns the active menu item catalog, categories, pricing, and current stock.Key validated
GET/api/developer/v1/wallet + Returns wallet balance.
+ • Default: Returns own balance (for Customer Key) or circulation stats (for System Key).
+ • Query Parameter: Pass ?mobileNumber=... or ?userId=... to check the wallet balance of any user in the system. +
Any user balance, read-only
GET/api/developer/v1/orders + Retrieves active and completed order lists.
+ • Customer Key: Returns key owner's own orders.
+ • System Key: Lists system orders with financial elements (payout details) masked. +
Owner scope, key validated
+ +

4. Core Rules & Security Shields

+
+

🔒 Strict Mutation and Financial Block

+

+ To prevent unauthorized changes or leaks of financial parameters, developer API keys are bounded by strict policies: +

+
    +
  • Read-Only Constraints: Standard operations like checking out, creating coupon codes, placing orders, or topping up balances are blocked. Mutation endpoints will return 401 Unauthorized or 403 Forbidden.
  • +
  • Financial Isolation: General database tables containing financial audits, payment processor reference IDs, vendor billing settlements, and total daily canteen earnings are inaccessible to API keys.
  • +
  • Quota Limit: To prevent token spam, a hard limit of 3 API keys is enforced per account. Revoke an old key to create a new one.
  • +
+
+ +

5. Error Responses Schema

+

When query violations occur, the firewall returns standard JSON error bodies to simplify client parsing:

+
+
+ HTTP 401 Unauthorized / Invalid Key +
+
+{`{
+  "error": "Invalid or missing X-Developer-Key header"
+}`}
+                    
+
+
+
+ HTTP 403 Forbidden / Access Denied +
+
+{`{
+  "status": 403,
+  "error": "Forbidden",
+  "message": "Access Denied on restricted financial/mutation endpoint",
+  "path": "/api/wallet/topup"
+}`}
+                    
+
+ +

6. Quick Integration JavaScript Snippet

+
+
+ JavaScript Fetch Template +
+
+{`async function getCanteenMenu(apiKey) {
+  try {
+    const response = await fetch('${BACKEND_URL}/api/developer/v1/products', {
+      headers: {
+        'X-Developer-Key': apiKey
+      }
+    });
+    
+    if (!response.ok) {
+      const errorBody = await response.json();
+      throw new Error(errorBody.error || 'Failed to fetch');
+    }
+    
+    const products = await response.json();
+    console.log('Active Products:', products);
+    return products;
+  } catch (error) {
+    console.error('API Error:', error.message);
+  }
+}`}
+                    
+
+
+
+ )} +
+ + )} +
+ ); +} + +export default App; diff --git a/api-dashboard/src/assets/Tillo.png b/api-dashboard/src/assets/Tillo.png new file mode 100644 index 0000000000000000000000000000000000000000..7bdd84cabff706935a2d7b35f0afbb57dff6b8b5 GIT binary patch literal 17582 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 P00000NkvXXu0mjft=yBf literal 0 HcmV?d00001 diff --git a/api-dashboard/src/assets/logo.png b/api-dashboard/src/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c4b79e3f22972621e7f47ed292e18dc3d8ad6f90 GIT binary patch literal 18429 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