From cab93c7f7c31592e1774a9dae1295073bd8299e0 Mon Sep 17 00:00:00 2001 From: Pierre Date: Sun, 23 Aug 2026 09:06:18 +0000 Subject: [PATCH] Initial commit: SecourIT Gestion --- .gitignore | 5 + README.md | 112 + next-env.d.ts | 5 + next.config.js | 9 + package-lock.json | 6731 +++++++++++++++++ package.json | 48 + postcss.config.js | 6 + prisma/schema.prisma | 253 + prisma/seed-demo.js | 93 + prisma/seed.js | 50 + public/favicon.svg | 6 + public/icon-192.png | Bin 0 -> 3039 bytes public/icon-512.png | Bin 0 -> 9414 bytes public/logo-192.png | Bin 0 -> 5676 bytes public/logo-512.png | Bin 0 -> 16244 bytes public/logo.png | Bin 0 -> 33480 bytes public/manifest.json | 22 + public/sw.js | 11 + server-monitor.sh | 20 + src/app/api/auth/change-password/route.ts | 40 + src/app/api/auth/login/route.ts | 34 + src/app/api/auth/logout/route.ts | 17 + src/app/api/auth/me/route.ts | 14 + src/app/api/auth/register/route.ts | 34 + src/app/api/clients/[id]/route.ts | 91 + src/app/api/clients/route.ts | 126 + src/app/api/contracts/[id]/route.ts | 100 + src/app/api/contracts/route.ts | 55 + src/app/api/dashboard/route.ts | 287 + src/app/api/invoices/generate/route.ts | 54 + src/app/api/invoices/notifications/route.ts | 26 + src/app/api/invoices/route.ts | 108 + src/app/api/quotes/[id]/route.ts | 96 + src/app/api/quotes/route.ts | 70 + src/app/api/sales/[id]/route.ts | 131 + src/app/api/sales/notifications/route.ts | 19 + src/app/api/sales/route.ts | 131 + src/app/api/servers/[id]/metrics/route.ts | 51 + src/app/api/servers/[id]/route.ts | 110 + .../servers/[id]/services/discover/route.ts | 164 + src/app/api/servers/[id]/services/route.ts | 21 + src/app/api/servers/dashboard/route.ts | 45 + src/app/api/servers/route.ts | 63 + src/app/api/services/[id]/route.ts | 75 + src/app/api/services/route.ts | 53 + src/app/api/settings/products/route.ts | 70 + .../[id]/attachments/[attachmentId]/route.ts | 29 + src/app/api/tickets/[id]/attachments/route.ts | 25 + src/app/api/tickets/[id]/messages/route.ts | 84 + src/app/api/tickets/[id]/route.ts | 108 + src/app/api/tickets/dashboard/route.ts | 60 + src/app/api/tickets/email/route.ts | 74 + src/app/api/tickets/route.ts | 72 + src/app/api/todos/route.ts | 106 + src/app/globals.css | 45 + src/app/layout.tsx | 49 + src/app/login/page.tsx | 69 + src/app/page.tsx | 5 + src/app/providers.tsx | 8 + src/components/ClientsContent.tsx | 513 ++ src/components/ContractsContent.tsx | 413 + src/components/DashboardClient.tsx | 80 + src/components/DashboardContent.tsx | 358 + src/components/Header.tsx | 133 + src/components/InvoicesContent.tsx | 208 + src/components/QuotesContent.tsx | 303 + src/components/SalesContent.tsx | 534 ++ src/components/ServerDashboardContent.tsx | 226 + src/components/ServersContent.tsx | 254 + src/components/ServicesContent.tsx | 358 + src/components/SettingsContent.tsx | 238 + src/components/Sidebar.tsx | 159 + src/components/SimulationContent.tsx | 415 + src/components/TicketContent.tsx | 671 ++ src/components/TodoContent.tsx | 236 + src/contexts/AuthContext.tsx | 79 + src/lib/auth.ts | 66 + src/lib/invoices.ts | 25 + src/lib/mail.ts | 21 + src/lib/prisma.ts | 7 + src/lib/utils.ts | 54 + src/types/index.ts | 112 + tailwind.config.js | 27 + ticket-email-handler.py | 99 + tsconfig.json | 22 + 85 files changed, 15801 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 next-env.d.ts create mode 100644 next.config.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 postcss.config.js create mode 100644 prisma/schema.prisma create mode 100644 prisma/seed-demo.js create mode 100644 prisma/seed.js create mode 100644 public/favicon.svg create mode 100644 public/icon-192.png create mode 100644 public/icon-512.png create mode 100644 public/logo-192.png create mode 100644 public/logo-512.png create mode 100644 public/logo.png create mode 100644 public/manifest.json create mode 100644 public/sw.js create mode 100755 server-monitor.sh create mode 100644 src/app/api/auth/change-password/route.ts create mode 100644 src/app/api/auth/login/route.ts create mode 100644 src/app/api/auth/logout/route.ts create mode 100644 src/app/api/auth/me/route.ts create mode 100644 src/app/api/auth/register/route.ts create mode 100644 src/app/api/clients/[id]/route.ts create mode 100644 src/app/api/clients/route.ts create mode 100644 src/app/api/contracts/[id]/route.ts create mode 100644 src/app/api/contracts/route.ts create mode 100644 src/app/api/dashboard/route.ts create mode 100644 src/app/api/invoices/generate/route.ts create mode 100644 src/app/api/invoices/notifications/route.ts create mode 100644 src/app/api/invoices/route.ts create mode 100644 src/app/api/quotes/[id]/route.ts create mode 100644 src/app/api/quotes/route.ts create mode 100644 src/app/api/sales/[id]/route.ts create mode 100644 src/app/api/sales/notifications/route.ts create mode 100644 src/app/api/sales/route.ts create mode 100644 src/app/api/servers/[id]/metrics/route.ts create mode 100644 src/app/api/servers/[id]/route.ts create mode 100644 src/app/api/servers/[id]/services/discover/route.ts create mode 100644 src/app/api/servers/[id]/services/route.ts create mode 100644 src/app/api/servers/dashboard/route.ts create mode 100644 src/app/api/servers/route.ts create mode 100644 src/app/api/services/[id]/route.ts create mode 100644 src/app/api/services/route.ts create mode 100644 src/app/api/settings/products/route.ts create mode 100644 src/app/api/tickets/[id]/attachments/[attachmentId]/route.ts create mode 100644 src/app/api/tickets/[id]/attachments/route.ts create mode 100644 src/app/api/tickets/[id]/messages/route.ts create mode 100644 src/app/api/tickets/[id]/route.ts create mode 100644 src/app/api/tickets/dashboard/route.ts create mode 100644 src/app/api/tickets/email/route.ts create mode 100644 src/app/api/tickets/route.ts create mode 100644 src/app/api/todos/route.ts create mode 100644 src/app/globals.css create mode 100644 src/app/layout.tsx create mode 100644 src/app/login/page.tsx create mode 100644 src/app/page.tsx create mode 100644 src/app/providers.tsx create mode 100644 src/components/ClientsContent.tsx create mode 100644 src/components/ContractsContent.tsx create mode 100644 src/components/DashboardClient.tsx create mode 100644 src/components/DashboardContent.tsx create mode 100644 src/components/Header.tsx create mode 100644 src/components/InvoicesContent.tsx create mode 100644 src/components/QuotesContent.tsx create mode 100644 src/components/SalesContent.tsx create mode 100644 src/components/ServerDashboardContent.tsx create mode 100644 src/components/ServersContent.tsx create mode 100644 src/components/ServicesContent.tsx create mode 100644 src/components/SettingsContent.tsx create mode 100644 src/components/Sidebar.tsx create mode 100644 src/components/SimulationContent.tsx create mode 100644 src/components/TicketContent.tsx create mode 100644 src/components/TodoContent.tsx create mode 100644 src/contexts/AuthContext.tsx create mode 100644 src/lib/auth.ts create mode 100644 src/lib/invoices.ts create mode 100644 src/lib/mail.ts create mode 100644 src/lib/prisma.ts create mode 100644 src/lib/utils.ts create mode 100644 src/types/index.ts create mode 100644 tailwind.config.js create mode 100755 ticket-email-handler.py create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cfcecf8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +.next/ +.env +*.log +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..0b2e0c4 --- /dev/null +++ b/README.md @@ -0,0 +1,112 @@ +# SecourIT - Gestion + +Application de gestion commerciale : clients (prospects/clients), devis avec calcul de marge et frais de déplacement, contrats, factures, ventes, coûts de produits, to-do list. Interface en français, PWA. + +## Stack technique + +| Composant | Technologie | +|-----------|------------| +| Framework | Next.js 14 (App Router) | +| Langage | TypeScript | +| Base de données | PostgreSQL 17 (LXC distante) | +| ORM | Prisma 5 | +| Authentification | JWT (jose) + bcryptjs | +| UI | Tailwind CSS + Lucide React | +| Formulaires | react-hook-form + zod | +| Graphes | Recharts | +| Emails | Nodemailer (SMTP Infomaniak) | + +## Déploiement + +```bash +cd /opt/gestion + +# Installer les dépendances +npm install + +# Générer le client Prisma +npx prisma generate + +# Pousser le schema en BDD +npx prisma db push + +# Lancer en production +NODE_ENV=production npx next start -p 3005 +``` + +> **Note** : Pas de process manager en production. Le process est lancé via `nohup`. Un redémarrage automatique n'est pas garanti. + +## Structure + +``` +/opt/gestion/ +├── src/ +│ ├── app/ +│ │ ├── api/ # Routes API (App Router) +│ │ │ ├── auth/ # Login, logout, register, me, change-password +│ │ │ ├── clients/ # CRUD clients +│ │ │ ├── quotes/ # Devis +│ │ │ ├── contracts/ # Contrats +│ │ │ ├── invoices/ # Factures +│ │ │ ├── sales/ # Ventes +│ │ │ ├── servers/ # Gestion serveurs +│ │ │ ├── tickets/ # Support/tickets +│ │ │ ├── todos/ # To-do list +│ │ │ └── dashboard/ # Tableau de bord +│ │ ├── login/ # Page de connexion +│ │ └── layout.tsx # Layout principal +│ └── lib/ # Utilitaires (auth, BDD) +├── prisma/ +│ ├── schema.prisma # Modèles de données +│ ├── seed.js # Données initiales +│ └── seed-demo.js # Données de démonstration +├── public/ # Assets statiques +├── .env # Variables d'environnement +├── next.config.js # Config Next.js +├── tailwind.config.js # Config Tailwind +└── package.json +``` + +## Modèles de données (Prisma) + +- **User** - Utilisateurs avec sessions +- **Client** - Prospects et clients (type enum PROSPECT/CLIENT) +- **Quote** / **QuoteItem** - Devis avec lignes, marge, frais déplacement +- **Contract** - Contrats liés aux devis acceptés +- **Invoice** - Factures +- **Sale** / **SaleItem** - Ventes avec lignes +- **ProductCost** - Coûts des produits +- **TodoItem** - Liste de tâches + +## Variables d'environnement + +| Variable | Description | Valeur | +|----------|-------------|--------| +| `DATABASE_URL` | Connexion PostgreSQL | `postgresql://gestion_user:L0relei69@10.10.10.10:5432/gestion_secourit` | +| `JWT_SECRET` | Secret JWT | configuré dans .env | +| `SERVER_MONITOR_SECRET` | Secret monitoring | configuré dans .env | +| `SMTP_HOST` | Serveur SMTP | `smtp.infomaniak.ch` | +| `SMTP_PORT` | Port SMTP | `465` | +| `SMTP_USER` | Email SMTP | `support@secourit.fr` | +| `SMTP_PASS` | Mot de passe SMTP | configuré dans .env | +| `NODE_ENV` | Environnement | `production` | + +## Commandes utiles + +```bash +npx prisma db push # Pousser le schema en BDD +npx prisma generate # Générer le client Prisma +npx prisma studio # Interface web pour la BDD +npx prisma migrate reset # Reset complet de la BDD +node prisma/seed.js # Insérer les données initiales +node prisma/seed-demo.js # Insérer des données de test +``` + +## Domaine de production + +- **URL** : https://gestion.secourit.fr +- **LXC** : 104 (IP 10.10.10.21) +- **Port** : 3005 +- **SSL** : Let's Encrypt via Nginx Proxy Manager +- **BDD** : PostgreSQL sur LXC 101 (10.10.10.10) +- **Preprod** : https://gestion-preprod.secourit.fr (LXC 110, port 3105) diff --git a/next-env.d.ts b/next-env.d.ts new file mode 100644 index 0000000..4f11a03 --- /dev/null +++ b/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/next.config.js b/next.config.js new file mode 100644 index 0000000..8b22915 --- /dev/null +++ b/next.config.js @@ -0,0 +1,9 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, + experimental: { + serverActions: { bodySizeLimit: '2mb' }, + }, +} + +module.exports = nextConfig \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..421b3a2 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6731 @@ +{ + "name": "gestion-app", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "gestion-app", + "version": "0.1.0", + "dependencies": { + "@hookform/resolvers": "^3.3.0", + "@prisma/client": "^5.10.0", + "bcryptjs": "^2.4.3", + "clsx": "^2.1.0", + "date-fns": "^3.3.0", + "jose": "^5.2.0", + "lucide-react": "^0.344.0", + "next": "14.2.0", + "nodemailer": "^9.0.1", + "react": "^18.3.0", + "react-dom": "^18.3.0", + "react-hook-form": "^7.51.0", + "recharts": "^2.12.0", + "tailwind-merge": "^2.2.0", + "zod": "^3.22.0" + }, + "devDependencies": { + "@types/bcryptjs": "^2.4.6", + "@types/node": "^20.11.0", + "@types/nodemailer": "^8.0.1", + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "autoprefixer": "^10.4.0", + "eslint": "^8.56.0", + "eslint-config-next": "14.2.0", + "postcss": "^8.4.0", + "prisma": "^5.10.0", + "tailwindcss": "^3.4.0", + "typescript": "^5.3.0" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/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/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@hookform/resolvers": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz", + "integrity": "sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==", + "license": "MIT", + "peerDependencies": { + "react-hook-form": "^7.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.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/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "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/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.5", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.5.tgz", + "integrity": "sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@next/env": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.0.tgz", + "integrity": "sha512-4+70ELtSbRtYUuyRpAJmKC8NHBW2x1HMje9KO2Xd7IkoyucmV9SjgO+qeWMC0JWkRQXgydv1O7yKOK8nu/rITQ==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.0.tgz", + "integrity": "sha512-QkM01VPhwcupezVevy9Uyl1rmpg2PimhMjkb+ySmnPgSKUUM/PGGRQxdFgMpHv/JzQoC8kRySgKeM441GiizcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "10.3.10" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.0.tgz", + "integrity": "sha512-kHktLlw0AceuDnkVljJ/4lTJagLzDiO3klR1Fzl2APDFZ8r+aTxNaNcPmpp0xLMkgRwwk6sggYeqq0Rz9K4zzA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.0.tgz", + "integrity": "sha512-HFSDu7lb1U3RDxXNeKH3NGRR5KyTPBSUTuIOr9jXoAso7i76gNYvnTjbuzGVWt2X5izpH908gmOYWtI7un+JrA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.0.tgz", + "integrity": "sha512-iQsoWziO5ZMxDWZ4ZTCAc7hbJ1C9UDj/gATSqTaMjW2bJFwAsvf9UM79AKnljBl73uPZ+V0kH4rvnHTco4Ps2w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.0.tgz", + "integrity": "sha512-0JOk2uzLUt8fJK5LpsKKZa74zAch7bJjjgJzR9aOMs231AlE4gPYzsSm430ckZitjPGKeH5bgDZjqwqJQKIS2w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.0.tgz", + "integrity": "sha512-uYHkuTzX0NM6biKNp7hdKTf+BF0iMV254SxO0B8PgrQkxUBKGmk5ysHKB+FYBfdf9xei/t8OIKlXJs9ckD943A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.0.tgz", + "integrity": "sha512-paN89nLs2dTBDtfXWty1/NVPit+q6ldwdktixYSVwiiAz647QDCd+EIYqoiS+/rPG3oXs/A7rWcJK9HVqfnMVg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.0.tgz", + "integrity": "sha512-j1oiidZisnymYjawFqEfeGNcE22ZQ7lGUaa4pGOCVWrWeIDkPSj8zYgS9TzMNlg17Q3wSWCQC/F5uJAhSh7qcA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.0.tgz", + "integrity": "sha512-6ff6F4xb+QGD1jhx/dOT9Ot7PQ/GAYekV9ykwEh2EFS/cLTyU4Y3cXkX5cNtNIhpctS5NvyjW9gIksRNErYE0A==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.0.tgz", + "integrity": "sha512-09DbG5vXAxz0eTFSf1uebWD36GF3D5toynRkgo2AlSrxwGZkWtJ1RhmrczRYQ17eD5bdo4FZ0ibiffdq5kc4vg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nolyfill/is-core-module": { + "version": "1.0.39", + "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz", + "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.4.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@prisma/client": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.22.0.tgz", + "integrity": "sha512-M0SVXfyHnQREBKxCgyo7sffrKttwE6R8PMq330MIUF0pTwjUhLbW84pFDlf06B27XyCR++VtjugEnIHdr07SVA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=16.13" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } + }, + "node_modules/@prisma/debug": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.22.0.tgz", + "integrity": "sha512-AUt44v3YJeggO2ZU5BkXI7M4hu9BF2zzH2iF2V5pyXT/lRTyWiElZ7It+bRH1EshoMRxHgpYg4VB6rCM+mG5jQ==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.22.0.tgz", + "integrity": "sha512-UNjfslWhAt06kVL3CjkuYpHAWSO6L4kDCVPegV6itt7nD1kSJavd3vhgAEhjglLJJKEdJ7oIqDJ+yHk6qO8gPA==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.22.0", + "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "@prisma/fetch-engine": "5.22.0", + "@prisma/get-platform": "5.22.0" + } + }, + "node_modules/@prisma/engines-version": { + "version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2.tgz", + "integrity": "sha512-2PTmxFR2yHW/eB3uqWtcgRcgAbG1rwG9ZriSvQw+nnb7c4uCr3RAcGMb6/zfE88SKlC1Nj2ziUvc96Z379mHgQ==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.22.0.tgz", + "integrity": "sha512-bkrD/Mc2fSvkQBV5EpoFcZ87AvOgDxbG99488a5cexp5Ccny+UM6MAe/UFkUC0wLYD9+9befNOqGiIJhhq+HbA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.22.0", + "@prisma/engines-version": "5.22.0-44.605197351a3c8bdd595af2d2a9bc3025bca48ea2", + "@prisma/get-platform": "5.22.0" + } + }, + "node_modules/@prisma/get-platform": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.22.0.tgz", + "integrity": "sha512-pHhpQdr1UPFpt+zFfnPazhulaZYCUqeIcPpJViYoq9R+D/yw4fjE+CtnsnKzPYm0ddUbeXUzjGVGIRVgPDCk4Q==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "5.22.0" + } + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rushstack/eslint-patch": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.16.1.tgz", + "integrity": "sha512-TvZbIpeKqGQQ7X0zSCvPH9riMSFQFSggnfBjFZ1mEoILW+UuXCKwOoPcgjMwiUtRqFZ8jWhPJc4um14vC6I4ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, + "node_modules/@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/bcryptjs": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz", + "integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/nodemailer": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-8.0.1.tgz", + "integrity": "sha512-PxpaInm8V1JQDd4j0ds5HfvWQk8JupS1C0Picb96QJsrrRDjBH+DlK7L4ZdNSqNULhiZRQHc40nLVShaGxXAMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.31", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz", + "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz", + "integrity": "sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "7.2.0", + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/typescript-estree": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz", + "integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz", + "integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz", + "integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "7.2.0", + "@typescript-eslint/visitor-keys": "7.2.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "9.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz", + "integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "7.2.0", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.1.tgz", + "integrity": "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.12.2.tgz", + "integrity": "sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.12.2.tgz", + "integrity": "sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.12.2.tgz", + "integrity": "sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.12.2.tgz", + "integrity": "sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.12.2.tgz", + "integrity": "sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.12.2.tgz", + "integrity": "sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.12.2.tgz", + "integrity": "sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.12.2.tgz", + "integrity": "sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.12.2.tgz", + "integrity": "sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-gnu/-/resolver-binding-linux-loong64-gnu-1.12.2.tgz", + "integrity": "sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-loong64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-loong64-musl/-/resolver-binding-linux-loong64-musl-1.12.2.tgz", + "integrity": "sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.12.2.tgz", + "integrity": "sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.12.2.tgz", + "integrity": "sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.12.2.tgz", + "integrity": "sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.12.2.tgz", + "integrity": "sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.12.2.tgz", + "integrity": "sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.12.2.tgz", + "integrity": "sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-openharmony-arm64": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-openharmony-arm64/-/resolver-binding-openharmony-arm64-1.12.2.tgz", + "integrity": "sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.12.2.tgz", + "integrity": "sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.12.2.tgz", + "integrity": "sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.12.2.tgz", + "integrity": "sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.12.2.tgz", + "integrity": "sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "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/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/autoprefixer": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.0.tgz", + "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "caniuse-lite": "^1.0.30001787", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.12.1.tgz", + "integrity": "sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.37", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.37.tgz", + "integrity": "sha512-girxaJ7WZssDOFhzCGZTDKoTa1gk6A1TbflaYTpykLJ4UU9Fz9kx1aREM8JCuoVHbL8X8T/mJg7w2oYSq72Oig==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001799", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", + "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", + "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/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/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/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "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/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.372", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.372.tgz", + "integrity": "sha512-M3yhbAlilnwqC8D21t28UCDGHyitShTmmLRU/H+b74P6Ski16Nb9HONYEaVpMj/pwC7BEo5B95FpjODLCWbtfA==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-abstract": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.3.tgz", + "integrity": "sha512-0PuBxFi+4uPanB97iDxCLWuHeYud2FALrw5HFZGtAF38UpJDbDC8frwp2cnDyae692CQ0dou60UwWfhgsa4U/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.1.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.3.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.5", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-next": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.0.tgz", + "integrity": "sha512-N0eQkn/wz557mIpW4JQWGEv4wGU8zvJ7emLHMS15uC18jjaU4kx6leR4U9QYT/eNghUZT7N9lBlfd8E4N0cp1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/eslint-plugin-next": "14.2.0", + "@rushstack/eslint-patch": "^1.3.3", + "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-react": "^7.33.2", + "eslint-plugin-react-hooks": "^4.5.0 || 5.0.0-canary-7118f5dd7-20230705" + }, + "peerDependencies": { + "eslint": "^7.23.0 || ^8.0.0", + "typescript": ">=3.3.1" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.10", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.10.tgz", + "integrity": "sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.16.1", + "resolve": "^2.0.0-next.6" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-import-resolver-typescript": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-3.10.1.tgz", + "integrity": "sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@nolyfill/is-core-module": "1.0.39", + "debug": "^4.4.0", + "get-tsconfig": "^4.10.0", + "is-bun-module": "^2.0.0", + "stable-hash": "^0.0.5", + "tinyglobby": "^0.2.13", + "unrs-resolver": "^1.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-import-resolver-typescript" + }, + "peerDependencies": { + "eslint": "*", + "eslint-plugin-import": "*", + "eslint-plugin-import-x": "*" + }, + "peerDependenciesMeta": { + "eslint-plugin-import": { + "optional": true + }, + "eslint-plugin-import-x": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.13.0.tgz", + "integrity": "sha512-bLohSkT6469rRs8czj0tLTD8vaeIS/whvPRJVjDr7IuoTT1k5DYDERlNycjDj/HkOlvQdYurmfZ/g3fG5bgeLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz", + "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.9", + "array.prototype.findlastindex": "^1.2.6", + "array.prototype.flat": "^1.3.3", + "array.prototype.flatmap": "^1.3.3", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.1", + "hasown": "^2.0.2", + "is-core-module": "^2.16.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.1", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.9", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/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/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.0.0-canary-7118f5dd7-20230705", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.0.0-canary-7118f5dd7-20230705.tgz", + "integrity": "sha512-AZYbMo/NW9chdL7vk6HQzQhT+PvTAEVqWk9ziruUoW2kAOcN5qNyelv70e0F1VNQAbvutOC9oc+xfWycI9FxDw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/eslint-plugin-react/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-react/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/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/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/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "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-equals": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", + "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "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/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz", + "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2", + "hasown": "^2.0.4", + "is-callable": "^1.2.7", + "is-document.all": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-tsconfig": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", + "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/glob/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bun-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-bun-module/-/is-bun-module-2.0.0.tgz", + "integrity": "sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.7.1" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-document.all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz", + "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/jose": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz", + "integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", + "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/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": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "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/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "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/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/lucide-react": { + "version": "0.344.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.344.0.tgz", + "integrity": "sha512-6YyBnn91GB45VuVT96bYCOKElbJzUHqp65vX8cDcu55MQL9T969v4dhGClpljamuI/+KMO9P6w9Acq1CVQGvIQ==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "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/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-postinstall": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz", + "integrity": "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/next": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.0.tgz", + "integrity": "sha512-2T41HqJdKPqheR27ll7MFZ3gtTYvGew7cUc0PwPSyK9Ao5vvwpf9bYfP4V5YBGLckHF2kEGvrLte5BqLSv0s8g==", + "deprecated": "This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/security-update-2025-12-11 for more details.", + "license": "MIT", + "dependencies": { + "@next/env": "14.2.0", + "@swc/helpers": "0.5.5", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=18.17.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "14.2.0", + "@next/swc-darwin-x64": "14.2.0", + "@next/swc-linux-arm64-gnu": "14.2.0", + "@next/swc-linux-arm64-musl": "14.2.0", + "@next/swc-linux-x64-gnu": "14.2.0", + "@next/swc-linux-x64-musl": "14.2.0", + "@next/swc-win32-arm64-msvc": "14.2.0", + "@next/swc-win32-ia32-msvc": "14.2.0", + "@next/swc-win32-x64-msvc": "14.2.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "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.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/node-exports-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.0.tgz", + "integrity": "sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "array.prototype.flatmap": "^1.3.3", + "es-errors": "^1.3.0", + "object.entries": "^1.1.9", + "semver": "^6.3.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/node-exports-info/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/node-releases": { + "version": "2.0.47", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", + "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/nodemailer": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-9.0.1.tgz", + "integrity": "sha512-Gwv8SQewT616ZM/URn0H54b8PWo/Wum7md3EW2aWy1lO27+WZCX+Xyak3J+NlmHUjDh5ME+uesJUDRbR3Ye8Bw==", + "license": "MIT-0", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "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/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "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==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-import/node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz", + "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "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/prisma": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.22.0.tgz", + "integrity": "sha512-vtpjW3XuYCSnMsNVBjLMNkTj6OZbudcPPTPYHqX0CJfpcdWciI1dM8uHETwmDxxiqEwCIE6WvXucWUetJgfu/A==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/engines": "5.22.0" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=16.13" + }, + "optionalDependencies": { + "fsevents": "2.3.3" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "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/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-hook-form": { + "version": "7.79.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.79.0.tgz", + "integrity": "sha512-mhYp/MTmXvzYX6AJcJVko0rktoIhhmRnEouObj4wF5i/tCttgJvnp1+9wRkpITZjDTqpo4IOSJqu0dBlPlV/Lw==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/react-smooth": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", + "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", + "license": "MIT", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recharts": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", + "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", + "deprecated": "1.x and 2.x branches are no longer active. Bump to Recharts v3 to receive latest features and bugfixes. See https://github.com/recharts/recharts/wiki/3.0-migration-guide", + "license": "MIT", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^18.3.1", + "react-smooth": "^4.0.4", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "license": "MIT", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/recharts/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "2.0.0-next.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz", + "integrity": "sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.2", + "node-exports-info": "^1.6.0", + "object-keys": "^1.1.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "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==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stable-hash": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", + "integrity": "sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz", + "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-object-atoms": "^1.1.2", + "has-property-descriptors": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz", + "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwind-merge": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.1.tgz", + "integrity": "sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "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/tinyglobby/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/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz", + "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "possible-typed-array-names": "^1.1.0", + "reflect.getprototypeof": "^1.0.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unrs-resolver": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz", + "integrity": "sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.3.4" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.12.2", + "@unrs/resolver-binding-android-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-arm64": "1.12.2", + "@unrs/resolver-binding-darwin-x64": "1.12.2", + "@unrs/resolver-binding-freebsd-x64": "1.12.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.12.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.12.2", + "@unrs/resolver-binding-linux-loong64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-loong64-musl": "1.12.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.12.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.12.2", + "@unrs/resolver-binding-linux-x64-musl": "1.12.2", + "@unrs/resolver-binding-openharmony-arm64": "1.12.2", + "@unrs/resolver-binding-wasm32-wasi": "1.12.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.12.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.12.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.12.2" + } + }, + "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/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "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/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", + "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "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": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..dc969ef --- /dev/null +++ b/package.json @@ -0,0 +1,48 @@ +{ + "name": "gestion-app", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev -p 3005", + "build": "next build", + "start": "next start -p 3005", + "lint": "next lint", + "db:push": "prisma db push", + "db:studio": "prisma studio", + "db:generate": "prisma generate", + "db:seed": "node prisma/seed.js", + "db:seed-demo": "node prisma/seed-demo.js", + "db:reset": "prisma migrate reset --force" + }, + "dependencies": { + "@hookform/resolvers": "^3.3.0", + "@prisma/client": "^5.10.0", + "bcryptjs": "^2.4.3", + "clsx": "^2.1.0", + "date-fns": "^3.3.0", + "jose": "^5.2.0", + "lucide-react": "^0.344.0", + "next": "14.2.0", + "nodemailer": "^9.0.1", + "react": "^18.3.0", + "react-dom": "^18.3.0", + "react-hook-form": "^7.51.0", + "recharts": "^2.12.0", + "tailwind-merge": "^2.2.0", + "zod": "^3.22.0" + }, + "devDependencies": { + "@types/bcryptjs": "^2.4.6", + "@types/node": "^20.11.0", + "@types/nodemailer": "^8.0.1", + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "autoprefixer": "^10.4.0", + "eslint": "^8.56.0", + "eslint-config-next": "14.2.0", + "postcss": "^8.4.0", + "prisma": "^5.10.0", + "tailwindcss": "^3.4.0", + "typescript": "^5.3.0" + } +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..96bb01e --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..5d76268 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,253 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model User { + id String @id @default(cuid()) + email String @unique + name String? + passwordHash String? + role String @default("admin") + infomaniakId String? @unique + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + sessions Session[] + quotes Quote[] + contracts Contract[] + clients Client[] + sales Sale[] + todos TodoItem[] +} + +model Session { + id String @id @default(cuid()) + userId String + token String @unique + expiresAt DateTime + createdAt DateTime @default(now()) + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId]) + @@index([token]) +} + +model Client { + id String @id @default(cuid()) + name String + email String? + phone String? + address String? + siret String? + type ClientType @default(PROSPECT) + isProfessional Boolean? @default(true) + notes String? + userId String + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + quotes Quote[] + contracts Contract[] + sales Sale[] + + @@index([userId]) + @@index([type]) +} + +enum ClientType { + PROSPECT + CLIENT +} + +model Quote { + id String @id @default(cuid()) + clientId String + userId String + name String + quoteNumber String? + status QuoteStatus @default(DRAFT) + setupTotal Float @default(0) + monthlyTotal Float @default(0) + costTotal Float @default(0) + marginTotal Float @default(0) + discount Float @default(0) + travelKm Int @default(0) + travelCost Float @default(0) + notes String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + client Client @relation(fields: [clientId], references: [id], onDelete: Cascade) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + items QuoteItem[] + contract Contract? + + @@index([clientId]) + @@index([userId]) + @@index([status]) +} + +enum QuoteStatus { + DRAFT + PROPOSAL + SENT + ACCEPTED + REJECTED + EXPIRED +} + +model QuoteItem { + id String @id @default(cuid()) + quoteId String + type QuoteItemType + label String + description String? + unitPrice Float + costPrice Float + quantity Int @default(1) + isRecurring Boolean @default(false) + setupPrice Float @default(0) + setupCost Float @default(0) + monthlyPrice Float @default(0) + monthlyCost Float @default(0) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + quote Quote @relation(fields: [quoteId], references: [id], onDelete: Cascade) + + @@index([quoteId]) +} + +enum QuoteItemType { + MAINTENANCE_POSTE + MAINTENANCE_SERVER + KS_SUITE_STANDARD + KS_SUITE_BUSINESS + KS_SUITE_ENTREPRISE + HOURLY_INTERVENTION + TRAVEL + CUSTOM +} + +model Contract { + id String @id @default(cuid()) + clientId String + userId String + quoteId String? @unique + name String + startDate DateTime + endDate DateTime? + setupPrice Float @default(0) + setupCost Float @default(0) + monthlyPrice Float @default(0) + monthlyCost Float @default(0) + status ContractStatus @default(ACTIVE) + billingDay Int @default(1) + notes String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + client Client @relation(fields: [clientId], references: [id], onDelete: Cascade) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + quote Quote? @relation(fields: [quoteId], references: [id], onDelete: SetNull) + invoices Invoice[] + + @@index([clientId]) + @@index([userId]) + @@index([status]) +} + +enum ContractStatus { + PROPOSAL + ACTIVE + PAUSED + CANCELLED + EXPIRED +} + +model Invoice { + id String @id @default(cuid()) + contractId String + month DateTime + amount Float + invoiceNumber String? + emitted Boolean @default(false) + emittedAt DateTime? + paidAt DateTime? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + contract Contract @relation(fields: [contractId], references: [id], onDelete: Cascade) + + @@index([contractId]) + @@unique([contractId, month]) +} + +model TodoItem { + id String @id @default(cuid()) + userId String + text String + position Int + done Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId]) +} + +model Sale { + id String @id @default(cuid()) + clientId String + userId String + date DateTime @default(now()) + totalAmount Float @default(0) + totalCost Float @default(0) + paid Boolean @default(false) + notes String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + client Client @relation(fields: [clientId], references: [id], onDelete: Cascade) + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + items SaleItem[] + + @@index([clientId]) + @@index([userId]) +} + +model SaleItem { + id String @id @default(cuid()) + saleId String + label String + description String? + quantity Int @default(1) + unitPrice Float @default(0) + costPrice Float @default(0) + createdAt DateTime @default(now()) + + sale Sale @relation(fields: [saleId], references: [id], onDelete: Cascade) + + @@index([saleId]) +} + +model ProductCost { + id String @id @default(cuid()) + key String @unique + label String + costPrice Float + sellPrice Float + unit String + category String + isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt +} \ No newline at end of file diff --git a/prisma/seed-demo.js b/prisma/seed-demo.js new file mode 100644 index 0000000..7b09856 --- /dev/null +++ b/prisma/seed-demo.js @@ -0,0 +1,93 @@ +const { PrismaClient } = require('@prisma/client') +const bcrypt = require('bcryptjs') + +const prisma = new PrismaClient() + +async function main() { + const passwordHash = await bcrypt.hash('demo123', 12) + + const user = await prisma.user.upsert({ + where: { email: 'demo@secourit.fr' }, + update: {}, + create: { + email: 'demo@secourit.fr', + name: 'Démo', + passwordHash, + role: 'demo', + }, + }) + + console.log('Demo user created:', user.email) + + // Nettoyer les anciennes données démo + await prisma.contract.deleteMany({ where: { userId: user.id } }) + await prisma.quote.deleteMany({ where: { userId: user.id } }) + await prisma.client.deleteMany({ where: { userId: user.id } }) + + // Créer des clients + const clients = await Promise.all([ + prisma.client.create({ + data: { name: 'SARL Dupont Informatique', email: 'contact@dupont-info.fr', phone: '01 23 45 67 89', address: '12 Rue de Paris, 75001 Paris', siret: '12345678900012', type: 'CLIENT', notes: 'Client principal', userId: user.id }, + }), + prisma.client.create({ + data: { name: 'Cabinet Médical Martin', email: 'secretariat@cabinet-martin.fr', phone: '01 98 76 54 32', address: '5 Avenue des Lilas, 69001 Lyon', siret: '98765432100045', type: 'CLIENT', notes: '3 postes + 1 serveur', userId: user.id }, + }), + prisma.client.create({ + data: { name: 'Boulangerie Le Pain Doré', email: 'contact@lepaindore.fr', phone: '03 20 15 30 45', address: '8 Rue du Four, 59000 Lille', siret: '45678912300078', type: 'CLIENT', notes: '1 poste, maintenance simple', userId: user.id }, + }), + prisma.client.create({ + data: { name: 'SCI Bellevue', email: 'gestion@scibellevue.com', phone: '04 91 23 45 67', address: '15 Boulevard de la Mer, 13008 Marseille', siret: '32165498700023', type: 'PROSPECT', notes: 'Projet 5 postes + KSuite', userId: user.id }, + }), + prisma.client.create({ + data: { name: 'Restaurant Le Gourmet', email: 'contact@legourmet.fr', phone: '02 40 12 34 56', address: '3 Place du Marché, 44000 Nantes', siret: '65412378900056', type: 'PROSPECT', notes: 'A besoin de 2 caisses', userId: user.id }, + }), + prisma.client.create({ + data: { name: 'École Privée Saint-Joseph', email: 'direction@saint-joseph.fr', phone: '05 56 78 90 12', address: '20 Rue de lÉducation, 33000 Bordeaux', siret: '14725836900089', type: 'CLIENT', notes: '10 postes, 2 serveurs, KSuite Business 15 utilisateurs', userId: user.id }, + }), + ]) + + console.log('Clients created:', clients.length) + + // Créer des contrats + const contracts = [ + { clientId: clients[0].id, name: 'Contrat maintenance SARL Dupont', startDate: new Date('2025-01-01'), monthlyPrice: 76.80, monthlyCost: 11.20, setupPrice: 500, setupCost: 150, status: 'ACTIVE' }, + { clientId: clients[0].id, name: 'KSuite Business - SARL Dupont (3 users)', startDate: new Date('2025-01-01'), monthlyPrice: 47.40, monthlyCost: 19.74, setupPrice: 0, setupCost: 0, status: 'ACTIVE' }, + { clientId: clients[1].id, name: 'Contrat maintenance Cabinet Médical', startDate: new Date('2025-03-15'), monthlyPrice: 54.00, monthlyCost: 8.60, setupPrice: 800, setupCost: 200, status: 'ACTIVE' }, + { clientId: clients[2].id, name: 'Maintenance Boulangerie', startDate: new Date('2025-06-01'), monthlyPrice: 9.60, monthlyCost: 1.40, setupPrice: 150, setupCost: 50, status: 'ACTIVE' }, + { clientId: clients[5].id, name: 'Contrat global École St-Joseph', startDate: new Date('2025-09-01'), monthlyPrice: 333.00, monthlyCost: 115.70, setupPrice: 2500, setupCost: 800, status: 'ACTIVE' }, + { clientId: clients[1].id, name: 'KSuite Business - Cabinet Médical (2 users)', startDate: new Date('2025-04-01'), monthlyPrice: 31.60, monthlyCost: 13.16, setupPrice: 0, setupCost: 0, status: 'PAUSED' }, + ] + + for (const contract of contracts) { + await prisma.contract.create({ + data: { ...contract, userId: user.id, billingDay: 1, notes: null }, + }) + } + + console.log('Contracts created:', contracts.length) + + // Créer des devis + const quotes = [ + { clientId: clients[3].id, name: 'Devis SCI Bellevue - 5 postes', status: 'DRAFT', setupTotal: 250, monthlyTotal: 48, costTotal: 7, marginTotal: 41, discount: 0, travelKm: 30, travelCost: 15 }, + { clientId: clients[4].id, name: 'Devis Restaurant Le Gourmet', status: 'SENT', setupTotal: 300, monthlyTotal: 19.20, costTotal: 2.80, marginTotal: 16.40, discount: 10, travelKm: 15, travelCost: 7.50 }, + ] + + for (const quote of quotes) { + await prisma.quote.create({ + data: { ...quote, userId: user.id, notes: null }, + }) + } + + console.log('Quotes created:', quotes.length) + console.log('') + console.log('Demo account: demo@secourit.fr / demo123') +} + +main() + .catch((e) => { + console.error(e) + process.exit(1) + }) + .finally(async () => { + await prisma.$disconnect() + }) diff --git a/prisma/seed.js b/prisma/seed.js new file mode 100644 index 0000000..cd53236 --- /dev/null +++ b/prisma/seed.js @@ -0,0 +1,50 @@ +const { PrismaClient } = require('@prisma/client') +const bcrypt = require('bcryptjs') + +const prisma = new PrismaClient() + +async function main() { + const passwordHash = await bcrypt.hash('admin123', 12) + + const user = await prisma.user.upsert({ + where: { email: 'pierre@secourit.fr' }, + update: {}, + create: { + email: 'pierre@secourit.fr', + name: 'Pierre', + passwordHash, + role: 'admin', + }, + }) + + console.log('User created:', user.email) + + const defaultProducts = [ + { key: 'maintenance-poste', label: 'Maintenance Poste (Windows/Mac)', costPrice: 1.40, sellPrice: 9.60, unit: 'mois', category: 'maintenance' }, + { key: 'maintenance-serveur', label: 'Maintenance Serveur', costPrice: 4.20, sellPrice: 25.20, unit: 'mois', category: 'maintenance' }, + { key: 'ksuite-business', label: 'KSuite Business / utilisateur', costPrice: 6.58, sellPrice: 15.80, unit: 'mois', category: 'ksuite' }, + { key: 'ksuite-standard', label: 'KSuite Standard / utilisateur', costPrice: 1.58, sellPrice: 0, unit: 'mois', category: 'ksuite' }, + { key: 'ksuite-entreprise', label: 'KSuite Entreprise / utilisateur', costPrice: 12.41, sellPrice: 0, unit: 'mois', category: 'ksuite' }, + { key: 'intervention-horaire', label: 'Intervention (Taux horaire)', costPrice: 0, sellPrice: 50, unit: 'heure', category: 'hourly' }, + { key: 'deplacement-km', label: 'Déplacement (par km)', costPrice: 0.30, sellPrice: 0.50, unit: 'km', category: 'travel' }, + ] + + for (const product of defaultProducts) { + await prisma.productCost.upsert({ + where: { key: product.key }, + update: product, + create: product, + }) + } + + console.log('Default products created') +} + +main() + .catch((e) => { + console.error(e) + process.exit(1) + }) + .finally(async () => { + await prisma.$disconnect() + }) diff --git a/public/favicon.svg b/public/favicon.svg new file mode 100644 index 0000000..332c4e4 --- /dev/null +++ b/public/favicon.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/icon-192.png b/public/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..882cc2764eb5da365f5419c4791c9e7d7a5e30ed GIT binary patch literal 3039 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 literal 0 HcmV?d00001 diff --git a/public/logo.png b/public/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..12f396be4e011379982b454bc484ed5cf8c8eb3f GIT binary patch literal 33480 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 J>@((f{|_r%ptS%1 literal 0 HcmV?d00001 diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..04689ee --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,22 @@ +{ + "name": "SecourIT - Gestion", + "short_name": "SecourIT", + "description": "Application de gestion commerciale SecourIT", + "start_url": "/", + "display": "standalone", + "background_color": "#0f172a", + "theme_color": "#2563eb", + "icons": [ + { + "src": "/logo-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/logo-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any maskable" + } + ] +} diff --git a/public/sw.js b/public/sw.js new file mode 100644 index 0000000..fa7adbf --- /dev/null +++ b/public/sw.js @@ -0,0 +1,11 @@ +self.addEventListener('install', (e) => { + self.skipWaiting() +}) + +self.addEventListener('activate', (e) => { + e.waitUntil(clients.claim()) +}) + +self.addEventListener('fetch', (e) => { + e.respondWith(fetch(e.request)) +}) diff --git a/server-monitor.sh b/server-monitor.sh new file mode 100755 index 0000000..44dbf47 --- /dev/null +++ b/server-monitor.sh @@ -0,0 +1,20 @@ +#!/bin/bash +SERVER_ID="$1" +API_URL="$2" +TOKEN="$3" + +if [ -z "$SERVER_ID" ] || [ -z "$API_URL" ] || [ -z "$TOKEN" ]; then + echo "Usage: $0 SERVER_ID API_URL SECRET_TOKEN" + exit 1 +fi + +CPU_USAGE=$(top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4}' | head -1) +TOTAL_RAM=$(free -m | awk '/Mem:/ {print $2}') +USED_RAM=$(free -m | awk '/Mem:/ {print $3}') +RAM_USAGE=$(awk "BEGIN {printf \"%.2f\", ($USED_RAM / $TOTAL_RAM) * 100}") +DISK_USAGE=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//') +UPTIME=$(awk '{print $1}' /proc/uptime | cut -d. -f1) + +curl -s -X POST "$API_URL?token=$TOKEN" \ + -H "Content-Type: application/json" \ + --data-raw "{\"cpuUsage\": $CPU_USAGE, \"ramUsage\": $RAM_USAGE, \"diskUsage\": $DISK_USAGE, \"uptime\": $UPTIME}" diff --git a/src/app/api/auth/change-password/route.ts b/src/app/api/auth/change-password/route.ts new file mode 100644 index 0000000..1675d90 --- /dev/null +++ b/src/app/api/auth/change-password/route.ts @@ -0,0 +1,40 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser, hashPassword, verifyPassword } from '@/lib/auth' + +export async function PUT(request: Request) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const { currentPassword, newPassword } = await request.json() + + if (!currentPassword || !newPassword) { + return NextResponse.json({ error: 'Mot de passe actuel et nouveau mot de passe requis' }, { status: 400 }) + } + + if (newPassword.length < 6) { + return NextResponse.json({ error: 'Le nouveau mot de passe doit faire au moins 6 caractères' }, { status: 400 }) + } + + const dbUser = await prisma.user.findUnique({ where: { id: user.id } }) + if (!dbUser || !dbUser.passwordHash) { + return NextResponse.json({ error: 'Utilisateur non trouvé' }, { status: 404 }) + } + + const valid = await verifyPassword(currentPassword, dbUser.passwordHash) + if (!valid) { + return NextResponse.json({ error: 'Mot de passe actuel incorrect' }, { status: 400 }) + } + + const passwordHash = await hashPassword(newPassword) + await prisma.user.update({ + where: { id: user.id }, + data: { passwordHash }, + }) + + return NextResponse.json({ success: true }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts new file mode 100644 index 0000000..5ead95e --- /dev/null +++ b/src/app/api/auth/login/route.ts @@ -0,0 +1,34 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { verifyPassword, createToken, setAuthCookie } from '@/lib/auth' + +export async function POST(request: Request) { + try { + const { email, password } = await request.json() + + if (!email || !password) { + return NextResponse.json({ error: 'Email et mot de passe requis' }, { status: 400 }) + } + + const user = await prisma.user.findUnique({ where: { email } }) + if (!user || !user.passwordHash) { + return NextResponse.json({ error: 'Email ou mot de passe incorrect' }, { status: 401 }) + } + + const valid = await verifyPassword(password, user.passwordHash) + if (!valid) { + return NextResponse.json({ error: 'Email ou mot de passe incorrect' }, { status: 401 }) + } + + const token = await createToken(user.id, user.email) + await prisma.session.create({ + data: { userId: user.id, token, expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) }, + }) + + const response = NextResponse.json({ success: true }) + await setAuthCookie(token) + return response + } catch (error) { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/auth/logout/route.ts b/src/app/api/auth/logout/route.ts new file mode 100644 index 0000000..d8c7cbd --- /dev/null +++ b/src/app/api/auth/logout/route.ts @@ -0,0 +1,17 @@ +import { NextResponse } from 'next/server' +import { clearAuthCookie, getSession } from '@/lib/auth' +import { prisma } from '@/lib/prisma' + +export async function POST() { + try { + const session = await getSession() + if (session) { + await prisma.session.deleteMany({ where: { userId: session.userId } }) + } + const response = NextResponse.json({ success: true }) + await clearAuthCookie() + return response + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/auth/me/route.ts b/src/app/api/auth/me/route.ts new file mode 100644 index 0000000..d160a0c --- /dev/null +++ b/src/app/api/auth/me/route.ts @@ -0,0 +1,14 @@ +import { NextResponse } from 'next/server' +import { getCurrentUser } from '@/lib/auth' + +export async function GET() { + try { + const user = await getCurrentUser() + if (!user) { + return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + } + return NextResponse.json({ user }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/auth/register/route.ts b/src/app/api/auth/register/route.ts new file mode 100644 index 0000000..da7da01 --- /dev/null +++ b/src/app/api/auth/register/route.ts @@ -0,0 +1,34 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { hashPassword, createToken, setAuthCookie } from '@/lib/auth' + +export async function POST(request: Request) { + try { + const { email, password, name } = await request.json() + + if (!email || !password) { + return NextResponse.json({ error: 'Email et mot de passe requis' }, { status: 400 }) + } + + const existing = await prisma.user.findUnique({ where: { email } }) + if (existing) { + return NextResponse.json({ error: 'Cet email est déjà utilisé' }, { status: 400 }) + } + + const passwordHash = await hashPassword(password) + const user = await prisma.user.create({ + data: { email, name: name || email.split('@')[0], passwordHash }, + }) + + const token = await createToken(user.id, user.email) + await prisma.session.create({ + data: { userId: user.id, token, expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) }, + }) + + const response = NextResponse.json({ success: true, user: { id: user.id, email: user.email, name: user.name } }) + await setAuthCookie(token) + return response + } catch (error) { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/clients/[id]/route.ts b/src/app/api/clients/[id]/route.ts new file mode 100644 index 0000000..f5f6638 --- /dev/null +++ b/src/app/api/clients/[id]/route.ts @@ -0,0 +1,91 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function GET(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const client = await prisma.client.findFirst({ + where: { id: params.id, userId: user.id }, + include: { + quotes: { orderBy: { createdAt: 'desc' } }, + contracts: { orderBy: { createdAt: 'desc' } }, + }, + }) + if (!client) return NextResponse.json({ error: 'Client non trouvé' }, { status: 404 }) + + const servers: any[] = await prisma.$queryRawUnsafe(` + SELECT s.id, s.name, s.ip, s."monthlyCost" + FROM "Server" s + JOIN "ClientServer" cs ON cs."serverId" = s.id + WHERE cs."clientId" = $1 + `, params.id) + + return NextResponse.json({ client, servers }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function PUT(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const data = await request.json() + const client = await prisma.client.updateMany({ + where: { id: params.id, userId: user.id }, + data: { + name: data.name, + email: data.email, + phone: data.phone, + address: data.address, + siret: data.siret, + type: data.type, + notes: data.notes, + }, + }) + + if (client.count === 0) return NextResponse.json({ error: 'Client non trouvé' }, { status: 404 }) + + if (data.serverIds !== undefined) { + await prisma.$executeRawUnsafe(`DELETE FROM "ClientServer" WHERE "clientId" = $1`, params.id) + for (const serverId of data.serverIds) { + await prisma.$executeRawUnsafe(` + INSERT INTO "ClientServer" ("clientId", "serverId") VALUES ($1, $2) + `, params.id, serverId) + } + } + + const updated = await prisma.client.findUnique({ where: { id: params.id } }) + const servers: any[] = await prisma.$queryRawUnsafe(` + SELECT s.id, s.name, s.ip, s."monthlyCost" + FROM "Server" s + JOIN "ClientServer" cs ON cs."serverId" = s.id + WHERE cs."clientId" = $1 + `, params.id) + + return NextResponse.json({ client: updated, servers }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function DELETE(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + await prisma.$executeRawUnsafe(`DELETE FROM "ClientServer" WHERE "clientId" = $1`, params.id) + const client = await prisma.client.deleteMany({ + where: { id: params.id, userId: user.id }, + }) + if (client.count === 0) return NextResponse.json({ error: 'Client non trouvé' }, { status: 404 }) + + return NextResponse.json({ success: true }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/clients/route.ts b/src/app/api/clients/route.ts new file mode 100644 index 0000000..a5c8996 --- /dev/null +++ b/src/app/api/clients/route.ts @@ -0,0 +1,126 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' +import { differenceInMonths, startOfMonth } from 'date-fns' + +export async function GET(request: Request) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const { searchParams } = new URL(request.url) + const type = searchParams.get('type') + + const where: any = { userId: user.id } + if (type === 'PROSPECT' || type === 'CLIENT') where.type = type + + const clientsRaw = await prisma.client.findMany({ + where, + orderBy: { createdAt: 'desc' }, + include: { + _count: { select: { quotes: true, contracts: true, sales: true } }, + contracts: { select: { monthlyPrice: true, setupPrice: true, startDate: true, endDate: true, status: true } }, + sales: { select: { totalAmount: true, paid: true } }, + }, + }) + + const serverCosts: any[] = await prisma.$queryRawUnsafe(` + SELECT cs."clientId", s."monthlyCost", + (SELECT COUNT(*) FROM "ClientServer" cs2 WHERE cs2."serverId" = s.id) as "clientCount" + FROM "ClientServer" cs + JOIN "Server" s ON s.id = cs."serverId" + `) + const costByClient: Record = {} + for (const sc of serverCosts) { + const share = sc.clientCount > 0 ? Number(sc.monthlyCost) / Number(sc.clientCount) : 0 + costByClient[sc.clientId] = (costByClient[sc.clientId] || 0) + share + } + + const serverCounts: any[] = await prisma.$queryRawUnsafe(` + SELECT "clientId", COUNT(*) as count + FROM "ClientServer" + GROUP BY "clientId" + `) + const countByClient: Record = {} + for (const sc of serverCounts) { + countByClient[sc.clientId] = Number(sc.count) + } + + const now = new Date() + const clients = clientsRaw.map((c) => { + const activeContracts = c.contracts.filter(ct => ct.status === 'ACTIVE') + const contractSetup = activeContracts.reduce((s, ct) => s + ct.setupPrice, 0) + const contractTotalMonthly = activeContracts.reduce((s, ct) => { + const start = startOfMonth(ct.startDate) + const end = ct.endDate ? startOfMonth(ct.endDate) : startOfMonth(now) + const months = Math.max(0, differenceInMonths(end, start) + 1) + return s + ct.monthlyPrice * months + }, 0) + const salesTotal = c.sales.reduce((s, sale) => s + sale.totalAmount, 0) + const totalInvoiced = contractSetup + contractTotalMonthly + salesTotal + const totalPaid = c.sales.filter((s) => s.paid).reduce((s, sale) => s + sale.totalAmount, 0) + const totalUnpaid = totalInvoiced - totalPaid + return { + id: c.id, + name: c.name, + email: c.email, + phone: c.phone, + address: c.address, + siret: c.siret, + type: c.type, + isProfessional: c.isProfessional, + notes: c.notes, + userId: c.userId, + createdAt: c.createdAt, + updatedAt: c.updatedAt, + _count: c._count, + contractSetup, + contractTotalMonthly, + contractMonthly: c.contracts.reduce((s, ct) => s + ct.monthlyPrice, 0), + salesTotal, + totalInvoiced, + totalPaid, + totalUnpaid, + serverCount: countByClient[c.id] || 0, + serverCostShare: costByClient[c.id] || 0, + } + }) + + return NextResponse.json({ clients }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function POST(request: Request) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const data = await request.json() + const client = await prisma.client.create({ + data: { + name: data.name, + email: data.email || null, + phone: data.phone || null, + address: data.address || null, + siret: data.siret || null, + type: data.type || 'PROSPECT', + notes: data.notes || null, + userId: user.id, + }, + }) + + if (data.serverIds && data.serverIds.length > 0) { + for (const serverId of data.serverIds) { + await prisma.$executeRawUnsafe(` + INSERT INTO "ClientServer" ("clientId", "serverId") VALUES ($1, $2) + `, client.id, serverId) + } + } + + return NextResponse.json({ client }, { status: 201 }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/contracts/[id]/route.ts b/src/app/api/contracts/[id]/route.ts new file mode 100644 index 0000000..01de241 --- /dev/null +++ b/src/app/api/contracts/[id]/route.ts @@ -0,0 +1,100 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' +import { startOfMonth, eachMonthOfInterval, isBefore, isAfter } from 'date-fns' + +export async function GET(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const contract = await prisma.contract.findFirst({ + where: { id: params.id, userId: user.id }, + include: { client: true, invoices: true }, + }) + if (!contract) return NextResponse.json({ error: 'Contrat non trouvé' }, { status: 404 }) + + return NextResponse.json({ contract }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function PUT(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const data = await request.json() + + const oldContract: any[] = await prisma.$queryRawUnsafe(` + SELECT status, "startDate", "monthlyPrice" FROM "Contract" WHERE id = $1 AND "userId" = $2 + `, params.id, user.id) + + const contract = await prisma.contract.updateMany({ + where: { id: params.id, userId: user.id }, + data: { + clientId: data.clientId, + name: data.name, + startDate: data.startDate ? new Date(data.startDate) : undefined, + endDate: data.endDate ? new Date(data.endDate) : null, + setupPrice: data.setupPrice || 0, + setupCost: data.setupCost || 0, + monthlyPrice: data.monthlyPrice || 0, + monthlyCost: data.monthlyCost || 0, + status: data.status, + billingDay: data.billingDay, + notes: data.notes, + }, + }) + if (contract.count === 0) return NextResponse.json({ error: 'Contrat non trouvé' }, { status: 404 }) + + // Auto-generate invoices for ACTIVE contracts (on status change or startDate change) + if ((data.status === 'ACTIVE' || oldContract[0]?.status === 'ACTIVE') && (data.monthlyPrice || oldContract[0]?.monthlyPrice) > 0) { + const startDate = data.startDate || oldContract[0]?.startDate || new Date() + const startMonth = startOfMonth(new Date(startDate)) + const today = startOfMonth(new Date()) + const months = eachMonthOfInterval({ start: startMonth, end: today }) + + for (const month of months) { + if (isBefore(month, startMonth)) continue + if (isAfter(month, today)) continue + + const existing: any[] = await prisma.$queryRawUnsafe(` + SELECT id FROM "Invoice" WHERE "contractId" = $1 AND month = $2 + `, params.id, month) + + if (existing.length > 0) continue + + await prisma.$executeRawUnsafe(` + INSERT INTO "Invoice" ("contractId", month, amount) + VALUES ($1, $2, $3) + `, params.id, month, data.monthlyPrice || oldContract[0]?.monthlyPrice || 0) + } + } + + const updated = await prisma.contract.findUnique({ + where: { id: params.id }, + include: { client: true }, + }) + return NextResponse.json({ contract: updated }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function DELETE(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const contract = await prisma.contract.deleteMany({ + where: { id: params.id, userId: user.id }, + }) + if (contract.count === 0) return NextResponse.json({ error: 'Contrat non trouvé' }, { status: 404 }) + + return NextResponse.json({ success: true }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/contracts/route.ts b/src/app/api/contracts/route.ts new file mode 100644 index 0000000..dae664e --- /dev/null +++ b/src/app/api/contracts/route.ts @@ -0,0 +1,55 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function GET(request: Request) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const contracts = await prisma.contract.findMany({ + where: { userId: user.id }, + orderBy: { createdAt: 'desc' }, + include: { + client: { select: { id: true, name: true } }, + _count: { select: { invoices: true } }, + }, + }) + + return NextResponse.json({ contracts }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function POST(request: Request) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const data = await request.json() + const contract = await prisma.contract.create({ + data: { + clientId: data.clientId, + userId: user.id, + quoteId: data.quoteId || null, + name: data.name, + startDate: new Date(data.startDate), + endDate: data.endDate ? new Date(data.endDate) : null, + setupPrice: data.setupPrice || 0, + setupCost: data.setupCost || 0, + monthlyPrice: data.monthlyPrice || 0, + monthlyCost: data.monthlyCost || 0, + status: data.status || 'ACTIVE', + billingDay: data.billingDay || 1, + notes: data.notes || null, + }, + include: { client: true }, + }) + + return NextResponse.json({ contract }, { status: 201 }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/dashboard/route.ts b/src/app/api/dashboard/route.ts new file mode 100644 index 0000000..53a99da --- /dev/null +++ b/src/app/api/dashboard/route.ts @@ -0,0 +1,287 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' +import { startOfMonth, endOfMonth, startOfYear, eachMonthOfInterval, format } from 'date-fns' +import { fr } from 'date-fns/locale' + +export async function GET() { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const now = new Date() + const monthStart = startOfMonth(now) + const monthEnd = endOfMonth(now) + const yearStart = startOfYear(now) + + const contracts = await prisma.contract.findMany({ + where: { userId: user.id, status: 'ACTIVE' }, + select: { monthlyPrice: true, monthlyCost: true, setupPrice: true, setupCost: true }, + }) + + const serverCosts: any[] = await prisma.$queryRawUnsafe(` + SELECT COALESCE(SUM(s."monthlyCost"), 0) as total + FROM "Server" s + WHERE s."userId" = $1 AND s.status = 'ACTIVE' + `, user.id) + const serverMonthlyCost = Number(serverCosts[0]?.total || 0) + + const clientServerCounts: any[] = await prisma.$queryRawUnsafe(` + SELECT cs."serverId", s."monthlyCost", COUNT(cs2."clientId") as client_count + FROM "ClientServer" cs + JOIN "Server" s ON s.id = cs."serverId" + JOIN "ClientServer" cs2 ON cs2."serverId" = s.id + GROUP BY cs."serverId", s."monthlyCost" + `) + let totalServerCostShared = 0 + for (const row of clientServerCounts) { + if (row.client_count > 0) { + totalServerCostShared += Number(row.monthlyCost) / Number(row.client_count) + } + } + + const monthlyRecurringRevenue = contracts.reduce((sum: number, c) => sum + c.monthlyPrice, 0) + const contractRecurringCost = contracts.reduce((sum: number, c) => sum + c.monthlyCost, 0) + const monthlyRecurringCost = contractRecurringCost + serverMonthlyCost + const monthlyMargin = monthlyRecurringRevenue - monthlyRecurringCost + const monthlyMarginPercent = monthlyRecurringRevenue > 0 ? (monthlyMargin / monthlyRecurringRevenue) * 100 : 0 + + const monthSales = await prisma.sale.findMany({ + where: { userId: user.id, date: { gte: monthStart, lte: monthEnd }, paid: true }, + select: { totalAmount: true, totalCost: true }, + }) + const salesThisMonth = monthSales.reduce((sum, s) => sum + s.totalAmount, 0) + const salesCostThisMonth = monthSales.reduce((sum, s) => sum + s.totalCost, 0) + const costThisMonth = contractRecurringCost + salesCostThisMonth + + const monthInvoices: any[] = await prisma.$queryRawUnsafe(` + SELECT COALESCE(SUM(inv.amount), 0) as total + FROM "Invoice" inv + JOIN "Contract" c ON c.id = inv."contractId" + WHERE c."userId" = $1 AND inv.month >= $2 AND inv.month <= $3 + `, user.id, monthStart, monthEnd) + const invoicedThisMonth = salesThisMonth + (Number(monthInvoices[0]?.total) || 0) + + const activeClients = await prisma.client.count({ + where: { userId: user.id, type: 'CLIENT' }, + }) + const prospectsCount = await prisma.client.count({ + where: { userId: user.id, type: 'PROSPECT' }, + }) + + const allClients = await prisma.client.findMany({ + where: { userId: user.id, type: 'CLIENT' }, + include: { + contracts: { where: { status: 'ACTIVE' }, select: { monthlyPrice: true } }, + sales: { where: { paid: true }, select: { totalAmount: true } }, + }, + }) + + const invoiceByClient: any[] = await prisma.$queryRawUnsafe(` + SELECT c."clientId", COALESCE(SUM(inv.amount), 0) as total + FROM "Invoice" inv + JOIN "Contract" c ON c.id = inv."contractId" + WHERE c."userId" = $1 + GROUP BY c."clientId" + `, user.id) + const invoiceByClientMap: Record = {} + for (const row of invoiceByClient) { + invoiceByClientMap[row.clientId] = Number(row.total) + } + + const topClients = allClients + .map((c) => { + const monthlyRecurring = c.contracts.reduce((sum, ct) => sum + ct.monthlyPrice, 0) + const oneTimeTotal = c.sales.reduce((sum, s) => sum + s.totalAmount, 0) + const invoiceTotal = invoiceByClientMap[c.id] || 0 + return { + id: c.id, + name: c.name, + monthlyRecurring, + oneTimeTotal, + invoiceTotal, + totalRevenue: monthlyRecurring + oneTimeTotal + invoiceTotal, + contractCount: c.contracts.length, + } + }) + .sort((a, b) => b.totalRevenue - a.totalRevenue) + .slice(0, 5) + + const months = eachMonthOfInterval({ start: yearStart, end: now }) + + const allContractsForChart = await prisma.contract.findMany({ + where: { userId: user.id }, + select: { startDate: true, endDate: true, monthlyPrice: true, monthlyCost: true, setupPrice: true, setupCost: true, status: true }, + }) + + const allUserContracts = await prisma.contract.findMany({ + where: { userId: user.id }, + select: { status: true, monthlyPrice: true }, + }) + const proposalContracts = allUserContracts.filter(c => (c.status as string) === 'PROPOSAL') + const pausedContracts = allUserContracts.filter(c => (c.status as string) === 'PAUSED') + const pendingProposalMonthly = proposalContracts.reduce((sum, c) => sum + c.monthlyPrice, 0) + const pendingProposalCount = proposalContracts.length + const pausedMonthly = pausedContracts.reduce((sum, c) => sum + c.monthlyPrice, 0) + const pausedCount = pausedContracts.length + + const topQuotes = await prisma.quote.findMany({ + where: { userId: user.id }, + orderBy: { createdAt: 'desc' }, + take: 5, + include: { + client: { select: { name: true } }, + }, + }) + const topQuotesSorted = topQuotes + .map((q) => ({ + id: q.id, + name: q.name, + clientName: q.client.name, + totalValue: q.setupTotal + q.monthlyTotal, + status: q.status, + })) + .sort((a, b) => b.totalValue - a.totalValue) + .slice(0, 5) + + const proposalQuotesTotal: any[] = await prisma.$queryRawUnsafe(` + SELECT COALESCE(SUM("setupTotal"), 0) as total + FROM "Quote" + WHERE "userId" = $1 AND status IN ('DRAFT', 'PROPOSAL', 'SENT') + `, user.id) + const totalProposalValue = Number(proposalQuotesTotal[0]?.total || 0) + + const unpaidSales: any[] = await prisma.$queryRawUnsafe(` + SELECT COALESCE(SUM("totalAmount"), 0) as total + FROM "Sale" + WHERE "userId" = $1 AND paid = false AND proposal = false + `, user.id) + const unpaidSalesAmount = Number(unpaidSales[0]?.total || 0) + + const unpaidInvoices: any[] = await prisma.$queryRawUnsafe(` + SELECT COALESCE(SUM(inv.amount), 0) as total + FROM "Invoice" inv + JOIN "Contract" c ON c.id = inv."contractId" + WHERE c."userId" = $1 AND inv.emitted = true AND inv."paidAt" IS NULL + `, user.id) + const unpaidRevenue = unpaidSalesAmount + (Number(unpaidInvoices[0]?.total) || 0) + + const allSalesForChart: any[] = await prisma.$queryRawUnsafe(` + SELECT date, "totalAmount", "totalCost" + FROM "Sale" + WHERE "userId" = $1 AND paid = true AND proposal = false + ORDER BY date ASC + `, user.id) + + const allInvoicesForChart: any[] = await prisma.$queryRawUnsafe(` + SELECT inv.month, inv.amount + FROM "Invoice" inv + JOIN "Contract" c ON c.id = inv."contractId" + WHERE c."userId" = $1 + `, user.id) + const invoicesByMonth: Record = {} + for (const inv of allInvoicesForChart) { + const m = startOfMonth(new Date(inv.month)) + const key = format(m, 'yyyy-MM-dd') + invoicesByMonth[key] = (invoicesByMonth[key] || 0) + Number(inv.amount) + } + + const newClientsByMonth: any[] = await prisma.$queryRawUnsafe(` + SELECT DATE_TRUNC('month', c."startDate") as month, COUNT(DISTINCT c."clientId") as count + FROM "Contract" c + WHERE c."userId" = $1 + GROUP BY DATE_TRUNC('month', c."startDate") + ORDER BY month ASC + `, user.id) + const newClientsMap: Record = {} + for (const row of newClientsByMonth) { + const m = startOfMonth(new Date(row.month)) + const key = format(m, 'yyyy-MM-dd') + newClientsMap[key] = Number(row.count) + } + + const chartData = months.map((month) => { + const mStart = startOfMonth(month) + const mEnd = endOfMonth(month) + + let recurringRevenue = 0 + let recurringCost = 0 + let oneTimeRevenue = 0 + let oneTimeCost = 0 + let invoiceRevenue = 0 + + const monthKey = format(month, 'yyyy-MM-dd') + invoiceRevenue = invoicesByMonth[monthKey] || 0 + const newClients = newClientsMap[monthKey] || 0 + + for (const ct of allContractsForChart) { + if (ct.status === 'ACTIVE' && ct.startDate <= mEnd && (!ct.endDate || ct.endDate >= mStart)) { + recurringRevenue += ct.monthlyPrice + recurringCost += ct.monthlyCost + } + if (ct.startDate >= mStart && ct.startDate <= mEnd) { + oneTimeRevenue += ct.setupPrice + oneTimeCost += ct.setupCost + } + } + + for (const s of allSalesForChart) { + if (s.date >= mStart && s.date <= mEnd) { + oneTimeRevenue += s.totalAmount + oneTimeCost += s.totalCost + } + } + + return { + month: format(month, 'MMM', { locale: fr }), + monthLabel: format(month, 'MMMM yyyy', { locale: fr }), + recurringRevenue, + recurringCost, + oneTimeRevenue, + oneTimeCost, + invoiceRevenue, + newClients, + revenue: recurringRevenue + oneTimeRevenue, + cost: recurringCost + oneTimeCost + (serverMonthlyCost / 12), + margin: (recurringRevenue + oneTimeRevenue) - (recurringCost + oneTimeCost + (serverMonthlyCost / 12)), + } + }) + + const yearlyCA = chartData.reduce((sum, m) => sum + m.revenue, 0) + + return NextResponse.json({ + stats: { + yearlyCA, + monthlyRecurringRevenue, + monthlyRecurringCost, + monthlyMargin, + monthlyMarginPercent, + invoicedThisMonth, + costThisMonth, + salesCostThisMonth, + marginThisMonth: (monthlyRecurringRevenue + invoicedThisMonth) - costThisMonth, + activeContracts: contracts.length, + activeClients, + prospectsCount, + topClients, + unpaidRevenue, + pendingProposalCount, + pendingProposalMonthly, + pausedCount, + pausedMonthly, + topQuotes: topQuotesSorted, + totalProposalValue, + projectedMonthlyRecurring: monthlyRecurringRevenue + pendingProposalMonthly + pausedMonthly, + projectedOneTimeSetup: 0, + projectedTotal: monthlyRecurringRevenue + pendingProposalMonthly + pausedMonthly, + serverMonthlyCost, + totalServerCostShared, + totalServerCostSharedRounded: Math.round(totalServerCostShared * 100) / 100, + }, + chartData, + }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/invoices/generate/route.ts b/src/app/api/invoices/generate/route.ts new file mode 100644 index 0000000..c069677 --- /dev/null +++ b/src/app/api/invoices/generate/route.ts @@ -0,0 +1,54 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' +import { startOfMonth, eachMonthOfInterval, isBefore, isAfter } from 'date-fns' + +export async function POST() { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const contracts: any[] = await prisma.$queryRawUnsafe(` + SELECT id, "startDate", "endDate", "monthlyPrice" FROM "Contract" + WHERE "userId" = $1 AND status = 'ACTIVE' + `, user.id) + + let created = 0 + let skipped = 0 + const today = startOfMonth(new Date()) + + for (const contract of contracts) { + const startMonth = startOfMonth(new Date(contract.startDate)) + const endMonth = contract.endDate ? startOfMonth(new Date(contract.endDate)) : today + + const months = eachMonthOfInterval({ start: startMonth, end: endMonth }) + + for (const month of months) { + if (isBefore(month, startMonth)) continue + if (isAfter(month, today)) continue + + const existing: any[] = await prisma.$queryRawUnsafe(` + SELECT id FROM "Invoice" + WHERE "contractId" = $1 AND month = $2 + `, contract.id, month) + + if (existing.length > 0) { + skipped++ + continue + } + + const id = `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}` + await prisma.$executeRawUnsafe(` + INSERT INTO "Invoice" (id, "contractId", month, amount, "updatedAt") + VALUES ($1, $2, $3, $4, $5) + `, id, contract.id, month, contract.monthlyPrice, new Date()) + created++ + } + } + + return NextResponse.json({ created, skipped }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/invoices/notifications/route.ts b/src/app/api/invoices/notifications/route.ts new file mode 100644 index 0000000..d830a44 --- /dev/null +++ b/src/app/api/invoices/notifications/route.ts @@ -0,0 +1,26 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function GET() { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const result: any[] = await prisma.$queryRawUnsafe(` + SELECT COUNT(*)::int as count FROM "Invoice" inv + JOIN "Contract" c ON c.id = inv."contractId" + WHERE c."userId" = $1 + AND (inv."invoiceNumber" IS NULL OR inv."invoiceNumber" = '' + OR inv.emitted IS NOT TRUE + OR inv."paidAt" IS NULL) + `, user.id) + + const count = result[0]?.count ?? 0 + + return NextResponse.json({ count }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/invoices/route.ts b/src/app/api/invoices/route.ts new file mode 100644 index 0000000..2ac903e --- /dev/null +++ b/src/app/api/invoices/route.ts @@ -0,0 +1,108 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function GET() { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const rows: any[] = await prisma.$queryRawUnsafe(` + SELECT inv.id, inv.month, inv.amount, inv."invoiceNumber", + inv.emitted, inv."emittedAt", inv."paidAt", + c.id as "contractId", c.name as "contractName", + cl.id as "clientId", cl.name as "clientName" + FROM "Invoice" inv + JOIN "Contract" c ON c.id = inv."contractId" + JOIN "Client" cl ON cl.id = c."clientId" + WHERE c."userId" = $1 + ORDER BY inv.month DESC, cl.name ASC + `, user.id) + + const invoices = rows.map((r: any) => ({ + id: r.id, + month: r.month instanceof Date ? r.month.toISOString() : r.month, + amount: r.amount, + invoiceNumber: r.invoiceNumber, + emitted: r.emitted, + emittedAt: r.emittedAt instanceof Date ? r.emittedAt.toISOString() : r.emittedAt, + paidAt: r.paidAt instanceof Date ? r.paidAt.toISOString() : r.paidAt, + contract: { + id: r.contractId, + name: r.contractName, + client: { id: r.clientId, name: r.clientName }, + }, + })) + + return NextResponse.json({ invoices }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function PATCH(request: Request) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const data = await request.json() + const { id, invoiceNumber, emitted, paidAt } = data + + if (invoiceNumber !== undefined) { + await prisma.$executeRawUnsafe( + `UPDATE "Invoice" SET "invoiceNumber" = $1 FROM "Contract" c WHERE "Invoice"."contractId" = c.id AND "Invoice".id = $2 AND c."userId" = $3`, + invoiceNumber || null, id, user.id + ) + } + if (emitted !== undefined) { + if (emitted) { + await prisma.$executeRawUnsafe( + `UPDATE "Invoice" SET emitted = $1, "emittedAt" = NOW() FROM "Contract" c WHERE "Invoice"."contractId" = c.id AND "Invoice".id = $2 AND c."userId" = $3`, + emitted, id, user.id + ) + } else { + await prisma.$executeRawUnsafe( + `UPDATE "Invoice" SET emitted = $1 FROM "Contract" c WHERE "Invoice"."contractId" = c.id AND "Invoice".id = $2 AND c."userId" = $3`, + emitted, id, user.id + ) + } + } + if (paidAt !== undefined) { + const paidAtDate = paidAt ? new Date(paidAt + 'T00:00:00') : null + await prisma.$executeRawUnsafe( + `UPDATE "Invoice" SET "paidAt" = $1 FROM "Contract" c WHERE "Invoice"."contractId" = c.id AND "Invoice".id = $2 AND c."userId" = $3`, + paidAtDate, id, user.id + ) + } + + return NextResponse.json({ success: true }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function DELETE(request: Request) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const { searchParams } = new URL(request.url) + const id = searchParams.get('id') + if (!id) return NextResponse.json({ error: 'ID requis' }, { status: 400 }) + + const result = await prisma.$executeRawUnsafe( + `DELETE FROM "Invoice" USING "Contract" c WHERE "Invoice"."contractId" = c.id AND "Invoice".id = $1 AND c."userId" = $2`, + id, user.id + ) + if (result === 0) { + return NextResponse.json({ error: 'Facture non trouvée' }, { status: 404 }) + } + + return NextResponse.json({ success: true }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/quotes/[id]/route.ts b/src/app/api/quotes/[id]/route.ts new file mode 100644 index 0000000..d7db30b --- /dev/null +++ b/src/app/api/quotes/[id]/route.ts @@ -0,0 +1,96 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function GET(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const quote = await prisma.quote.findFirst({ + where: { id: params.id, userId: user.id }, + include: { items: true, client: true }, + }) + if (!quote) return NextResponse.json({ error: 'Devis non trouvé' }, { status: 404 }) + + return NextResponse.json({ quote }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function PUT(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const data = await request.json() + + await prisma.quoteItem.deleteMany({ where: { quoteId: params.id } }) + + const quote = await prisma.quote.updateMany({ + where: { id: params.id, userId: user.id }, + data: { + clientId: data.clientId, + name: data.name, + quoteNumber: data.quoteNumber || null, + status: data.status, + setupTotal: data.setupTotal || 0, + monthlyTotal: data.monthlyTotal || 0, + costTotal: data.costTotal || 0, + marginTotal: data.marginTotal || 0, + discount: data.discount || 0, + travelKm: data.travelKm || 0, + travelCost: data.travelCost || 0, + notes: data.notes || null, + }, + }) + + if (quote.count === 0) return NextResponse.json({ error: 'Devis non trouvé' }, { status: 404 }) + + if (data.items) { + for (const item of data.items) { + await prisma.quoteItem.create({ + data: { + quoteId: params.id, + type: item.type, + label: item.label, + description: item.description || null, + unitPrice: item.unitPrice || 0, + costPrice: item.costPrice || 0, + quantity: item.quantity || 1, + isRecurring: item.isRecurring || false, + setupPrice: item.setupPrice || 0, + setupCost: item.setupCost || 0, + monthlyPrice: item.monthlyPrice || 0, + monthlyCost: item.monthlyCost || 0, + }, + }) + } + } + + const updated = await prisma.quote.findUnique({ + where: { id: params.id }, + include: { items: true, client: true }, + }) + return NextResponse.json({ quote: updated }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function DELETE(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const quote = await prisma.quote.deleteMany({ + where: { id: params.id, userId: user.id }, + }) + if (quote.count === 0) return NextResponse.json({ error: 'Devis non trouvé' }, { status: 404 }) + + return NextResponse.json({ success: true }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/quotes/route.ts b/src/app/api/quotes/route.ts new file mode 100644 index 0000000..b840793 --- /dev/null +++ b/src/app/api/quotes/route.ts @@ -0,0 +1,70 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function GET(request: Request) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const quotes = await prisma.quote.findMany({ + where: { userId: user.id }, + orderBy: { createdAt: 'desc' }, + include: { + client: { select: { id: true, name: true } }, + items: true, + }, + }) + + return NextResponse.json({ quotes }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function POST(request: Request) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const data = await request.json() + const quote = await prisma.quote.create({ + data: { + clientId: data.clientId, + userId: user.id, + name: data.name, + quoteNumber: data.quoteNumber || null, + status: 'DRAFT', + setupTotal: data.setupTotal || 0, + monthlyTotal: data.monthlyTotal || 0, + costTotal: data.costTotal || 0, + marginTotal: data.marginTotal || 0, + discount: data.discount || 0, + travelKm: data.travelKm || 0, + travelCost: data.travelCost || 0, + notes: data.notes || null, + items: { + create: (data.items || []).map((item: any) => ({ + type: item.type, + label: item.label, + description: item.description || null, + unitPrice: item.unitPrice || 0, + costPrice: item.costPrice || 0, + quantity: item.quantity || 1, + isRecurring: item.isRecurring || false, + setupPrice: item.setupPrice || 0, + setupCost: item.setupCost || 0, + monthlyPrice: item.monthlyPrice || 0, + monthlyCost: item.monthlyCost || 0, + })), + }, + }, + include: { items: true, client: true }, + }) + + return NextResponse.json({ quote }, { status: 201 }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/sales/[id]/route.ts b/src/app/api/sales/[id]/route.ts new file mode 100644 index 0000000..70b4c7a --- /dev/null +++ b/src/app/api/sales/[id]/route.ts @@ -0,0 +1,131 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function GET(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const sale = await prisma.sale.findFirst({ + where: { id: params.id, userId: user.id }, + include: { items: true, client: true }, + }) + if (!sale) return NextResponse.json({ error: 'Vente non trouvée' }, { status: 404 }) + + return NextResponse.json({ sale }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function PUT(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const data = await request.json() + let clientId = data.clientId + + if (!clientId && data.clientName) { + const client = await prisma.client.create({ + data: { + name: data.clientName, + email: data.clientEmail || null, + phone: data.clientPhone || null, + address: data.clientAddress || null, + siret: data.clientSiret || null, + isProfessional: data.isProfessional ?? true, + type: 'CLIENT', + userId: user.id, + }, + }) + clientId = client.id + } + + const updateData: any = {} + if (data.paid !== undefined) updateData.paid = data.paid + if (data.clientId !== undefined || data.clientName) updateData.clientId = clientId + if (data.date) updateData.date = new Date(data.date) + if (data.totalAmount !== undefined) updateData.totalAmount = data.totalAmount + if (data.totalCost !== undefined) updateData.totalCost = data.totalCost + if (data.notes !== undefined) updateData.notes = data.notes + + const sale = await prisma.sale.updateMany({ + where: { id: params.id, userId: user.id }, + data: updateData, + }) + + if (sale.count === 0) return NextResponse.json({ error: 'Vente non trouvée' }, { status: 404 }) + + if (data.paid !== undefined) { + await prisma.$executeRawUnsafe(`UPDATE "Sale" SET paid = $1 WHERE id = $2`, data.paid, params.id) + } + + if (data.proposal !== undefined) { + await prisma.$executeRawUnsafe(`UPDATE "Sale" SET proposal = $1 WHERE id = $2`, data.proposal, params.id) + } + + if (data.items) { + await prisma.$executeRawUnsafe(`DELETE FROM "SaleItem" WHERE "saleId" = $1`, params.id) + const now = new Date() + for (const item of data.items) { + const itemId = `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}` + await prisma.$executeRawUnsafe(` + INSERT INTO "SaleItem" (id, "saleId", label, description, quantity, "unitPrice", "costPrice", "createdAt") + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + `, itemId, params.id, item.label, item.description || null, item.quantity || 1, item.unitPrice || 0, item.costPrice || 0, now) + } + } + + const [updated]: any[] = await prisma.$queryRawUnsafe(` + SELECT s.id, s.paid, s.proposal, s."totalAmount", s."totalCost", s.date, s.notes, + c.id as "clientId", c.name as "clientName" + FROM "Sale" s + JOIN "Client" c ON c.id = s."clientId" + WHERE s.id = $1 + `, params.id) + + const updatedItems: any[] = await prisma.$queryRawUnsafe(` + SELECT id, label, description, quantity, "unitPrice", "costPrice" + FROM "SaleItem" + WHERE "saleId" = $1 + ORDER BY "createdAt" ASC + `, params.id) + + return NextResponse.json({ + sale: { + id: updated.id, + client: { id: updated.clientId, name: updated.clientName }, + date: updated.date, + totalAmount: updated.totalAmount, + totalCost: updated.totalCost, + paid: updated.paid, + proposal: updated.proposal, + notes: updated.notes, + items: updatedItems.map((i: any) => ({ + id: i.id, label: i.label, description: i.description, + quantity: i.quantity, unitPrice: i.unitPrice, costPrice: i.costPrice, + })), + }, + }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function DELETE(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const sale = await prisma.sale.deleteMany({ + where: { id: params.id, userId: user.id }, + }) + if (sale.count === 0) return NextResponse.json({ error: 'Vente non trouvée' }, { status: 404 }) + + return NextResponse.json({ success: true }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/sales/notifications/route.ts b/src/app/api/sales/notifications/route.ts new file mode 100644 index 0000000..4f56bc5 --- /dev/null +++ b/src/app/api/sales/notifications/route.ts @@ -0,0 +1,19 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function GET() { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const count = await prisma.sale.count({ + where: { userId: user.id, paid: false }, + }) + + return NextResponse.json({ count }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/sales/route.ts b/src/app/api/sales/route.ts new file mode 100644 index 0000000..88b7bfb --- /dev/null +++ b/src/app/api/sales/route.ts @@ -0,0 +1,131 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +function makeId() { + return `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}` +} + +export async function GET() { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const salesRaw: any[] = await prisma.$queryRawUnsafe(` + SELECT s.id, s.paid, s.proposal, s."totalAmount", s."totalCost", s.date, s.notes, s."createdAt", s."updatedAt", + c.id as "clientId", c.name as "clientName" + FROM "Sale" s + JOIN "Client" c ON c.id = s."clientId" + WHERE s."userId" = $1 + ORDER BY s.date DESC + `, user.id) + + const itemsRaw: any[] = await prisma.$queryRawUnsafe(` + SELECT si.id, si."saleId", si.label, si.description, si.quantity, si."unitPrice", si."costPrice" + FROM "SaleItem" si + JOIN "Sale" s ON s.id = si."saleId" + WHERE s."userId" = $1 + ORDER BY si."createdAt" ASC + `, user.id) + + const itemsBySale: Record = {} + for (const item of itemsRaw) { + if (!itemsBySale[item.saleId]) itemsBySale[item.saleId] = [] + itemsBySale[item.saleId].push({ + id: item.id, + label: item.label, + description: item.description, + quantity: item.quantity, + unitPrice: item.unitPrice, + costPrice: item.costPrice, + }) + } + + const sales = salesRaw.map((s) => ({ + id: s.id, + client: { id: s.clientId, name: s.clientName }, + date: s.date, + totalAmount: s.totalAmount, + totalCost: s.totalCost, + paid: s.paid, + proposal: s.proposal, + notes: s.notes, + items: itemsBySale[s.id] || [], + })) + + return NextResponse.json({ sales }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function POST(request: Request) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const data = await request.json() + let clientId = data.clientId + + if (!clientId) { + const client = await prisma.client.create({ + data: { + name: data.clientName, + email: data.clientEmail || null, + phone: data.clientPhone || null, + address: data.clientAddress || null, + siret: data.clientSiret || null, + isProfessional: data.isProfessional ?? true, + type: 'CLIENT', + userId: user.id, + }, + }) + clientId = client.id + } + + const now = new Date() + const saleId = makeId() + const saleDate = data.date ? new Date(data.date) : now + + await prisma.$executeRawUnsafe(` + INSERT INTO "Sale" (id, "clientId", "userId", date, "totalAmount", "totalCost", paid, proposal, notes, "createdAt", "updatedAt") + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) + `, saleId, clientId, user.id, saleDate, data.totalAmount || 0, data.totalCost || 0, + data.paid === true, data.proposal === true, data.notes || null, now, now) + + const items = data.items || [] + for (const item of items) { + await prisma.$executeRawUnsafe(` + INSERT INTO "SaleItem" (id, "saleId", label, description, quantity, "unitPrice", "costPrice", "createdAt") + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + `, makeId(), saleId, item.label, item.description || null, item.quantity || 1, item.unitPrice || 0, item.costPrice || 0, now) + } + + const client: any[] = await prisma.$queryRawUnsafe(` + SELECT id, name FROM "Client" WHERE id = $1 + `, clientId) + + return NextResponse.json({ + sale: { + id: saleId, + client: client[0] || { id: clientId, name: '' }, + date: saleDate, + totalAmount: data.totalAmount || 0, + totalCost: data.totalCost || 0, + paid: data.paid === true, + proposal: data.proposal === true, + notes: data.notes || null, + items: items.map((i: any) => ({ + label: i.label, + description: i.description || null, + quantity: i.quantity || 1, + unitPrice: i.unitPrice || 0, + costPrice: i.costPrice || 0, + })), + }, + }, { status: 201 }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/servers/[id]/metrics/route.ts b/src/app/api/servers/[id]/metrics/route.ts new file mode 100644 index 0000000..eb12bc8 --- /dev/null +++ b/src/app/api/servers/[id]/metrics/route.ts @@ -0,0 +1,51 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function GET(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifi??' }, { status: 401 }) + + const metrics: any[] = await prisma.$queryRawUnsafe(` + SELECT * FROM "ServerMetric" + WHERE "serverId" = $1 + ORDER BY "timestamp" DESC + LIMIT 60 + `, params.id) + + return NextResponse.json(metrics) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function POST(request: Request, { params }: { params: { id: string } }) { + try { + const { searchParams } = new URL(request.url) + const token = searchParams.get('token') + const expectedToken = process.env.SERVER_MONITOR_SECRET + + if (expectedToken && token !== expectedToken) { + return NextResponse.json({ error: 'Non autoris??' }, { status: 401 }) + } + + const body = await request.json() + const metricId = `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}` + + await prisma.$executeRawUnsafe(` + INSERT INTO "ServerMetric" (id, "serverId", "cpuUsage", "ramUsage", "diskUsage", uptime, "timestamp") + VALUES ($1, $2, $3, $4, $5, $6, NOW()) + `, metricId, params.id, + body.cpuUsage != null ? Number(body.cpuUsage) : null, + body.ramUsage != null ? Number(body.ramUsage) : null, + body.diskUsage != null ? Number(body.diskUsage) : null, + body.uptime != null ? BigInt(body.uptime) : null) + + return NextResponse.json({ success: true }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/servers/[id]/route.ts b/src/app/api/servers/[id]/route.ts new file mode 100644 index 0000000..74369e8 --- /dev/null +++ b/src/app/api/servers/[id]/route.ts @@ -0,0 +1,110 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function GET(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifi??' }, { status: 401 }) + + const servers: any[] = await prisma.$queryRawUnsafe(` + SELECT s.*, + (SELECT json_agg(json_build_object( + 'cpuUsage', sm."cpuUsage", + 'ramUsage', sm."ramUsage", + 'diskUsage', sm."diskUsage", + 'uptime', sm.uptime, + 'timestamp', sm."timestamp" + ) ORDER BY sm."timestamp" DESC) FROM "ServerMetric" sm + WHERE sm."serverId" = s.id + LIMIT 60) as metrics + FROM "Server" s + WHERE s.id = $1 AND s."userId" = $2 + `, params.id, user.id) + + if (!servers.length) { + return NextResponse.json({ error: 'Serveur introuvable' }, { status: 404 }) + } + + return NextResponse.json(servers[0]) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function PATCH(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifi??' }, { status: 401 }) + + const body = await request.json() + const now = new Date() + + const fields: string[] = [] + const values: any[] = [] + let idx = 1 + + for (const key of ['name', 'ip', 'os', 'cpu', 'ram', 'disk', 'status', 'notes']) { + if (body[key] !== undefined) { + fields.push(`"${key}" = $${idx++}`) + values.push(body[key]) + } + } + + for (const key of ['monthlyCost']) { + if (body[key] !== undefined) { + fields.push(`"${key}" = $${idx++}`) + values.push(Number(body[key])) + } + } + + for (const key of ['startDate', 'endDate']) { + if (body[key] !== undefined) { + fields.push(`"${key}" = $${idx++}`) + values.push(body[key] ? new Date(body[key]) : null) + } + } + + if (fields.length === 0) { + return NextResponse.json({ error: 'Aucun champ ?? mettre ?? jour' }, { status: 400 }) + } + + fields.push(`"updatedAt" = $${idx++}`) + values.push(now) + values.push(params.id) + values.push(user.id) + + await prisma.$executeRawUnsafe(` + UPDATE "Server" SET ${fields.join(', ')} + WHERE id = $${idx} AND "userId" = $${idx + 1} + `, ...values) + + const updated: any[] = await prisma.$queryRawUnsafe(`SELECT * FROM "Server" WHERE id = $1`, params.id) + + return NextResponse.json(updated[0]) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function DELETE(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifi??' }, { status: 401 }) + + await prisma.$executeRawUnsafe(` + DELETE FROM "ServerMetric" WHERE "serverId" = $1 + `, params.id) + + await prisma.$executeRawUnsafe(` + DELETE FROM "Server" WHERE id = $1 AND "userId" = $2 + `, params.id, user.id) + + return NextResponse.json({ success: true }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/servers/[id]/services/discover/route.ts b/src/app/api/servers/[id]/services/discover/route.ts new file mode 100644 index 0000000..2560ede --- /dev/null +++ b/src/app/api/servers/[id]/services/discover/route.ts @@ -0,0 +1,164 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' +import { exec } from 'child_process' +import { promisify } from 'util' + +const execAsync = promisify(exec) + +export async function POST(request: Request, { params }: { params: { id: string } }) { + try { + const { searchParams } = new URL(request.url) + const token = searchParams.get('token') + const expectedToken = process.env.SERVER_MONITOR_SECRET + const useToken = expectedToken && token === expectedToken + + if (!useToken) { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifi??' }, { status: 401 }) + } + + const serverCheck: any[] = await prisma.$queryRawUnsafe(`SELECT id, name FROM "Server" WHERE id = $1`, params.id) + if (!serverCheck.length) return NextResponse.json({ error: 'Serveur introuvable' }, { status: 404 }) + + const discovered: any[] = [] + + // 1. Systemd services + try { + const { stdout } = await execAsync( + `systemctl list-units --type=service --all --no-legend --no-pager 2>/dev/null | head -50` + ) + for (const line of stdout.trim().split('\n')) { + const parts = line.trim().split(/\s+/) + if (parts.length < 4) continue + const name = parts[0].replace('.service', '') + const status = parts[3] === 'running' ? 'running' : parts[3] === 'exited' ? 'stopped' : 'error' + const pid = parts[3] === 'running' ? 1 : null + + discovered.push({ + name, + type: 'service', + status, + processName: parts[0], + technology: 'systemd', + }) + } + } catch {} + + // 2. Docker containers + try { + const { stdout } = await execAsync(`docker ps -a --format '{{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}' 2>/dev/null | head -30`) + for (const line of stdout.trim().split('\n')) { + if (!line.trim()) continue + const parts = line.trim().split('\t') + if (parts.length < 2) continue + const name = parts[0] + const image = parts[1] + const statusText = parts[2] || '' + const ports = parts[3] || '' + const isRunning = statusText.toLowerCase().includes('up') + const port = ports.match(/(\d+)->/) ? parseInt(ports.match(/(\d+)->/)![1]) : null + + discovered.push({ + name: `Docker: ${name}`, + type: 'container', + status: isRunning ? 'running' : 'stopped', + port, + technology: `Docker (${image})`, + processName: `docker:${name}`, + }) + } + } catch {} + + // 3. Processes listening on ports + try { + const { stdout } = await execAsync(`ss -tlnp4 2>/dev/null | tail -n +2 | head -30`) + for (const line of stdout.trim().split('\n')) { + if (!line.trim()) continue + const parts = line.trim().split(/\s+/) + if (parts.length < 5) continue + const addr = parts[4] + const port = addr.includes(':') ? parseInt(addr.split(':').pop()!) : null + const processInfo = parts[5] || '' + const procMatch = processInfo.match(/users:\(\("([^"]+)/) + const processName = procMatch ? procMatch[1] : 'unknown' + + if (port && port < 9000 && ![3005, 5432, 3306, 8082, 8086, 8096].includes(port)) { + discovered.push({ + name: `Port ${port}`, + type: 'port', + status: 'running', + port, + processName, + technology: 'TCP', + }) + } + } + } catch {} + + // 4. Nginx sites + try { + const { stdout } = await execAsync(`ls /etc/nginx/sites-enabled/ 2>/dev/null`) + for (const site of stdout.trim().split('\n')) { + if (!site.trim()) continue + const { stdout: port } = await execAsync( + `grep -E 'listen|proxy_pass|server_name' /etc/nginx/sites-enabled/${site} 2>/dev/null | head -5` + ) + discovered.push({ + name: `Nginx: ${site}`, + type: 'website', + status: 'running', + technology: `nginx\n${port}`, + processName: 'nginx', + }) + } + } catch {} + + // Insert discovered services + const now = new Date() + let insertedCount = 0 + const discoveredNames = new Set(discovered.map(s => s.name)) + + for (const svc of discovered) { + const existing: any[] = await prisma.$queryRawUnsafe(` + SELECT id FROM "Service" WHERE "serverId" = $1 AND name = $2 + `, params.id, svc.name) + + if (existing.length > 0) { + await prisma.$executeRawUnsafe(` + UPDATE "Service" SET status = $1, port = $2, technology = $3, "processName" = $4, "updatedAt" = $5 + WHERE id = $6 + `, svc.status, svc.port || null, svc.technology || '', svc.processName || '', now, existing[0].id) + } else { + const id = `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}` + await prisma.$executeRawUnsafe(` + INSERT INTO "Service" (id, "serverId", name, type, port, technology, status, "processName", "createdAt", "updatedAt") + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) + `, id, params.id, svc.name, svc.type, svc.port || null, svc.technology || '', svc.status, svc.processName || '', now, now) + insertedCount++ + } + } + + // Delete stale services that no longer exist on the server + let deleted = 0 + if (discoveredNames.size > 0) { + const namesArr = Array.from(discoveredNames) + const placeholders = namesArr.map((_, i) => `$${i + 2}`).join(', ') + await prisma.$executeRawUnsafe(` + DELETE FROM "Service" + WHERE "serverId" = $1 AND "processName" IS NOT NULL AND "processName" != '' AND name NOT IN (${placeholders}) + `, params.id, ...namesArr) + } + + return NextResponse.json({ + success: true, + discovered: discovered.length, + inserted: insertedCount, + deleted, + total: discovered.length, + }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/servers/[id]/services/route.ts b/src/app/api/servers/[id]/services/route.ts new file mode 100644 index 0000000..1edd3b1 --- /dev/null +++ b/src/app/api/servers/[id]/services/route.ts @@ -0,0 +1,21 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function GET(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifi??' }, { status: 401 }) + + const services: any[] = await prisma.$queryRawUnsafe(` + SELECT * FROM "Service" + WHERE "serverId" = $1 + ORDER BY type ASC, name ASC + `, params.id) + + return NextResponse.json(services) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/servers/dashboard/route.ts b/src/app/api/servers/dashboard/route.ts new file mode 100644 index 0000000..dca26d6 --- /dev/null +++ b/src/app/api/servers/dashboard/route.ts @@ -0,0 +1,45 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function GET() { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const servers: any[] = await prisma.$queryRawUnsafe(` + SELECT s.*, + (SELECT json_build_object( + 'cpuUsage', sm."cpuUsage", + 'ramUsage', sm."ramUsage", + 'diskUsage', sm."diskUsage", + 'uptime', sm.uptime, + 'timestamp', sm."timestamp" + ) FROM "ServerMetric" sm + WHERE sm."serverId" = s.id + ORDER BY sm."timestamp" DESC LIMIT 1) as latest_metric + FROM "Server" s + WHERE s."userId" = $1 + ORDER BY s.name ASC + `, user.id) + + const totalServers = servers.length + const activeServers = servers.filter((s: any) => s.status === 'ACTIVE').length + const totalMonthlyCost = servers.reduce((sum: number, s: any) => sum + Number(s.monthlyCost || 0), 0) + const onlineServers = servers.filter((s: any) => s.latest_metric !== null).length + + return NextResponse.json({ + servers, + stats: { + totalServers, + activeServers, + totalMonthlyCost, + onlineServers, + offlineServers: activeServers - onlineServers, + }, + }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/servers/route.ts b/src/app/api/servers/route.ts new file mode 100644 index 0000000..9cbee62 --- /dev/null +++ b/src/app/api/servers/route.ts @@ -0,0 +1,63 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function GET() { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const servers: any[] = await prisma.$queryRawUnsafe(` + SELECT s.*, + (SELECT json_build_object( + 'cpuUsage', sm."cpuUsage", + 'ramUsage', sm."ramUsage", + 'diskUsage', sm."diskUsage", + 'uptime', sm.uptime, + 'timestamp', sm."timestamp" + ) FROM "ServerMetric" sm + WHERE sm."serverId" = s.id + ORDER BY sm."timestamp" DESC LIMIT 1) as latest_metric + FROM "Server" s + WHERE s."userId" = $1 + ORDER BY s.name ASC + `, user.id) + + const parsed = servers.map((s: any) => ({ + ...s, + latest_metric: s.latest_metric || null, + })) + + return NextResponse.json(parsed) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function POST(request: Request) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const body = await request.json() + const id = `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}` + const now = new Date() + + const { name, ip, os, cpu, ram, disk, startDate, endDate, monthlyCost, status, notes } = body + + await prisma.$executeRawUnsafe(` + INSERT INTO "Server" (id, "userId", name, ip, os, cpu, ram, disk, "startDate", "endDate", "monthlyCost", status, notes, "createdAt", "updatedAt") + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15) + `, id, user.id, name || '', ip || '', os || '', cpu || '', ram || '', disk || '', + startDate ? new Date(startDate) : null, endDate ? new Date(endDate) : null, + monthlyCost ? Number(monthlyCost) : 0, status || 'ACTIVE', notes || '', now, now) + + const created: any[] = await prisma.$queryRawUnsafe(`SELECT * FROM "Server" WHERE id = $1`, id) + + return NextResponse.json(created[0]) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/services/[id]/route.ts b/src/app/api/services/[id]/route.ts new file mode 100644 index 0000000..3088f5c --- /dev/null +++ b/src/app/api/services/[id]/route.ts @@ -0,0 +1,75 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function PATCH(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifi??' }, { status: 401 }) + + const body = await request.json() + const now = new Date() + + const fields: string[] = [] + const values: any[] = [] + let idx = 1 + + for (const key of ['name', 'type', 'technology', 'url', 'status', 'notes', 'processName']) { + if (body[key] !== undefined) { + fields.push(`"${key}" = $${idx++}`) + values.push(body[key]) + } + } + + for (const key of ['port', 'pid']) { + if (body[key] !== undefined) { + fields.push(`"${key}" = $${idx++}`) + values.push(body[key] != null ? Number(body[key]) : null) + } + } + + for (const key of ['cpuUsage', 'ramUsage', 'diskUsage']) { + if (body[key] !== undefined) { + fields.push(`"${key}" = $${idx++}`) + values.push(body[key] != null ? Number(body[key]) : null) + } + } + + if (fields.length === 0) { + return NextResponse.json({ error: 'Aucun champ ?? mettre ?? jour' }, { status: 400 }) + } + + fields.push(`"updatedAt" = $${idx++}`) + values.push(now) + values.push(params.id) + values.push(user.id) + + await prisma.$executeRawUnsafe(` + UPDATE "Service" SET ${fields.join(', ')} + WHERE id = $${idx} AND "serverId" IN (SELECT id FROM "Server" WHERE "userId" = $${idx + 1}) + `, ...values) + + const updated: any[] = await prisma.$queryRawUnsafe(`SELECT * FROM "Service" WHERE id = $1`, params.id) + + return NextResponse.json(updated[0]) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function DELETE(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifi??' }, { status: 401 }) + + await prisma.$executeRawUnsafe(` + DELETE FROM "Service" WHERE id = $1 AND "serverId" IN (SELECT id FROM "Server" WHERE "userId" = $2) + `, params.id, user.id) + + return NextResponse.json({ success: true }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/services/route.ts b/src/app/api/services/route.ts new file mode 100644 index 0000000..1b16831 --- /dev/null +++ b/src/app/api/services/route.ts @@ -0,0 +1,53 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function GET() { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const services: any[] = await prisma.$queryRawUnsafe(` + SELECT sv.*, s.name as server_name, s.ip as server_ip + FROM "Service" sv + JOIN "Server" s ON s.id = sv."serverId" + WHERE s."userId" = $1 + ORDER BY s.name ASC, sv.name ASC + `, user.id) + + return NextResponse.json(services) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function POST(request: Request) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const body = await request.json() + const id = `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}` + const now = new Date() + + const { serverId, name, type, port, technology, url, status, cpuUsage, ramUsage, diskUsage, pid, processName, notes } = body + + const serverCheck: any[] = await prisma.$queryRawUnsafe(`SELECT id FROM "Server" WHERE id = $1 AND "userId" = $2`, serverId, user.id) + if (!serverCheck.length) return NextResponse.json({ error: 'Serveur introuvable' }, { status: 404 }) + + await prisma.$executeRawUnsafe(` + INSERT INTO "Service" (id, "serverId", name, type, port, technology, url, status, "cpuUsage", "ramUsage", "diskUsage", pid, "processName", notes, "createdAt", "updatedAt") + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16) + `, id, serverId, name, type || 'service', port ? Number(port) : null, technology || '', url || '', + status || 'running', cpuUsage != null ? Number(cpuUsage) : null, ramUsage != null ? Number(ramUsage) : null, + diskUsage != null ? Number(diskUsage) : null, pid ? Number(pid) : null, processName || '', notes || '', now, now) + + const created: any[] = await prisma.$queryRawUnsafe(`SELECT * FROM "Service" WHERE id = $1`, id) + + return NextResponse.json(created[0]) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/settings/products/route.ts b/src/app/api/settings/products/route.ts new file mode 100644 index 0000000..6b58739 --- /dev/null +++ b/src/app/api/settings/products/route.ts @@ -0,0 +1,70 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +const DEFAULT_PRODUCTS = [ + { key: 'maintenance-poste', label: 'Maintenance Poste (Windows/Mac)', costPrice: 1.40, sellPrice: 9.60, unit: 'mois', category: 'maintenance' }, + { key: 'maintenance-serveur', label: 'Maintenance Serveur', costPrice: 4.20, sellPrice: 25.20, unit: 'mois', category: 'maintenance' }, + { key: 'ksuite-business', label: 'KSuite Business / utilisateur', costPrice: 6.58, sellPrice: 15.80, unit: 'mois', category: 'ksuite' }, + { key: 'ksuite-standard', label: 'KSuite Standard / utilisateur', costPrice: 1.58, sellPrice: 0, unit: 'mois', category: 'ksuite' }, + { key: 'ksuite-entreprise', label: 'KSuite Entreprise / utilisateur', costPrice: 12.41, sellPrice: 0, unit: 'mois', category: 'ksuite' }, + { key: 'intervention-horaire', label: 'Intervention (Taux horaire)', costPrice: 0, sellPrice: 50, unit: 'heure', category: 'hourly' }, + { key: 'deplacement-km', label: 'Déplacement (par km)', costPrice: 0.30, sellPrice: 0.50, unit: 'km', category: 'travel' }, +] + +export async function GET() { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const products = await prisma.productCost.findMany({ + where: { isActive: true }, + orderBy: { category: 'asc' }, + }) + + return NextResponse.json({ products }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function PUT(request: Request) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const data = await request.json() + const product = await prisma.productCost.update({ + where: { id: data.id }, + data: { + costPrice: data.costPrice, + sellPrice: data.sellPrice, + label: data.label, + isActive: data.isActive, + }, + }) + + return NextResponse.json({ product }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function POST() { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + for (const product of DEFAULT_PRODUCTS) { + await prisma.productCost.upsert({ + where: { key: product.key }, + update: product, + create: product, + }) + } + + return NextResponse.json({ success: true }) + } catch { + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/tickets/[id]/attachments/[attachmentId]/route.ts b/src/app/api/tickets/[id]/attachments/[attachmentId]/route.ts new file mode 100644 index 0000000..9a50ac2 --- /dev/null +++ b/src/app/api/tickets/[id]/attachments/[attachmentId]/route.ts @@ -0,0 +1,29 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function GET(request: Request, { params }: { params: { id: string, attachmentId: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const check: any[] = await prisma.$queryRawUnsafe(`SELECT id FROM "Ticket" WHERE id = $1 AND "userId" = $2`, params.id, user.id) + if (!check.length) return NextResponse.json({ error: 'Ticket introuvable' }, { status: 404 }) + + const att: any[] = await prisma.$queryRawUnsafe(` + SELECT * FROM "TicketAttachment" WHERE id = $1 AND "ticketId" = $2 + `, params.attachmentId, params.id) + if (!att.length) return NextResponse.json({ error: 'Fichier introuvable' }, { status: 404 }) + + const buffer = Buffer.from(att[0].data) + return new NextResponse(buffer, { + headers: { + 'Content-Type': att[0].contentType, + 'Cache-Control': 'public, max-age=31536000, immutable', + }, + }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/tickets/[id]/attachments/route.ts b/src/app/api/tickets/[id]/attachments/route.ts new file mode 100644 index 0000000..233f1c1 --- /dev/null +++ b/src/app/api/tickets/[id]/attachments/route.ts @@ -0,0 +1,25 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function GET(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const check: any[] = await prisma.$queryRawUnsafe(`SELECT id FROM "Ticket" WHERE id = $1 AND "userId" = $2`, params.id, user.id) + if (!check.length) return NextResponse.json({ error: 'Ticket introuvable' }, { status: 404 }) + + const attachments: any[] = await prisma.$queryRawUnsafe(` + SELECT id, filename, "contentType", "contentId", "messageId", "createdAt" + FROM "TicketAttachment" + WHERE "ticketId" = $1 + ORDER BY "createdAt" ASC + `, params.id) + + return NextResponse.json(attachments) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/tickets/[id]/messages/route.ts b/src/app/api/tickets/[id]/messages/route.ts new file mode 100644 index 0000000..b8cb0e6 --- /dev/null +++ b/src/app/api/tickets/[id]/messages/route.ts @@ -0,0 +1,84 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' +import { sendEmail } from '@/lib/mail' + +export async function GET(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const check: any[] = await prisma.$queryRawUnsafe(`SELECT id FROM "Ticket" WHERE id = $1 AND "userId" = $2`, params.id, user.id) + if (!check.length) return NextResponse.json({ error: 'Ticket introuvable' }, { status: 404 }) + + const messages: any[] = await prisma.$queryRawUnsafe(` + SELECT * FROM "TicketMessage" + WHERE "ticketId" = $1 + ORDER BY "createdAt" ASC + `, params.id) + + return NextResponse.json(messages) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function POST(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const tickets: any[] = await prisma.$queryRawUnsafe(` + SELECT id, source, "emailFrom", subject FROM "Ticket" WHERE id = $1 AND "userId" = $2 + `, params.id, user.id) + if (!tickets.length) return NextResponse.json({ error: 'Ticket introuvable' }, { status: 404 }) + + const ticket = tickets[0] + const body = await request.json() + const msgId = `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}` + const now = new Date() + + await prisma.$executeRawUnsafe(` + INSERT INTO "TicketMessage" (id, "ticketId", content, author, "isInternal", "createdAt") + VALUES ($1, $2, $3, $4, $5, $6) + `, msgId, params.id, body.content, body.author || user.name || 'Moi', body.isInternal || false, now) + + if (body.images && Array.isArray(body.images)) { + for (const img of body.images) { + if (!img.data) continue + const attId = `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}` + try { + await prisma.$executeRawUnsafe(` + INSERT INTO "TicketAttachment" (id, "ticketId", "messageId", filename, "contentType", data, "contentId", "createdAt") + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + `, attId, params.id, msgId, img.filename || 'image.png', img.contentType || 'image/png', Buffer.from(img.data, 'base64'), null, now) + } catch (attErr) { + console.error('Erreur sauvegarde image:', attErr) + } + } + } + + await prisma.$executeRawUnsafe(` + UPDATE "Ticket" SET "updatedAt" = $1 WHERE id = $2 + `, now, params.id) + + if (ticket.source === 'email' && ticket.emailFrom && !body.isInternal) { + try { + await sendEmail( + ticket.emailFrom, + `Re: ${ticket.subject}`, + body.content + ) + } catch (mailErr) { + console.error('Erreur envoi email:', mailErr) + } + } + + const created: any[] = await prisma.$queryRawUnsafe(`SELECT * FROM "TicketMessage" WHERE id = $1`, msgId) + return NextResponse.json(created[0]) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/tickets/[id]/route.ts b/src/app/api/tickets/[id]/route.ts new file mode 100644 index 0000000..a5f3336 --- /dev/null +++ b/src/app/api/tickets/[id]/route.ts @@ -0,0 +1,108 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function GET(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const tickets: any[] = await prisma.$queryRawUnsafe(` + SELECT t.*, c.name as client_name, c.name as client_company, c.email as client_email, + s.name as server_name, s.ip as server_ip + FROM "Ticket" t + LEFT JOIN "Client" c ON c.id = t."clientId" + LEFT JOIN "Server" s ON s.id = t."serverId" + WHERE t.id = $1 AND t."userId" = $2 + `, params.id, user.id) + + if (!tickets.length) { + return NextResponse.json({ error: 'Ticket introuvable' }, { status: 404 }) + } + + const results = await Promise.all([ + prisma.$queryRawUnsafe(` + SELECT * FROM "TicketMessage" + WHERE "ticketId" = $1 + ORDER BY "createdAt" ASC + `, params.id), + prisma.$queryRawUnsafe(` + SELECT id, filename, "contentType", "contentId", "messageId", "createdAt" + FROM "TicketAttachment" + WHERE "ticketId" = $1 + ORDER BY "createdAt" ASC + `, params.id), + ]) + const messages = results[0] as any[] + const attachments = results[1] as any[] + + return NextResponse.json({ ...tickets[0], messages, attachments }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function PATCH(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const body = await request.json() + const now = new Date() + + const check: any[] = await prisma.$queryRawUnsafe(`SELECT id FROM "Ticket" WHERE id = $1 AND "userId" = $2`, params.id, user.id) + if (!check.length) return NextResponse.json({ error: 'Ticket introuvable' }, { status: 404 }) + + const allowed = ['status', 'priority', 'clientId', 'serverId', 'subject', 'description', 'assignedTo'] + const fields: string[] = [] + const values: any[] = [] + let idx = 1 + + for (const key of allowed) { + if (body[key] !== undefined) { + fields.push(`"${key}" = $${idx++}`) + values.push(body[key]) + } + } + + if (fields.length === 0) { + return NextResponse.json({ error: 'Aucun champ à mettre à jour' }, { status: 400 }) + } + + fields.push(`"updatedAt" = $${idx++}`) + values.push(now) + values.push(params.id) + values.push(user.id) + + await prisma.$executeRawUnsafe(` + UPDATE "Ticket" SET ${fields.join(', ')} + WHERE id = $${idx} AND "userId" = $${idx + 1} + `, ...values) + + const updated: any[] = await prisma.$queryRawUnsafe(`SELECT * FROM "Ticket" WHERE id = $1`, params.id) + return NextResponse.json(updated[0]) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function DELETE(request: Request, { params }: { params: { id: string } }) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const check: any[] = await prisma.$queryRawUnsafe(`SELECT id FROM "Ticket" WHERE id = $1 AND "userId" = $2`, params.id, user.id) + if (!check.length) return NextResponse.json({ error: 'Ticket introuvable' }, { status: 404 }) + + await prisma.$executeRawUnsafe(`DELETE FROM "TicketAttachment" WHERE "ticketId" = $1`, params.id) + await prisma.$executeRawUnsafe(`DELETE FROM "TicketMessage" WHERE "ticketId" = $1`, params.id) + await prisma.$executeRawUnsafe(`DELETE FROM "Ticket" WHERE id = $1`, params.id) + + return NextResponse.json({ success: true }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/tickets/dashboard/route.ts b/src/app/api/tickets/dashboard/route.ts new file mode 100644 index 0000000..77b318e --- /dev/null +++ b/src/app/api/tickets/dashboard/route.ts @@ -0,0 +1,60 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function GET() { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const total: any[] = await prisma.$queryRawUnsafe(` + SELECT COUNT(*)::int as count FROM "Ticket" WHERE "userId" = $1 + `, user.id) + + const byStatus: any[] = await prisma.$queryRawUnsafe(` + SELECT status, COUNT(*)::int as count FROM "Ticket" + WHERE "userId" = $1 + GROUP BY status ORDER BY status + `, user.id) + + const byPriority: any[] = await prisma.$queryRawUnsafe(` + SELECT priority, COUNT(*)::int as count FROM "Ticket" + WHERE "userId" = $1 + GROUP BY priority ORDER BY priority + `, user.id) + + const byClient: any[] = await prisma.$queryRawUnsafe(` + SELECT COALESCE(c.name, 'Sans client') as client, COUNT(*)::int as count + FROM "Ticket" t + LEFT JOIN "Client" c ON c.id = t."clientId" + WHERE t."userId" = $1 + GROUP BY c.name + ORDER BY count DESC + `, user.id) + + const monthly: any[] = await prisma.$queryRawUnsafe(` + SELECT to_char("createdAt", 'YYYY-MM') as month, COUNT(*)::int as count + FROM "Ticket" + WHERE "userId" = $1 + GROUP BY month + ORDER BY month ASC + `, user.id) + + const unresolved: any[] = await prisma.$queryRawUnsafe(` + SELECT COUNT(*)::int as count FROM "Ticket" + WHERE "userId" = $1 AND status NOT IN ('resolved', 'closed') + `, user.id) + + return NextResponse.json({ + total: total[0]?.count || 0, + byStatus, + byPriority, + byClient, + monthly, + unresolved: unresolved[0]?.count || 0, + }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/tickets/email/route.ts b/src/app/api/tickets/email/route.ts new file mode 100644 index 0000000..c9744d5 --- /dev/null +++ b/src/app/api/tickets/email/route.ts @@ -0,0 +1,74 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' + +const EMAIL_SECRET = process.env.SERVER_MONITOR_SECRET || '' + +export async function POST(request: Request) { + try { + const { searchParams } = new URL(request.url) + const token = searchParams.get('token') + + if (token !== EMAIL_SECRET) { + return NextResponse.json({ error: 'Non autorisé' }, { status: 401 }) + } + + const body = await request.json() + const { from, subject, text, html, attachments } = body + + if (!from || !subject) { + return NextResponse.json({ error: 'Champs from et subject requis' }, { status: 400 }) + } + + const userRow: any[] = await prisma.$queryRawUnsafe(` + SELECT id FROM "User" LIMIT 1 + `) + if (!userRow.length) { + return NextResponse.json({ error: 'Aucun utilisateur' }, { status: 500 }) + } + const userId = userRow[0].id + + const clientRow: any[] = await prisma.$queryRawUnsafe(` + SELECT id, name FROM "Client" + WHERE email = $1 + LIMIT 1 + `, from) + + const id = `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}` + const now = new Date() + + const maxRow: any[] = await prisma.$queryRawUnsafe(`SELECT COALESCE(MAX("ticketNumber"), 0) + 1 as next FROM "Ticket"`) + const ticketNumber = maxRow[0]?.next || 1 + + const msgId = `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}` + + await prisma.$executeRawUnsafe(` + INSERT INTO "Ticket" (id, "userId", "clientId", subject, description, status, priority, source, "emailFrom", "ticketNumber", "createdAt", "updatedAt") + VALUES ($1, $2, $3, $4, $5, 'new', 'normal', 'email', $6, $7, $8, $9) + `, id, userId, clientRow[0]?.id || null, subject, text || html || '', from, ticketNumber, now, now) + + await prisma.$executeRawUnsafe(` + INSERT INTO "TicketMessage" (id, "ticketId", content, author, "isInternal", "createdAt") + VALUES ($1, $2, $3, $4, false, $5) + `, msgId, id, text || html || '', clientRow[0]?.name || from, now) + + if (attachments && Array.isArray(attachments)) { + for (const att of attachments) { + if (!att.data) continue + const attId = `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}` + try { + await prisma.$executeRawUnsafe(` + INSERT INTO "TicketAttachment" (id, "ticketId", "messageId", filename, "contentType", data, "contentId", "createdAt") + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) + `, attId, id, msgId, att.filename || 'image.png', att.contentType || 'image/png', Buffer.from(att.data, 'base64'), att.contentId || null, now) + } catch (attErr) { + console.error('Erreur sauvegarde pièce jointe:', attErr) + } + } + } + + return NextResponse.json({ success: true, ticketId: id }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/tickets/route.ts b/src/app/api/tickets/route.ts new file mode 100644 index 0000000..fa2f041 --- /dev/null +++ b/src/app/api/tickets/route.ts @@ -0,0 +1,72 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function GET(request: Request) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const { searchParams } = new URL(request.url) + const status = searchParams.get('status') + const clientId = searchParams.get('clientId') + + let sql = `SELECT t.*, c.name as client_name, c.name as client_company, s.name as server_name + FROM "Ticket" t + LEFT JOIN "Client" c ON c.id = t."clientId" + LEFT JOIN "Server" s ON s.id = t."serverId" + WHERE t."userId" = $1` + const params: any[] = [user.id] + + if (status) { + const statuses = status.split(',') + if (statuses.length === 1) { + sql += ` AND t.status = $${params.length + 1}` + params.push(status) + } else { + sql += ` AND t.status IN (${statuses.map((_, i) => `$${params.length + 1 + i}`).join(',')})` + params.push(...statuses) + } + } + if (clientId) { + sql += ` AND t."clientId" = $${params.length + 1}` + params.push(clientId) + } + + sql += ' ORDER BY t."createdAt" DESC' + + const tickets = await prisma.$queryRawUnsafe(sql, ...params) + return NextResponse.json(tickets) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function POST(request: Request) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const body = await request.json() + const id = `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}` + const now = new Date() + + const maxRow: any[] = await prisma.$queryRawUnsafe(`SELECT COALESCE(MAX("ticketNumber"), 0) + 1 as next FROM "Ticket"`) + const ticketNumber = maxRow[0]?.next || 1 + + const { clientId, serverId, subject, description, priority, source, emailFrom } = body + + await prisma.$executeRawUnsafe(` + INSERT INTO "Ticket" (id, "userId", "clientId", "serverId", subject, description, status, priority, source, "emailFrom", "assignedTo", "ticketNumber", "createdAt", "updatedAt") + VALUES ($1, $2, $3, $4, $5, $6, 'new', $7, $8, $9, $10, $11, $12, $13) + `, id, user.id, clientId || null, serverId || null, subject, description || '', + priority || 'normal', source || 'manual', emailFrom || null, null, ticketNumber, now, now) + + const created: any[] = await prisma.$queryRawUnsafe(`SELECT * FROM "Ticket" WHERE id = $1`, id) + return NextResponse.json(created[0]) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/api/todos/route.ts b/src/app/api/todos/route.ts new file mode 100644 index 0000000..5988740 --- /dev/null +++ b/src/app/api/todos/route.ts @@ -0,0 +1,106 @@ +import { NextResponse } from 'next/server' +import { prisma } from '@/lib/prisma' +import { getCurrentUser } from '@/lib/auth' + +export async function GET() { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const todos: any[] = await prisma.$queryRawUnsafe(` + SELECT id, text, position, done FROM "TodoItem" + WHERE "userId" = $1 + ORDER BY position ASC + `, user.id) + + return NextResponse.json({ todos }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function POST(request: Request) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const data = await request.json() + + const maxPos: any[] = await prisma.$queryRawUnsafe(` + SELECT COALESCE(MAX(position), -1) as pos FROM "TodoItem" + WHERE "userId" = $1 + `, user.id) + + const pos = (maxPos[0]?.pos ?? -1) + 1 + const id = `${Date.now().toString(36)}-${Math.random().toString(36).substr(2, 9)}` + + await prisma.$executeRawUnsafe(` + INSERT INTO "TodoItem" (id, "userId", text, position, done, "createdAt", "updatedAt") + VALUES ($1, $2, $3, $4, false, $5, $6) + `, id, user.id, data.text, pos, new Date(), new Date()) + + const todo = { id, text: data.text, position: pos, done: false } + + return NextResponse.json({ todo }, { status: 201 }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function PATCH(request: Request) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const data = await request.json() + const { id, text, done, position } = data + + const check: any[] = await prisma.$queryRawUnsafe(` + SELECT id FROM "TodoItem" WHERE id = $1 AND "userId" = $2 + `, id, user.id) + + if (check.length === 0) { + return NextResponse.json({ error: 'Todo non trouvé' }, { status: 404 }) + } + + const sets: string[] = [] + const params: any[] = [] + let idx = 1 + + if (text !== undefined) { sets.push(`text = $${idx++}`); params.push(text) } + if (done !== undefined) { sets.push(`done = $${idx++}`); params.push(done) } + if (position !== undefined) { sets.push(`position = $${idx++}`); params.push(position) } + + if (sets.length > 0) { + params.push(id) + await prisma.$executeRawUnsafe(`UPDATE "TodoItem" SET ${sets.join(', ')} WHERE id = $${idx}`, ...params) + } + + return NextResponse.json({ success: true }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} + +export async function DELETE(request: Request) { + try { + const user = await getCurrentUser() + if (!user) return NextResponse.json({ error: 'Non authentifié' }, { status: 401 }) + + const { searchParams } = new URL(request.url) + const id = searchParams.get('id') + if (!id) return NextResponse.json({ error: 'ID requis' }, { status: 400 }) + + await prisma.$executeRawUnsafe(` + DELETE FROM "TodoItem" WHERE id = $1 AND "userId" = $2 + `, id, user.id) + + return NextResponse.json({ success: true }) + } catch (error) { + console.error(error) + return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 }) + } +} diff --git a/src/app/globals.css b/src/app/globals.css new file mode 100644 index 0000000..8c5548d --- /dev/null +++ b/src/app/globals.css @@ -0,0 +1,45 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +:root { + --background: #f8fafc; + --foreground: #1e293b; +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #0f172a; + --foreground: #f1f5f9; + } +} + +body { + background-color: var(--background); + color: var(--foreground); + font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; +} + +@layer utilities { + .scrollbar-hide { + -ms-overflow-style: none; + scrollbar-width: none; + } + .scrollbar-hide::-webkit-scrollbar { + display: none; + } +} + +.recharts-cartesian-grid-horizontal line, +.recharts-cartesian-grid-vertical line { + stroke: #e2e8f0; +} + +.dark .recharts-cartesian-grid-horizontal line, +.dark .recharts-cartesian-grid-vertical line { + stroke: #334155; +} + +.recharts-tooltip-wrapper { + outline: none; +} \ No newline at end of file diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 0000000..41c8b68 --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,49 @@ +import type { Metadata } from 'next' +import { Inter } from 'next/font/google' +import './globals.css' +import { Providers } from './providers' + +const inter = Inter({ subsets: ['latin'] }) + +export const metadata: Metadata = { + title: 'SecourIT - Gestion', + description: 'Application de gestion commerciale', + manifest: '/manifest.json', + icons: { + icon: '/favicon.svg?v=2', + apple: '/favicon.svg?v=2', + }, + other: { + 'theme-color': '#2563eb', + 'apple-mobile-web-app-capable': 'yes', + }, +} + +export default function RootLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( + + + + + + + + +