From 845942f36ccfecbdeb85fcfeeee05f3fb532ee7c Mon Sep 17 00:00:00 2001 From: Florian Wilde Date: Thu, 3 Sep 2026 00:11:43 +0200 Subject: [PATCH] Phase 0: Fundament der Feuerwehr-Verwaltungsplattform Legt den kompletten Unterbau an, auf dem die Fachmodule aufsetzen. Betrieb: - docker-compose.yml mit Traefik, App, Worker, PostgreSQL 17 + PostGIS, Redis, MinIO und Gotenberg; Entwicklungs-Override ohne Traefik, dafuer mit Mailpit und Adminer - Mehrstufiges Dockerfile: eigener Benutzer, tini als PID 1, Healthcheck - .env.example mit einem Kommentar zu jeder Variable Mandantentrennung: - Row Level Security auf allen mandantenpflichtigen Tabellen - Zwei getrennte Datenbankverbindungen: florian_app mit aktiver RLS fuer die Anwendung, Eigentuemer-Zugang nur fuer Migration, Seed und Anmeldung - fuerOrganisation() als einziger Weg zu Fachdaten Nachvollziehbarkeit: - AuditLog append-only per Trigger; UPDATE ist dauerhaft gesperrt, DELETE nur im gekennzeichneten Aufbewahrungslauf - Geheimnisse werden vor dem Schreiben entfernt Anmeldung: - OIDC gegen ein extern betriebenes authentik, Rollen aus Gruppen nur als Vorbelegung beim ersten Login - Lokaler E-Mail/Passwort-Login als Rueckfallweg - Terminal per PIN und RFID mit zwei Schranken (Geraet, dann Person) Weiteres: - lib/authz.ts mit 9 Rollen und 38 Einzelrechten - lib/storage.ts (MinIO), lib/audit.ts, lib/warteschlange.ts (BullMQ) - Integrationen fuer Mail, Telegram und LLM hinter abschaltbaren Interfaces - tRPC v11, Basis-Layout mit Seitenleiste und dunkler Darstellung - Seed mit einer Beispielwehr: 16 Personen, 3 Fahrzeuge, 40 Geraete - 32 Vitest-Tests (davon 8 gegen eine echte Datenbank), 10 Playwright-Tests - README, Anleitung mit Screenshots, Roadmap, Aufwandsschaetzung, Devlog, Versionierungsregeln und Changelog Co-Authored-By: Claude Opus 5 --- .dockerignore | 14 + .env.example | 231 + .gitignore | 42 + .npmrc | 3 + CHANGELOG.md | 89 + CLAUDE.md | 62 + Phase-0.md | 23 + Phase-1.md | 19 + Phase-2.md | 29 + Phase-3.md | 20 + Phase-4.md | 21 + Phase-5.md | 28 + Phase-6.md | 15 + Phase-7.md | 14 + Phase-8.md | 16 + README.md | 216 + components.json | 21 + docker-compose.override.yml | 116 + docker-compose.yml | 335 + docker/Dockerfile | 111 + docker/entrypoint.sh | 26 + docker/healthcheck.js | 25 + docker/postgres/10-init-app-role.sh | 79 + docs/ANLEITUNG.md | 258 + docs/AUFWANDSSCHAETZUNG.md | 237 + docs/DEVLOG.md | 133 + docs/ROADMAP.md | 209 + docs/VERSIONIERUNG.md | 117 + docs/screenshots/01-anmeldung-dark.png | Bin 0 -> 64307 bytes docs/screenshots/01-anmeldung-light.png | Bin 0 -> 67741 bytes docs/screenshots/02-uebersicht-dark.png | Bin 0 -> 188007 bytes docs/screenshots/02-uebersicht-light.png | Bin 0 -> 193435 bytes next.config.ts | 27 + package-lock.json | 12557 ++++++++++++++++ package.json | 88 + playwright.config.ts | 49 + postcss.config.mjs | 7 + prisma.config.ts | 14 + .../20260902000000_init/migration.sql | 409 + .../20260902000100_rls_policies/migration.sql | 156 + .../migration.sql | 60 + prisma/schema.prisma | 528 + prisma/seed.ts | 595 + scripts/screenshots.ts | 100 + src/app/(app)/layout.tsx | 42 + src/app/(app)/page.tsx | 121 + src/app/anmelden/formular.tsx | 151 + src/app/anmelden/page.tsx | 53 + src/app/api/auth/[...nextauth]/route.ts | 8 + src/app/api/health/route.ts | 37 + src/app/api/trpc/[trpc]/route.ts | 22 + src/app/globals.css | 170 + src/app/layout.tsx | 45 + src/components/layout/seitenleiste.tsx | 216 + src/components/layout/theme-provider.tsx | 15 + src/components/layout/theme-toggle.tsx | 29 + src/components/ui/badge.tsx | 41 + src/components/ui/button.tsx | 55 + src/components/ui/card.tsx | 61 + src/components/ui/input.tsx | 23 + src/components/ui/label.tsx | 22 + src/lib/audit.ts | 238 + src/lib/auth.ts | 613 + src/lib/authz.ts | 338 + src/lib/db.ts | 146 + src/lib/env.ts | 173 + src/lib/integrations/llm.ts | 106 + src/lib/integrations/mail.ts | 75 + src/lib/integrations/telegram.ts | 77 + src/lib/passwort.ts | 66 + src/lib/storage.ts | 209 + src/lib/utils.ts | 104 + src/lib/warteschlange.ts | 109 + src/server/context.ts | 32 + src/server/routers/_app.ts | 106 + src/server/trpc.ts | 99 + src/types/next-auth.d.ts | 38 + src/worker/index.ts | 94 + tests/e2e/anmeldung.spec.ts | 96 + tests/setup.ts | 32 + tests/unit/authz.test.ts | 197 + tests/unit/mandantentrennung.test.ts | 226 + tsconfig.json | 24 + vitest.config.ts | 25 + 84 files changed, 21433 insertions(+) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 .npmrc create mode 100644 CHANGELOG.md create mode 100644 CLAUDE.md create mode 100644 Phase-0.md create mode 100644 Phase-1.md create mode 100644 Phase-2.md create mode 100644 Phase-3.md create mode 100644 Phase-4.md create mode 100644 Phase-5.md create mode 100644 Phase-6.md create mode 100644 Phase-7.md create mode 100644 Phase-8.md create mode 100644 README.md create mode 100644 components.json create mode 100644 docker-compose.override.yml create mode 100644 docker-compose.yml create mode 100644 docker/Dockerfile create mode 100644 docker/entrypoint.sh create mode 100644 docker/healthcheck.js create mode 100644 docker/postgres/10-init-app-role.sh create mode 100644 docs/ANLEITUNG.md create mode 100644 docs/AUFWANDSSCHAETZUNG.md create mode 100644 docs/DEVLOG.md create mode 100644 docs/ROADMAP.md create mode 100644 docs/VERSIONIERUNG.md create mode 100644 docs/screenshots/01-anmeldung-dark.png create mode 100644 docs/screenshots/01-anmeldung-light.png create mode 100644 docs/screenshots/02-uebersicht-dark.png create mode 100644 docs/screenshots/02-uebersicht-light.png create mode 100644 next.config.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 playwright.config.ts create mode 100644 postcss.config.mjs create mode 100644 prisma.config.ts create mode 100644 prisma/migrations/20260902000000_init/migration.sql create mode 100644 prisma/migrations/20260902000100_rls_policies/migration.sql create mode 100644 prisma/migrations/20260902000200_audit_bereinigung/migration.sql create mode 100644 prisma/schema.prisma create mode 100644 prisma/seed.ts create mode 100644 scripts/screenshots.ts create mode 100644 src/app/(app)/layout.tsx create mode 100644 src/app/(app)/page.tsx create mode 100644 src/app/anmelden/formular.tsx create mode 100644 src/app/anmelden/page.tsx create mode 100644 src/app/api/auth/[...nextauth]/route.ts create mode 100644 src/app/api/health/route.ts create mode 100644 src/app/api/trpc/[trpc]/route.ts create mode 100644 src/app/globals.css create mode 100644 src/app/layout.tsx create mode 100644 src/components/layout/seitenleiste.tsx create mode 100644 src/components/layout/theme-provider.tsx create mode 100644 src/components/layout/theme-toggle.tsx create mode 100644 src/components/ui/badge.tsx create mode 100644 src/components/ui/button.tsx create mode 100644 src/components/ui/card.tsx create mode 100644 src/components/ui/input.tsx create mode 100644 src/components/ui/label.tsx create mode 100644 src/lib/audit.ts create mode 100644 src/lib/auth.ts create mode 100644 src/lib/authz.ts create mode 100644 src/lib/db.ts create mode 100644 src/lib/env.ts create mode 100644 src/lib/integrations/llm.ts create mode 100644 src/lib/integrations/mail.ts create mode 100644 src/lib/integrations/telegram.ts create mode 100644 src/lib/passwort.ts create mode 100644 src/lib/storage.ts create mode 100644 src/lib/utils.ts create mode 100644 src/lib/warteschlange.ts create mode 100644 src/server/context.ts create mode 100644 src/server/routers/_app.ts create mode 100644 src/server/trpc.ts create mode 100644 src/types/next-auth.d.ts create mode 100644 src/worker/index.ts create mode 100644 tests/e2e/anmeldung.spec.ts create mode 100644 tests/setup.ts create mode 100644 tests/unit/authz.test.ts create mode 100644 tests/unit/mandantentrennung.test.ts create mode 100644 tsconfig.json create mode 100644 vitest.config.ts diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2e4406d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,14 @@ +node_modules +.next +.git +.github +coverage +test-results +playwright-report +data +Phase-*.md +*.log +.env +.env.local +.env.*.local +README.md diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..ce12391 --- /dev/null +++ b/.env.example @@ -0,0 +1,231 @@ +# ============================================================================= +# FLORIAN — Feuerwehr-Verwaltungsplattform +# Kopiere diese Datei nach ".env" und passe die Werte an: +# cp .env.example .env +# Alles, was hier steht, wird von docker-compose.yml und von lib/env.ts gelesen. +# lib/env.ts validiert beim Start mit Zod — fehlt eine Pflichtvariable, startet +# die App gar nicht erst, statt später halb zu funktionieren. +# ============================================================================= + +# ----------------------------------------------------------------------------- +# Allgemein +# ----------------------------------------------------------------------------- +# development | production | test — steuert Logging, Fehlerseiten und Caching. +NODE_ENV=development +# Öffentliche Basis-URL der Anwendung, ohne abschließenden Schrägstrich. +# Muss exakt der Adresse entsprechen, unter der Nutzer die App aufrufen — +# Auth.js baut daraus die OIDC-Redirect-URI. +APP_URL=http://localhost:3100 +# Zeitzone für alle serverseitigen Fristen- und Terminberechnungen. +# Nicht ändern, außer die Wehr liegt außerhalb Deutschlands. +TZ=Europe/Berlin +# Host-Ports fuer die Entwicklung. Bewusst nicht die Standardports, +# damit FLORIAN neben anderen lokalen Stacks laufen kann. +APP_PORT=3100 +GOTENBERG_PORT=3101 +MAILPIT_UI_PORT=8125 +MAILPIT_SMTP_PORT=1125 +ADMINER_PORT=8081 + +# ----------------------------------------------------------------------------- +# Traefik / Domains (nur für den Produktionsbetrieb relevant) +# In der Entwicklung greift docker-compose.override.yml und veröffentlicht die +# Dienste direkt auf localhost — Traefik wird dort nicht gebraucht. +# ----------------------------------------------------------------------------- +# Hostname der Anwendung hinter Traefik, z. B. florian.beispielwehr.de +APP_DOMAIN=florian.localhost +# Hostname der MinIO-S3-API (Datei-Uploads, presignte URLs). +MINIO_DOMAIN=s3.florian.localhost +# Hostname der MinIO-Weboberfläche (Administration der Buckets). +MINIO_CONSOLE_DOMAIN=minio.florian.localhost +# Hostname des Traefik-Dashboards. Leer lassen, um es nicht zu veröffentlichen. +TRAEFIK_DASHBOARD_DOMAIN=traefik.florian.localhost +# E-Mail-Adresse für die ACME-Zertifikate (Let us Encrypt). +ACME_EMAIL=kommandant@beispielwehr.de +# Traefik-Entrypoint für HTTPS. "websecure" ist der Standard aus dieser Datei. +TRAEFIK_ENTRYPOINT=websecure + +# ----------------------------------------------------------------------------- +# PostgreSQL 17 + PostGIS +# ----------------------------------------------------------------------------- +POSTGRES_USER=florian +# Vor dem ersten Start ersetzen: openssl rand -base64 24 +POSTGRES_PASSWORD=bitte_aendern_postgres +POSTGRES_DB=florian +# Port, unter dem Postgres auf dem Host erreichbar ist (nur Entwicklung). +POSTGRES_PORT=5442 +# Verbindungszeichenfolge der Anwendung. Läuft über den Datenbankbenutzer +# "florian_app", für den Row Level Security AKTIV ist — dieser Zugang sieht nur +# Daten der Organisation, die per SET LOCAL app.current_organisation gesetzt +# wurde (siehe src/lib/db.ts). +DATABASE_URL=postgresql://florian_app:bitte_aendern_app@postgres:5432/florian?schema=public +# Passwort des Anwendungsbenutzers. Muss zu DATABASE_URL passen und wird beim +# ersten Start von docker/postgres/10-init-app-role.sh gesetzt. +POSTGRES_APP_PASSWORD=bitte_aendern_app +# Verbindung für Migrationen und Seed. Nutzt den Eigentümer-Zugang, der RLS +# umgehen darf. Niemals im Anwendungscode verwenden. +DIRECT_DATABASE_URL=postgresql://florian:bitte_aendern_postgres@postgres:5432/florian?schema=public + +# ----------------------------------------------------------------------------- +# Redis (Warteschlangen für BullMQ, Rate-Limiting) +# ----------------------------------------------------------------------------- +# Vor dem ersten Start ersetzen: openssl rand -base64 24 +REDIS_PASSWORD=bitte_aendern_redis +REDIS_URL=redis://:bitte_aendern_redis@redis:6379 +# Port auf dem Host (nur Entwicklung). +REDIS_PORT=6389 +# Anzahl paralleler Jobs je Worker-Prozess. +WORKER_CONCURRENCY=5 + +# ----------------------------------------------------------------------------- +# MinIO (S3-kompatibler Objektspeicher — ALLE Dateien liegen hier, nie im FS) +# ----------------------------------------------------------------------------- +MINIO_ROOT_USER=florian +# Mindestens 8 Zeichen, vor dem ersten Start ersetzen. +MINIO_ROOT_PASSWORD=bitte_aendern_minio +# Endpunkt, den App und Worker intern ansprechen — im Compose-Netz der +# Dienstname, außerhalb der externe Hostname. +S3_ENDPOINT=minio +S3_PORT=9000 +# true, sobald MinIO hinter Traefik mit TLS läuft. +S3_USE_SSL=false +S3_ACCESS_KEY=florian +S3_SECRET_KEY=bitte_aendern_minio +S3_REGION=eu-central-1 +# Bucket für alle Nutzdateien (Prüfprotokolle, Fotos, Rechnungen, Exporte). +S3_BUCKET=florian +# Öffentlich erreichbare Basis-URL für presignte Download-Links. In der +# Entwicklung http://localhost:9010, in Produktion https://$MINIO_DOMAIN +S3_PUBLIC_URL=http://localhost:9010 +# Gültigkeitsdauer presignter Links in Sekunden (Standard: 15 Minuten). +S3_URL_TTL=900 +# Ports auf dem Host (nur Entwicklung): API und Weboberfläche. +MINIO_API_PORT=9010 +MINIO_CONSOLE_PORT=9011 + +# ----------------------------------------------------------------------------- +# Gotenberg (PDF-Erzeugung für Prüfprotokolle, Berichte, Rechnungen) +# ----------------------------------------------------------------------------- +GOTENBERG_URL=http://gotenberg:3000 +# Zeitlimit je PDF-Auftrag in Sekunden. +GOTENBERG_TIMEOUT=30 + +# ----------------------------------------------------------------------------- +# Auth.js — Sitzungsverwaltung +# ----------------------------------------------------------------------------- +# Signiert die Sitzungs-Cookies. Zwingend erforderlich, pro Installation neu: +# openssl rand -base64 32 +AUTH_SECRET=bitte_aendern_mindestens_32_zeichen_lang +# Muss hinter einem Reverse Proxy (Traefik) auf true stehen, damit Auth.js die +# X-Forwarded-Header auswertet und korrekte Redirect-URIs erzeugt. +AUTH_TRUST_HOST=true +# Lebensdauer einer Sitzung in Sekunden (Standard: 8 Stunden). +AUTH_SESSION_MAX_AGE=28800 + +# ----------------------------------------------------------------------------- +# OIDC gegen authentik +# Die authentik-Instanz wird SEPARAT betrieben und ist nicht Teil dieses Stacks. +# In authentik anzulegen: Provider "OAuth2/OpenID" (Client-Typ: confidential), +# Redirect-URI exakt $APP_URL/api/auth/callback/authentik +# Scopes: openid profile email — plus einen Gruppen-Scope, wenn Rollen aus +# authentik-Gruppen vorbelegt werden sollen (siehe AUTH_OIDC_GROUP_CLAIM). +# ----------------------------------------------------------------------------- +# false blendet den OIDC-Knopf aus; dann bleibt nur der lokale Login. Sinnvoll +# für den Erststart, bevor authentik konfiguriert ist. +AUTH_OIDC_ENABLED=false +# Issuer-URL des Providers, OHNE /.well-known/openid-configuration. +# Bei authentik: https:///application/o// +AUTH_OIDC_ISSUER=https://auth.beispiel.de/application/o/florian/ +AUTH_OIDC_CLIENT_ID= +AUTH_OIDC_CLIENT_SECRET= +# Beschriftung des Anmeldeknopfes in der Oberfläche. +AUTH_OIDC_NAME=Anmeldung über authentik +# Claim, aus dem die Gruppenzugehörigkeit gelesen wird. In authentik über ein +# Scope-Mapping bereitzustellen. Leer lassen, um das Mapping abzuschalten — +# dann werden Rollen ausschließlich in FLORIAN gepflegt. +AUTH_OIDC_GROUP_CLAIM=groups +# Zuordnung authentik-Gruppe -> FLORIAN-Rolle, kommagetrennt als "gruppe:ROLLE". +# Wirkt nur als VORBELEGUNG beim ersten Login; danach ist die in FLORIAN +# gepflegte Rolle führend und wird nicht mehr überschrieben. +AUTH_OIDC_GROUP_MAPPING=feuerwehr-kommandant:KOMMANDANT,feuerwehr-geraetewart:GERAETEWART,feuerwehr-atemschutz:ATEMSCHUTZGERAETEWART,feuerwehr-kassier:KASSIER,feuerwehr-schriftfuehrer:SCHRIFTFUEHRER,feuerwehr-ausbilder:AUSBILDER,feuerwehr-mitglied:MITGLIED +# true legt einen in authentik bekannten Nutzer beim ersten Login automatisch +# als Person an. false verlangt, dass die Person vorher in FLORIAN existiert +# und über die E-Mail-Adresse verknüpft wird. +AUTH_OIDC_AUTO_PROVISION=true +# Organisation (Slug), der automatisch angelegte Personen zugeordnet werden. +AUTH_OIDC_STANDARD_ORGANISATION=ff-musterhausen + +# ----------------------------------------------------------------------------- +# Lokaler E-Mail/Passwort-Login (Rückfalloption für Erststart und Notbetrieb) +# ----------------------------------------------------------------------------- +# Nach der Einrichtung von authentik auf false setzen, damit nur noch OIDC geht. +AUTH_LOCAL_ENABLED=true +# Zugangsdaten des ersten Administrators, angelegt vom Seed-Skript. +# Nach dem ersten Login das Passwort in der Oberfläche ändern. +SEED_ADMIN_EMAIL=admin@beispielwehr.de +SEED_ADMIN_PASSWORT=Feuerwehr2026 +# bcrypt-Kostenfaktor. 12 ist ein guter Kompromiss aus Sicherheit und Tempo. +AUTH_BCRYPT_ROUNDS=12 + +# ----------------------------------------------------------------------------- +# Terminal-/Kiosk-Anmeldung im Gerätehaus (PIN und RFID) +# ----------------------------------------------------------------------------- +# Schaltet den Kiosk-Modus samt Terminal-Login frei. +KIOSK_ENABLED=true +# Gemeinsames Geheimnis, mit dem sich ein Terminal-Gerät gegenüber dem Server +# ausweist, bevor überhaupt eine PIN oder ein Token akzeptiert wird: +# openssl rand -hex 32 +KIOSK_DEVICE_SECRET=bitte_aendern_kiosk_geheimnis +# Länge der Personen-PIN. 4 ist bequem, 6 ist sicherer. +KIOSK_PIN_LAENGE=6 +# Nach wie vielen Fehlversuchen eine PIN gesperrt wird. +KIOSK_MAX_FEHLVERSUCHE=5 +# Sperrdauer nach zu vielen Fehlversuchen, in Sekunden. +KIOSK_SPERRDAUER=300 +# Automatische Abmeldung am Terminal nach Untätigkeit, in Sekunden. +KIOSK_IDLE_TIMEOUT=120 + +# ----------------------------------------------------------------------------- +# E-Mail-Versand (optional, hinter lib/integrations/mail.ts abstrahiert) +# Ohne MAIL_ENABLED=true wird nur ins Log geschrieben — praktisch für Tests. +# ----------------------------------------------------------------------------- +MAIL_ENABLED=false +MAIL_HOST=smtp.beispiel.de +MAIL_PORT=587 +MAIL_SECURE=false +MAIL_USER= +MAIL_PASSWORD= +MAIL_FROM=FLORIAN + +# ----------------------------------------------------------------------------- +# Telegram-Benachrichtigungen (optional, abschaltbar) +# ----------------------------------------------------------------------------- +TELEGRAM_ENABLED=false +TELEGRAM_BOT_TOKEN= +TELEGRAM_CHAT_ID= + +# ----------------------------------------------------------------------------- +# LLM-Anbindung (optional; nur selbstgehostete Endpunkte, kein Cloud-Zwang) +# ----------------------------------------------------------------------------- +LLM_ENABLED=false +# OpenAI-kompatibler Endpunkt, z. B. eine lokale Ollama- oder vLLM-Instanz. +LLM_BASE_URL=http://ollama:11434/v1 +LLM_API_KEY= +LLM_MODELL=qwen2.5:14b + +# ----------------------------------------------------------------------------- +# Datenschutz und Aufbewahrung (DSGVO) +# ----------------------------------------------------------------------------- +# Aufbewahrungsfrist für Einsatz- und Prüfdokumentation in Jahren. +AUFBEWAHRUNG_JAHRE=10 +# Aufbewahrungsfrist für den Audit-Log in Tagen. Der Log ist append-only; +# Älteres wird vom Worker exportiert, bevor es entfernt wird. +AUDIT_AUFBEWAHRUNG_TAGE=3650 +# Frist in Tagen, nach der ein ausgetretenes Mitglied anonymisiert wird. +PERSON_ANONYMISIERUNG_TAGE=365 + +# ----------------------------------------------------------------------------- +# Telemetrie bleibt aus. Nicht ändern. +# ----------------------------------------------------------------------------- +NEXT_TELEMETRY_DISABLED=1 +CHECKPOINT_DISABLE=1 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f6f37e1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,42 @@ +# Abhängigkeiten +node_modules/ +.pnp +.pnp.js + +# Build-Artefakte +.next/ +out/ +build/ +dist/ +*.tsbuildinfo +next-env.d.ts + +# Tests +coverage/ +test-results/ +playwright-report/ +blob-report/ +.playwright/ + +# Umgebung — niemals einchecken +.env +.env.local +.env.*.local +!.env.example + +# Laufzeitdaten (Docker-Volumes bei Bind-Mounts) +data/ +docker/traefik/acme.json + +# Editor / OS +.vscode/* +!.vscode/extensions.json +.idea/ +.DS_Store +Thumbs.db +debug.log +*.log + +# Prisma +prisma/*.db +prisma/*.db-journal diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..ecc2009 --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +# Keine Telemetrie, keine Audit-Calls beim Container-Build. +audit=false +fund=false diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..012d1df --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,89 @@ +# Changelog + +Alle nennenswerten Änderungen an FLORIAN. Format nach +[Keep a Changelog](https://keepachangelog.com/de/1.1.0/), Versionierung nach +[Semantic Versioning](https://semver.org/lang/de/). + +Die Regeln dazu stehen in [docs/VERSIONIERUNG.md](docs/VERSIONIERUNG.md). + +## [Unveröffentlicht] + +Noch nichts. Nächster Schritt ist Phase 1 (Stammdatenverwaltung). + +## [0.1.0] — 2026-09-03 + +Phase 0: das Fundament. Ab hier steht ein lauffähiger Stack mit +Mandantentrennung, Anmeldung, Dateiablage und lückenloser Protokollierung. + +### Hinzugefügt + +**Betrieb** + +- `docker-compose.yml` mit Traefik, App, Worker, PostgreSQL 17 + PostGIS, + Redis, MinIO und Gotenberg. +- `docker-compose.override.yml` für die Entwicklung: ohne Traefik, mit + Mailpit als SMTP-Auffangkorb und Adminer für den Blick in die Datenbank. + Host-Ports bewusst abseits der Standardwerte (3100, 5442, 6389, 9010/9011), + damit FLORIAN neben anderen lokalen Stacks laufen kann. +- Mehrstufiges `docker/Dockerfile`: eigener Benutzer `florian` (UID 1001, + kein root), `tini` als PID 1, Healthcheck gegen `/api/health`, + Standalone-Build von Next.js. +- `docker/entrypoint.sh` wendet ausstehende Migrationen an — nur im + App-Container, nicht im Worker. +- `docker/postgres/10-init-app-role.sh` legt beim ersten Start die Rolle + `florian_app` (`NOBYPASSRLS`) samt Vorgabeprivilegien an. +- `.env.example` mit einem Kommentar zu jeder einzelnen Variable. + +**Datenmodell** + +- Prisma-Schema mit `Organisation`, `Person`, `Rolle`, `Berechtigung`, + `RolleBerechtigung`, `PersonRolle`, `AusweisToken`, `AuditLog`, `Datei`. +- `Fahrzeug` und `Geraet` bereits schmal angelegt, damit der Seed eine + realistische Wehr aufbauen kann. Phase 1 erweitert beide. +- Migration `20260902000100_rls_policies`: Row Level Security auf allen + mandantenpflichtigen Tabellen, Funktion `aktuelle_organisation()`, + Append-only-Trigger auf `audit_log`. +- Migration `20260902000200_audit_bereinigung`: `UPDATE` auf `audit_log` + bleibt dauerhaft gesperrt, `DELETE` ist nur im gekennzeichneten + Aufbewahrungslauf möglich. + +**Anwendungskern** + +- `lib/env.ts` — Zod-Validierung der gesamten Umgebung beim Start. +- `lib/db.ts` — getrennte Verbindungen für Anwendung (RLS aktiv) und System + (RLS-frei), `fuerOrganisation()` als einziger Weg zu Fachdaten. +- `lib/authz.ts` — 9 Rollen, 38 Einzelrechte, zentrale Prüffunktionen. +- `lib/storage.ts` — MinIO, presignte Up- und Downloads, SHA-256-Prüfsumme. +- `lib/audit.ts` — Protokolleinträge mit Vorher/Nachher-Vergleich, entfernt + Geheimnisse vor dem Schreiben. +- `lib/auth.ts` — Auth.js mit OIDC gegen authentik, lokalem Passwort-Login + und Terminal-Anmeldung über PIN oder RFID. +- `lib/warteschlange.ts` und `src/worker/` — BullMQ-Gerüst mit den + Auftragsarten für Fristen, Erinnerungen, PDF und Aufbewahrung. +- `lib/integrations/` — Mail, Telegram und LLM hinter abschaltbaren + Schnittstellen. +- tRPC v11 mit den Prozedurarten `oeffentlich`, `angemeldet` und + `mitRecht(...)`. + +**Oberfläche** + +- Basis-Layout mit Seitenleiste, heller und dunkler Darstellung + (Systemvorgabe als Standard) und durchgängig deutschen Texten. +- Anmeldeseite, die genau die eingerichteten Anmeldewege zeigt. +- Übersicht mit Kennzahlen und dem Sicherheitshinweis zur analogen + Atemschutzüberwachung. +- Farbwelt in OKLCH mit festen Statusfarben für Gerätezustände. + +**Daten und Tests** + +- Seed mit einer vollständigen Beispielwehr: 15 Personen mit Rollen und + Terminal-Ausweisen, 3 Fahrzeuge, 40 Geräte mit gemischten Prüffristen. +- Vitest: 32 Tests, darunter acht, die die Mandantentrennung gegen eine echte + Datenbank prüfen. +- Playwright: 10 Tests über Desktop und Tablet. +- `npm run screenshots` erzeugt die Bilder für die Anleitung. + +**Dokumentation** + +- README mit Schnellstart in sechs Zeilen. +- Anleitung, Roadmap, Aufwandsschätzung, Devlog und Versionierungsregeln. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..e209539 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,62 @@ +# Projekt: FLORIAN — Feuerwehr-Verwaltungsplattform + +## Kontext +Selbstgehostete Verwaltungssoftware für Freiwillige Feuerwehren in Deutschland +(Bayern). Zielgruppe: Kommandant, Gerätewart, Atemschutzgerätewart, Schriftführer, +Kassier, Mannschaft. Betrieb: Docker Compose auf Proxmox VE, kein Cloud-Zwang, +DSGVO-konform, Daten bleiben im Gerätehaus bzw. auf eigener Hardware. + +## Nicht verhandelbarer Stack +- Next.js 15 App Router, TypeScript strict, React Server Components wo sinnvoll +- Prisma + PostgreSQL 17 mit PostGIS +- tRPC für interne Aufrufe, REST unter /api/v1 für externe Integrationen +- Tailwind CSS + shadcn/ui +- Redis + BullMQ für Hintergrundjobs +- MinIO (S3-API) für alle Dateien — niemals lokale Pfade +- Zod für jede Ein- und Ausgabevalidierung +- Vitest (Unit) + Playwright (E2E) +- Alles auf Deutsch: UI-Texte, Feldnamen in der UI, Kommentare. + Code-Bezeichner auf Englisch, Domänenbegriffe deutsch belassen + (z. B. `Atemschutztrupp`, `Pruefintervall`, `Geraetewart`). + +## Architekturregeln +1. Jede Tabelle hat `organisationId` (Mandant). Postgres RLS ist aktiv. + Kein Query ohne Mandantenfilter. +2. Jede schreibende Operation erzeugt einen Eintrag in `AuditLog` + (Actor, Zeitpunkt, Entität, Vorher/Nachher als JSONB). Append-only. +3. Prüf-, Wartungs- und Buchungsdaten werden nie hart gelöscht (Soft-Delete + + Versionierung). +4. Alle Fristen und Zeitberechnungen serverseitig, Zeitzone Europe/Berlin. +5. Berechtigungen zentral in `lib/authz.ts`. Keine Rollenprüfung in Komponenten. +6. Dateizugriff ausschließlich über `lib/storage.ts`. +7. Externe Dienste (LLM, Mail, Telegram) hinter Interfaces in `lib/integrations/`, + damit sie im Test mockbar und optional abschaltbar sind. + +## Domänenwissen, das zu beachten ist +- DGUV-Prüffristen: Geräte haben Prüfintervalle aus Vorschrift oder + Herstellervorgabe. Status: einsatzbereit | pruefung_faellig | gesperrt | + ausgesondert. +- Lebensdauerkategorien: Helme, Seile, Pressluftatmer, Schutzkleidung haben + eine maximale Nutzungsdauer ab Herstellungs- oder Indienststellungsdatum. +- FwDV 7 (Atemschutz): Überwachung dokumentiert Truppzusammensetzung, + Einsatzbeginn, Druckwerte in Intervallen, Rückzugsdruck, Ende. +- FwDV 3: Einsatzstärke wird als Staffel/Gruppe/Zug erfasst. +- Einsatzzeitstempel: Alarmierung, Ausrücken, Eintreffen, Einsatzende, + Einrücken, Einsatzbereitschaft. + +## Sicherheits- und Rechtsleitplanken +- Atemschutzüberwachung und Alarmmonitor sind sicherheitsrelevant. Jede + entsprechende Ansicht zeigt einen Hinweis, dass die analoge Überwachung + führend bleibt, solange das System nicht freigegeben ist. +- Personenbezogene Daten: Löschkonzept, Export je Person, Aufbewahrungsfristen + konfigurierbar. +- Keine Telemetrie, keine externen CDNs, keine Google Fonts. Alles lokal. + +## Arbeitsweise +- Arbeite phasenweise. Fasse nie mehrere Phasen in einem Schritt zusammen. +- Beginne jede Phase mit dem Prisma-Schema, dann Migration, dann Server-Logik, + dann UI, dann Tests. +- Nach jeder Phase: `docker compose up` muss durchlaufen, `npm run test` + muss grün sein, README-Abschnitt zur Phase ergänzen. +- Wenn eine Anforderung fachlich unklar ist: nachfragen statt raten. + Feuerwehr-Fachlichkeit ist regional unterschiedlich. \ No newline at end of file diff --git a/Phase-0.md b/Phase-0.md new file mode 100644 index 0000000..ece7d54 --- /dev/null +++ b/Phase-0.md @@ -0,0 +1,23 @@ +Lies CLAUDE.md. Erstelle Phase 0 des Projekts: das Fundament. + +Liefere: +1. Monorepo-Struktur (Next.js App, prisma/, docker/, scripts/) +2. docker-compose.yml mit: traefik, app, worker, postgres (postgis/postgis:17), + redis, minio, gotenberg. Dazu docker-compose.override.yml für Entwicklung + und eine vollständige .env.example mit Kommentaren zu jeder Variable. +3. Dockerfile für die App (Multi-Stage, non-root User, Healthcheck). +4. Prisma-Schema mit den Basismodellen: Organisation, Person, Rolle, + Berechtigung, AusweisToken, AuditLog, Datei. Inklusive RLS-Policies als + SQL-Migration. +5. Auth: OIDC gegen authentik (Auth.js) plus lokaler E-Mail/Passwort-Fallback + für den Erststart, plus Terminal-Login per PIN/RFID-Token für den Kiosk. +6. lib/authz.ts mit Rollen: SUPERADMIN, KOMMANDANT, GERAETEWART, + ATEMSCHUTZGERAETEWART, KASSIER, SCHRIFTFUEHRER, AUSBILDER, MITGLIED, KIOSK. +7. lib/storage.ts (MinIO), lib/audit.ts, lib/db.ts (mit Mandanten-Kontext). +8. Basis-Layout mit shadcn/ui, deutschem UI-Text, Dark Mode, Seitenleiste. +9. Seed-Skript: eine Beispielwehr, 15 Personen, 3 Fahrzeuge, 40 Geräte. +10. Vitest- und Playwright-Setup mit je einem Beispieltest. +11. README mit Schnellstart in unter 10 Zeilen. + +Erkläre am Ende kurz, wie ich den Stack hochfahre und mich anmelde. +Frage nach, bevor du Annahmen über meine authentik-Konfiguration triffst. \ No newline at end of file diff --git a/Phase-1.md b/Phase-1.md new file mode 100644 index 0000000..d0d71be --- /dev/null +++ b/Phase-1.md @@ -0,0 +1,19 @@ +Phase 1: Stammdatenverwaltung. + +Modelle: Standort, Fahrzeug (mit Funkrufname, Kennzeichen, Fahrgestellnummer, +Baujahr, Typ nach DIN, TÜV-Termin), Geraeteraum, Geraetetyp, Geraet +(Inventarnummer, Seriennummer, Hersteller, Herstellungsdatum, +Indienststellung, Standort ODER Fahrzeug+Geraeteraum, Status, +Prüfintervall, Lebensdauer-Kategorie), Qualifikationstyp, Qualifikation +(Person, Typ, erworben_am, gueltig_bis). + +Funktionen: +- CRUD mit Tabellen (Sortierung, Filter, Volltextsuche, Spaltenauswahl) +- QR-Code je Gerät generieren, Etikettenbogen als PDF (DIN A4, 65 Etiketten) +- QR-Scan im Browser (Kamera) öffnet direkt die Gerätedetailseite +- CSV-/Excel-Import mit Spaltenzuordnung und Vorschau vor dem Übernehmen +- Gerätehistorie (Standortwechsel, Statuswechsel) aus dem AuditLog gerendert +- Qualifikationen mit Ablaufwarnung im Dashboard + +UI: Deutsch, tastaturfreundlich, Massenbearbeitung für Statuswechsel. +Tests für Import und QR-Route. \ No newline at end of file diff --git a/Phase-2.md b/Phase-2.md new file mode 100644 index 0000000..e7c6804 --- /dev/null +++ b/Phase-2.md @@ -0,0 +1,29 @@ +Phase 2: Wartungs- und Prüfmanagement. Das ist das wichtigste Modul. + +Modelle: Wartungsvorlage (Name, Geraetetyp, Intervall in Monaten, +Vorwarnzeit, Pruefvorschrift-Referenz), Checklistenpunkt (Text, Typ: +ja_nein | messwert | text | foto, Pflichtfeld, Sollwert), Wartungstermin +(Geraet, Vorlage, faellig_am, Status), Pruefung (Termin, Prüfer, +durchgefuehrt_am, Dauer in Minuten, Ergebnis: bestanden | +bestanden_mit_maengeln | nicht_bestanden, extern durchgeführt von, +Kosten), Pruefergebnis (Checklistenpunkt, Wert, Foto), Signatur, +Mangel (Geraet, Melder, Beschreibung, Foto, Prioritaet, Status, +behoben_am, behoben_von), Rueckruf (Hersteller, betroffene Typen/Chargen). + +Funktionen: +- Fristen-Dashboard: überfällig / diese Woche / diesen Monat / nächste 90 Tage, + gruppierbar nach Fahrzeug, Standort, Gerätetyp, Prüfer +- BullMQ-Job (nächtlich): Termine generieren, Erinnerungen bei T-30/T-7/T-0, + Gerätestatus automatisch auf "pruefung_faellig" setzen +- Prüfungs-Wizard: Checkliste Schritt für Schritt, Fotoupload je Punkt, + Zeiterfassung, Unterschrift auf Canvas (Touch und Maus) +- PDF-Prüfprotokoll via Gotenberg: Kopf mit Wehr/Gerät/Prüfer-ID, + Checkliste mit Ergebnissen, eingebettete Fotos, eingebettete Signatur, + Fußzeile mit Prüfvorschrift und Prüfdatum. Ablage in MinIO, unveränderbar. +- Lebensdauerüberwachung: Warnung X Monate vor Ablauf, Vorschlag zur Aussonderung +- Rückrufe: betroffene Geräte automatisch markieren und optional sperren +- Externe Wartung: Fachfirma erfassen, Fremdprotokoll als PDF anhängen +- Mängel-Workflow: melden → zugewiesen → in Arbeit → behoben, mit Benachrichtigung + +Wichtig: Ein abgeschlossenes Prüfprotokoll ist unveränderbar. Korrekturen +erzeugen ein neues Protokoll mit Verweis auf das ursprüngliche. \ No newline at end of file diff --git a/Phase-3.md b/Phase-3.md new file mode 100644 index 0000000..e98bd3a --- /dev/null +++ b/Phase-3.md @@ -0,0 +1,20 @@ +Phase 3: Einsatz- und Übungserfassung. + +Modelle: Einsatz (Einsatznummer, Stichwort, Kategorie nach Landesstatistik, +Adresse, Geokoordinaten, Zeitstempel: alarmiert/ausgerueckt/eingetroffen/ +einsatzende/eingerueckt/einsatzbereit, Beschreibung, Bericht, +Einsatzleiter, Wetter), Einsatzfahrzeug, Einsatzkraft (Person, Funktion, +Zeit von/bis), Materialverbrauch, Lagemeldung (Text, Foto, wichtig-Flag), +Uebung (analog, mit Ausbildungsthema und Lernzielen). + +Funktionen: +- Schnellerfassung: Stichwort + Adresse + Fahrzeuge, Rest später nachtragen +- Geocoding über self-hosted Nominatim (mit Fallback auf manuelle Koordinaten) +- Personalauswahl mit Vorschlag anhand Verfügbarkeit und Qualifikation +- Stärkeberechnung nach FwDV 3 (z. B. 1/2/6/9) automatisch +- Einsatzbericht als PDF, Sammelexport pro Jahr für die Gemeinde +- Statistik: Einsätze nach Kategorie/Monat, Eintreffzeiten als Verteilung, + Beteiligung je Person, Fahrzeugauslastung +- Externes Portal: signierter QR-Link, ohne Login, für Fremdwehren — + nur Statusmeldung und Lagemeldung mit Foto, zeitlich begrenzt gültig, + rate-limited \ No newline at end of file diff --git a/Phase-4.md b/Phase-4.md new file mode 100644 index 0000000..0d82f79 --- /dev/null +++ b/Phase-4.md @@ -0,0 +1,21 @@ +Phase 4: Kiosk-Oberfläche als PWA für Wandtablets in der Fahrzeughalle. + +Anforderungen: +- Eigene Route /kiosk mit eigenem Layout: große Kacheln, mindestens 64px + Touchziele, hoher Kontrast, mit Handschuhen bedienbar, kein Hover-Zwang +- Login: RFID (WebHID/Tastaturemulation des Lesers), 6-stellige PIN, + QR-Code vom Handy. Auto-Logout nach 90 Sekunden Inaktivität. +- Rollenabhängige Kachelauswahl +- Wizards: Wartung durchführen, Fahrtenbuch eintragen, Mangel melden + (QR scannen → Foto → fertig in unter 30 Sekunden), Inventur scannen, + Wäsche abgeben, Anwesenheit ein-/auschecken +- Hallen-Monitor-Modus (/kiosk/monitor): Vollbild ohne Interaktion, zeigt + nächste Termine, überfällige Prüfungen, Fahrzeugstatus, Wetterwarnung + (DWD Open Data), Uhrzeit +- Offline: Service Worker cached App-Shell und Stammdaten. Schreibende + Aktionen gehen in eine IndexedDB-Outbox (Dexie) mit UUID als Idempotenzschlüssel + und werden bei Reconnect synchronisiert. Sichtbare Statusanzeige + "offline / N Änderungen ausstehend". Konflikte: Server gewinnt bei + Stammdaten, Client gewinnt bei neuen Datensätzen, Konfliktliste für Admin. + +Schreibe Playwright-Tests, die den Offline-Fall simulieren. \ No newline at end of file diff --git a/Phase-5.md b/Phase-5.md new file mode 100644 index 0000000..bb1ae47 --- /dev/null +++ b/Phase-5.md @@ -0,0 +1,28 @@ +Phase 5: Atemschutzüberwachung nach FwDV 7. + +WICHTIG: Jede Ansicht dieses Moduls zeigt oben dauerhaft den Hinweis: +"Ergänzendes System — die analoge Atemschutzüberwachung bleibt führend." +Dieser Hinweis ist erst über eine Konfigurationsoption abschaltbar, die +dokumentiert eine Freigabe durch den Kommandanten voraussetzt. + +Modelle: Atemschutzeinsatz (Einsatz, Überwacher, Beginn, Ende), +Trupp (Truppführer, Truppmann, Auftrag, Funkrufname, Status: +bereit|im_einsatz|rueckweg|draussen), Druckmessung (Trupp, Person, +Zeitpunkt, Druck in bar, Art: eingang|zwischen|ausgang), +G26Untersuchung (Person, Datum, gueltig_bis), Belastungsuebung +(Person, Datum, Art, Ort). + +Funktionen: +- Truppanlage per RFID-Scan oder Auswahl, Eingangsdruck erfassen +- Live-Timer je Trupp, serverseitig autoritativ, per Socket.IO verteilt +- Rückzugsdruck-Berechnung (konfigurierbare Formel, Standard: + Eingangsdruck-Verbrauchsrate) mit optischer Ampel und akustischem Alarm + bei Schwellwert +- Erinnerung zur Druckabfrage in konfigurierbarem Intervall (Standard 10 min) +- Notfallknopf je Trupp (Mayday) mit Protokolleintrag +- Lückenloses Protokoll als PDF, angehängt an den Einsatz +- Verwaltung: G26.3-Fristen, Belastungsübungen, Einsatzstunden je AGT, + Tauglichkeitsübersicht mit Ampel, Dashboard-Widget "verfügbare AGT" + +Timer müssen einen Browser-Reload und einen Verbindungsabbruch überstehen. +Das ist die wichtigste Testanforderung. \ No newline at end of file diff --git a/Phase-6.md b/Phase-6.md new file mode 100644 index 0000000..db01846 --- /dev/null +++ b/Phase-6.md @@ -0,0 +1,15 @@ +Phase 6: Kartenmodule. + +- MapLibre GL mit self-hosted Tiles (tileserver-gl, MBTiles fürs Ausrückegebiet) +- Hydranten: PostGIS-Point, Typ (Unterflur/Überflur/Saugstelle/Zisterne/ + offenes Gewaesser), Nennweite, Durchfluss l/min, letzte Prüfung, Status, + Foto, Notiz. Import aus GeoJSON und aus einer CSV der Stadtwerke. + Cluster-Darstellung, Filter nach Status, Umkreissuche. +- Objekte/Feuerwehrpläne: Objekt mit Adresse, Kategorie, Ansprechpartner, + Zufahrt, Gefahren, Schlüsseldepot, angehängte PDF-Pläne nach DIN 14095, + Volltextsuche, Sofortzugriff vom Einsatz aus +- Lagekarte: Zeichenebene mit taktischen Zeichen nach FwDV 102 als SVG-Set, + freie Skizzen, Speicherung je Einsatz, Echtzeit-Sync zwischen Geräten +- Wasserförderung: Strecke aus Wegpunkten, Höhenprofil, Berechnung von + Druckverlust und benötigten Verstärkerpumpen bei gegebener Schlauchlänge, + Durchmesser und Fördermenge. Ausgabe als druckbares Schema. \ No newline at end of file diff --git a/Phase-7.md b/Phase-7.md new file mode 100644 index 0000000..ed8aace --- /dev/null +++ b/Phase-7.md @@ -0,0 +1,14 @@ +Phase 7: Benachrichtigungen und Termine. + +- Notification-Abstraktion mit Kanälen: E-Mail (SMTP), Telegram (Bot API), + ntfy (self-hosted Push), Web-Push. WhatsApp bewusst NICHT — begründe das + im README kurz mit Kosten und Datenschutz. +- Benachrichtigungsvorlagen mit Platzhaltern, pro Ereignistyp und Rolle + konfigurierbar, je Person abonnierbar +- Ereignisse: Prüfung fällig, Mangel gemeldet, Qualifikation läuft ab, + neuer Termin, Termin-Erinnerung, Einsatzbericht freigegeben +- Kalender: Dienste, Übungen, Lehrgänge, Veranstaltungen, Arbeitsdienste. + Serientermine, Selbstanmeldung mit Teilnehmerlimit, Absage mit Grund, + Anwesenheitsliste zum Ausdrucken, CalDAV-/ICS-Feed je Person mit Token. +- Digitaler Dienstausweis: signierter QR mit kurzer Gültigkeit, Prüfseite + für Kontrollen, Anzeige von Name, Wehr, Funktion, Foto, Qualifikationen. \ No newline at end of file diff --git a/Phase-8.md b/Phase-8.md new file mode 100644 index 0000000..8e1da95 --- /dev/null +++ b/Phase-8.md @@ -0,0 +1,16 @@ +Phase 8: Lokale KI-Funktionen. Alles gegen Ollama, keine externen Anbieter. + +- lib/ai/ mit OpenAI-kompatiblem Client, Modell und Endpunkt konfigurierbar, + komplett abschaltbar per Feature-Flag +- Diktat: Audioaufnahme im Browser → faster-whisper Container → Transkript + in das Einsatzberichtsfeld +- Berichtsassistent: aus Stichpunkten, Zeitstempeln, eingesetzten Fahrzeugen + und Materialverbrauch einen strukturierten Einsatzberichtsentwurf erzeugen. + Ausgabe immer als Vorschlag mit Übernehmen/Verwerfen, nie automatisch speichern. +- Wiki mit pgvector: Dienstvorschriften und Ausbildungsunterlagen als + Markdown, semantische Suche, RAG-Antwort mit Quellenangabe der Wiki-Seite +- Belegerfassung: Foto einer Quittung → Vision-Modell → Vorschlag für + Betrag, Datum, Händler, Kategorie. Immer manuell bestätigen. + +Jede KI-Ausgabe ist als solche gekennzeichnet und muss bestätigt werden, +bevor sie in ein Dokument mit Nachweischarakter gelangt. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..fe94a53 --- /dev/null +++ b/README.md @@ -0,0 +1,216 @@ +# FLORIAN — Feuerwehr-Verwaltungsplattform + +Selbstgehostete Verwaltungssoftware für Freiwillige Feuerwehren. Läuft im +Gerätehaus, nicht in der Cloud. Keine Telemetrie, keine externen CDNs. + +## Schnellstart + +```bash +git clone https://git.wilde-server.de/florian.wilde/FireAdministration.git && cd FireAdministration +cp .env.example .env # Passwörter und AUTH_SECRET ersetzen +npm ci # Abhängigkeiten +docker compose up -d # Postgres, Redis, MinIO, Gotenberg +npm run db:migrate && npm run db:seed # Schema und Beispielwehr +npm run dev # http://localhost:3100 +``` + +Anmelden mit `SEED_ADMIN_EMAIL` und `SEED_ADMIN_PASSWORT` aus der `.env` +(Vorgabe: `admin@beispielwehr.de` / `Feuerwehr2026`). Passwort danach ändern. + +--- + +## Was drin ist + +| Bereich | Stand | +|---|---| +| Phase 0 — Fundament | fertig | +| Phase 1 — Stammdaten | offen | +| Phase 2 — Wartung und Prüfung | offen | +| Phase 3 — Einsätze und Übungen | offen | +| Phase 4 — Kiosk-PWA | offen | +| Phase 5 — Atemschutz (FwDV 7) | offen | +| Phase 6 — Kartenmodule | offen | +| Phase 7 — Benachrichtigungen und Termine | offen | +| Phase 8 — Lokale KI | offen | + +Vollständige Planung mit Aufwänden: [docs/ROADMAP.md](docs/ROADMAP.md) und +[docs/AUFWANDSSCHAETZUNG.md](docs/AUFWANDSSCHAETZUNG.md). + +## Weiterführende Dokumentation + +| Datei | Inhalt | +|---|---| +| [docs/ANLEITUNG.md](docs/ANLEITUNG.md) | Bedienung mit Bildschirmfotos | +| [docs/ROADMAP.md](docs/ROADMAP.md) | Phasenplan und was jeweils entsteht | +| [docs/AUFWANDSSCHAETZUNG.md](docs/AUFWANDSSCHAETZUNG.md) | Aufwand je Phase, Schätzung gegen Ist | +| [docs/DEVLOG.md](docs/DEVLOG.md) | Entwicklungstagebuch mit Zeiterfassung | +| [docs/VERSIONIERUNG.md](docs/VERSIONIERUNG.md) | Versions- und Branch-Regeln | +| [CHANGELOG.md](CHANGELOG.md) | Was sich wann geändert hat | + +--- + +## Phase 0 — Fundament + +### Aufbau + +``` +FireAdministration/ +├─ docker/ Dockerfile, Entrypoint, Postgres-Initialisierung +├─ prisma/ Schema, Migrationen, Seed +├─ scripts/ Hilfsskripte (Bildschirmfotos) +├─ src/ +│ ├─ app/ Next.js App Router +│ │ ├─ (app)/ alles, was Anmeldung verlangt +│ │ ├─ anmelden/ Anmeldeseite +│ │ └─ api/ Auth.js, tRPC, Health +│ ├─ components/ shadcn/ui-Bausteine und Layout +│ ├─ lib/ authz, db, storage, audit, env, auth, warteschlange +│ ├─ server/ tRPC-Router und Kontext +│ └─ worker/ BullMQ-Hintergrundprozess +├─ tests/ Vitest (unit) und Playwright (e2e) +└─ docs/ Anleitung, Roadmap, Devlog, Aufwände +``` + +### Mandantentrennung + +Jede Tabelle trägt `organisationId`, und Postgres setzt die Trennung mit Row +Level Security durch. Es gibt zwei Datenbankverbindungen: + +- **`florian_app`** (`DATABASE_URL`) — RLS aktiv, `NOBYPASSRLS`. Über diese + Verbindung arbeitet die Anwendung. Ohne gesetztes + `app.current_organisation` liefert jede Abfrage **null Zeilen**. +- **Eigentümer** (`DIRECT_DATABASE_URL`) — umgeht RLS. Nur für Migrationen, + Seed, Anmeldung (die E-Mail-Suche muss über alle Wehren gehen) und + Wartungsjobs. + +Fachdaten laufen ausschließlich über `fuerOrganisation()` aus +[`src/lib/db.ts`](src/lib/db.ts): + +```ts +const personen = await fuerOrganisation(orgId, (db) => + db.person.findMany({ where: { aktiv: true } }), +); +``` + +Ein vergessener Mandantenfilter kostet damit im schlimmsten Fall ein leeres +Ergebnis — nicht die Daten einer fremden Wehr. +`tests/unit/mandantentrennung.test.ts` prüft das gegen eine echte Datenbank. + +### Audit-Log + +Jede schreibende Operation erzeugt einen Eintrag mit Akteur, Zeitpunkt, +Entität und Vorher/Nachher als JSONB. Die Tabelle ist append-only: + +- `UPDATE` ist per Trigger **immer** gesperrt. +- `DELETE` nur im gekennzeichneten Aufbewahrungslauf + (`SET LOCAL app.audit_bereinigung = 'an'`), den ausschließlich der Worker + ausführt — nachdem er exportiert hat. Ohne diese Ausnahme ließe sich das + Löschkonzept nach DSGVO nicht umsetzen. + +Geheimnisse (Passwort-Hashes, PINs, Token) werden vor dem Schreiben entfernt. + +### Berechtigungen + +Alles in [`src/lib/authz.ts`](src/lib/authz.ts) — keine Rollenprüfung in +Komponenten. 9 Rollen, 38 Einzelrechte. Der Seed spiegelt den Katalog in die +Datenbank, damit eine Wehr Rechte nachträglich zuschneiden kann, ohne dass +jemand Code ändert. + +```ts +export const geraetSpeichern = mitRecht("geraet.bearbeiten").mutation(/* … */); +``` + +### Anmeldung + +Drei Wege, eine Sitzung: + +1. **OIDC gegen authentik** — der Regelweg. authentik wird **separat** + betrieben und ist nicht Teil dieses Stacks. Im Provider einzutragen: + Redirect-URI `${APP_URL}/api/auth/callback/authentik`, Scopes + `openid profile email`. +2. **E-Mail und Passwort** — Rückfalloption für den Erststart und für den Fall, + dass authentik nicht erreichbar ist. Über `AUTH_LOCAL_ENABLED` abschaltbar. +3. **Terminal (PIN oder RFID)** — für das Kiosk-Gerät im Gerätehaus. Zwei + Schranken: erst weist sich das *Gerät* mit `KIOSK_DEVICE_SECRET` aus, dann + die *Person* mit PIN oder Karte. Die vollständige Oberfläche dazu kommt in + Phase 4. + +Rollen aus authentik-Gruppen wirken als **Vorbelegung beim ersten Login** +(`AUTH_OIDC_GROUP_MAPPING`). Sobald jemand die Rolle in FLORIAN ändert, ist +FLORIAN führend und OIDC überschreibt nicht mehr. + +### Dateien + +Alles liegt in MinIO, nie im Dateisystem und nie in der Datenbank. Zugriff +ausschließlich über [`src/lib/storage.ts`](src/lib/storage.ts). Objektschlüssel: + +``` +///- +``` + +Downloads laufen über presignte Links mit begrenzter Gültigkeit +(`S3_URL_TTL`, Vorgabe 15 Minuten). + +--- + +## Befehle + +```bash +npm run dev # Entwicklungsserver auf Port 3100 +npm run build # Produktionsbuild +npm run worker:dev # Hintergrundprozess mit Neuladen +npm run test # Vitest +npm run test:e2e # Playwright +npm run typecheck # TypeScript ohne Ausgabe +npm run db:migrate # ausstehende Migrationen anwenden +npm run db:seed # Beispielwehr einspielen +npm run db:studio # Prisma Studio +npm run screenshots # Bilder für docs/ANLEITUNG.md erzeugen +``` + +## Dienste und Ports (Entwicklung) + +Bewusst nicht die Standardports, damit FLORIAN neben anderen lokalen Stacks +laufen kann. Alle Werte stehen in der `.env`. + +| Dienst | Adresse | +|---|---| +| Anwendung | http://localhost:3100 | +| PostgreSQL 17 + PostGIS | `localhost:5442` | +| Redis | `localhost:6389` | +| MinIO API | http://localhost:9010 | +| MinIO Weboberfläche | http://localhost:9011 | +| Gotenberg | http://localhost:3101 | +| Mailpit (nur Entwicklung) | http://localhost:8125 | +| Adminer (nur Entwicklung) | http://localhost:8081 | + +## Produktionsbetrieb + +```bash +docker compose -f docker-compose.yml up -d +``` + +Die Angabe von `-f` ist wichtig: ohne sie legt Docker Compose automatisch +`docker-compose.override.yml` darüber, und das ist die Entwicklungsvariante +ohne Traefik. In Produktion terminiert Traefik TLS und verteilt auf +`APP_DOMAIN` und `MINIO_DOMAIN`. + +Der App-Container wendet beim Start ausstehende Migrationen an; der Worker +tut das bewusst nicht, damit nicht zwei Prozesse gleichzeitig migrieren. + +## Datenschutz + +- Keine Telemetrie, keine externen CDNs, keine Google Fonts. Es werden nur + Schriften verwendet, die auf dem Gerät vorhanden sind. +- Aufbewahrungsfristen konfigurierbar (`AUFBEWAHRUNG_JAHRE`, + `AUDIT_AUFBEWAHRUNG_TAGE`, `PERSON_ANONYMISIERUNG_TAGE`). +- Externe Dienste (Mail, Telegram, LLM) sitzen hinter Schnittstellen in + `src/lib/integrations/` und sind einzeln abschaltbar. Ohne Freischaltung + wird nur ins Log geschrieben. + +## Sicherheitshinweis + +Atemschutzüberwachung und Alarmmonitor sind sicherheitsrelevant. Solange die +Wehr das System nicht ausdrücklich freigegeben hat, bleibt die **analoge +Überwachung führend**. Jede entsprechende Ansicht zeigt diesen Hinweis +dauerhaft an. diff --git a/components.json b/components.json new file mode 100644 index 0000000..30fbc61 --- /dev/null +++ b/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/app/globals.css", + "baseColor": "slate", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + } +} diff --git a/docker-compose.override.yml b/docker-compose.override.yml new file mode 100644 index 0000000..6f555c7 --- /dev/null +++ b/docker-compose.override.yml @@ -0,0 +1,116 @@ +# ============================================================================= +# Entwicklungs-Override. Docker Compose lädt diese Datei automatisch zusätzlich +# zu docker-compose.yml — für den Produktionsbetrieb daher explizit nur die +# Hauptdatei angeben: +# docker compose -f docker-compose.yml up -d +# +# Unterschiede zur Produktion: +# - kein Traefik (die Dienste hängen direkt an localhost-Ports) +# - Quellcode als Bind-Mount, Hot Reload über "next dev" +# - Mailpit als lokaler SMTP-Auffangkorb +# - Adminer zum Draufschauen auf die Datenbank +# ============================================================================= + +services: + traefik: + # Wird in der Entwicklung nicht gebraucht. Mit dem Profil "proxy" trotzdem + # startbar: docker compose --profile proxy up -d + profiles: ["proxy"] + + postgres: + ports: + - "${POSTGRES_PORT:-5432}:5432" + + redis: + ports: + - "${REDIS_PORT:-6379}:6379" + + minio: + ports: + - "${MINIO_API_PORT:-9000}:9000" + - "${MINIO_CONSOLE_PORT:-9001}:9001" + environment: + # Ohne TLS-Terminierung zeigt die Konsole auf localhost. + MINIO_BROWSER_REDIRECT_URL: http://localhost:${MINIO_CONSOLE_PORT:-9001} + MINIO_SERVER_URL: http://localhost:${MINIO_API_PORT:-9000} + labels: + - traefik.enable=false + + gotenberg: + ports: + - "${GOTENBERG_PORT:-3101}:3000" + + app: + build: + target: builder + command: ["npm", "run", "dev"] + ports: + # "next dev" laeuft im Container auf 3100 (siehe package.json). + - "${APP_PORT:-3100}:3100" + environment: + NODE_ENV: development + # Datei-Watching funktioniert über Bind-Mounts von Windows aus nur mit + # Polling zuverlässig. + WATCHPACK_POLLING: "true" + CHOKIDAR_USEPOLLING: "true" + volumes: + - ./src:/app/src + - ./prisma:/app/prisma + - ./public:/app/public + - ./tests:/app/tests + - ./next.config.ts:/app/next.config.ts + - ./tsconfig.json:/app/tsconfig.json + - ./postcss.config.mjs:/app/postcss.config.mjs + # Anonyme Volumes: node_modules und .next bleiben im Container. + - /app/node_modules + - /app/.next + labels: + - traefik.enable=false + + worker: + build: + target: builder + command: ["npm", "run", "worker:dev"] + environment: + NODE_ENV: development + ROLLE: worker + volumes: + - ./src:/app/src + - ./prisma:/app/prisma + - /app/node_modules + + # --------------------------------------------------------------------------- + # Nur Entwicklung: SMTP-Auffangkorb. Alle Mails landen hier statt beim + # Empfänger. Weboberfläche: http://localhost:${MAILPIT_UI_PORT:-8125} + # --------------------------------------------------------------------------- + mailpit: + image: axllent/mailpit:latest + container_name: florian-mailpit + restart: unless-stopped + environment: + MP_MAX_MESSAGES: 500 + TZ: ${TZ:-Europe/Berlin} + ports: + - "${MAILPIT_UI_PORT:-8125}:8025" + - "${MAILPIT_SMTP_PORT:-1125}:1025" + networks: + - intern + + # --------------------------------------------------------------------------- + # Nur Entwicklung: schlanke Datenbankoberfläche. http://localhost:${ADMINER_PORT:-8081} + # Anmeldung: System PostgreSQL, Server "postgres", Benutzer/Passwort aus .env + # --------------------------------------------------------------------------- + adminer: + image: adminer:latest + container_name: florian-adminer + restart: unless-stopped + environment: + ADMINER_DEFAULT_SERVER: postgres + ADMINER_DESIGN: dracula + ports: + - "${ADMINER_PORT:-8081}:8080" + depends_on: + postgres: + condition: service_healthy + networks: + - intern diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..1d1441d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,335 @@ +# ============================================================================= +# FLORIAN — Produktionsnaher Stack. +# Für die Entwicklung wird docker-compose.override.yml automatisch daraufgelegt; +# die veröffentlicht die Dienste direkt auf localhost und braucht kein Traefik. +# Produktion starten: docker compose -f docker-compose.yml up -d +# Entwicklung starten: docker compose up -d +# ============================================================================= + +name: florian + +x-app-env: &app-env + NODE_ENV: ${NODE_ENV:-production} + TZ: ${TZ:-Europe/Berlin} + APP_URL: ${APP_URL:-http://localhost:3000} + DATABASE_URL: ${DATABASE_URL} + DIRECT_DATABASE_URL: ${DIRECT_DATABASE_URL} + REDIS_URL: ${REDIS_URL} + WORKER_CONCURRENCY: ${WORKER_CONCURRENCY:-5} + S3_ENDPOINT: ${S3_ENDPOINT:-minio} + S3_PORT: ${S3_PORT:-9000} + S3_USE_SSL: ${S3_USE_SSL:-false} + S3_ACCESS_KEY: ${S3_ACCESS_KEY} + S3_SECRET_KEY: ${S3_SECRET_KEY} + S3_REGION: ${S3_REGION:-eu-central-1} + S3_BUCKET: ${S3_BUCKET:-florian} + S3_PUBLIC_URL: ${S3_PUBLIC_URL} + S3_URL_TTL: ${S3_URL_TTL:-900} + GOTENBERG_URL: ${GOTENBERG_URL:-http://gotenberg:3000} + GOTENBERG_TIMEOUT: ${GOTENBERG_TIMEOUT:-30} + AUTH_SECRET: ${AUTH_SECRET} + AUTH_TRUST_HOST: ${AUTH_TRUST_HOST:-true} + AUTH_SESSION_MAX_AGE: ${AUTH_SESSION_MAX_AGE:-28800} + AUTH_OIDC_ENABLED: ${AUTH_OIDC_ENABLED:-false} + AUTH_OIDC_ISSUER: ${AUTH_OIDC_ISSUER:-} + AUTH_OIDC_CLIENT_ID: ${AUTH_OIDC_CLIENT_ID:-} + AUTH_OIDC_CLIENT_SECRET: ${AUTH_OIDC_CLIENT_SECRET:-} + AUTH_OIDC_NAME: ${AUTH_OIDC_NAME:-authentik} + AUTH_OIDC_GROUP_CLAIM: ${AUTH_OIDC_GROUP_CLAIM:-groups} + AUTH_OIDC_GROUP_MAPPING: ${AUTH_OIDC_GROUP_MAPPING:-} + AUTH_OIDC_AUTO_PROVISION: ${AUTH_OIDC_AUTO_PROVISION:-true} + AUTH_OIDC_STANDARD_ORGANISATION: ${AUTH_OIDC_STANDARD_ORGANISATION:-} + AUTH_LOCAL_ENABLED: ${AUTH_LOCAL_ENABLED:-true} + AUTH_BCRYPT_ROUNDS: ${AUTH_BCRYPT_ROUNDS:-12} + KIOSK_ENABLED: ${KIOSK_ENABLED:-true} + KIOSK_DEVICE_SECRET: ${KIOSK_DEVICE_SECRET} + KIOSK_PIN_LAENGE: ${KIOSK_PIN_LAENGE:-6} + KIOSK_MAX_FEHLVERSUCHE: ${KIOSK_MAX_FEHLVERSUCHE:-5} + KIOSK_SPERRDAUER: ${KIOSK_SPERRDAUER:-300} + KIOSK_IDLE_TIMEOUT: ${KIOSK_IDLE_TIMEOUT:-120} + MAIL_ENABLED: ${MAIL_ENABLED:-false} + MAIL_HOST: ${MAIL_HOST:-} + MAIL_PORT: ${MAIL_PORT:-587} + MAIL_SECURE: ${MAIL_SECURE:-false} + MAIL_USER: ${MAIL_USER:-} + MAIL_PASSWORD: ${MAIL_PASSWORD:-} + MAIL_FROM: ${MAIL_FROM:-} + TELEGRAM_ENABLED: ${TELEGRAM_ENABLED:-false} + TELEGRAM_BOT_TOKEN: ${TELEGRAM_BOT_TOKEN:-} + TELEGRAM_CHAT_ID: ${TELEGRAM_CHAT_ID:-} + LLM_ENABLED: ${LLM_ENABLED:-false} + LLM_BASE_URL: ${LLM_BASE_URL:-} + LLM_API_KEY: ${LLM_API_KEY:-} + LLM_MODELL: ${LLM_MODELL:-} + AUFBEWAHRUNG_JAHRE: ${AUFBEWAHRUNG_JAHRE:-10} + AUDIT_AUFBEWAHRUNG_TAGE: ${AUDIT_AUFBEWAHRUNG_TAGE:-3650} + PERSON_ANONYMISIERUNG_TAGE: ${PERSON_ANONYMISIERUNG_TAGE:-365} + NEXT_TELEMETRY_DISABLED: "1" + +x-logging: &logging + driver: json-file + options: + max-size: "10m" + max-file: "5" + +services: + # --------------------------------------------------------------------------- + # Reverse Proxy. Terminiert TLS, verteilt auf App und MinIO. + # --------------------------------------------------------------------------- + traefik: + image: traefik:v3.3 + container_name: florian-traefik + restart: unless-stopped + command: + - --providers.docker=true + - --providers.docker.exposedByDefault=false + - --providers.docker.network=florian-edge + - --entrypoints.web.address=:80 + - --entrypoints.web.http.redirections.entryPoint.to=websecure + - --entrypoints.web.http.redirections.entryPoint.scheme=https + - --entrypoints.websecure.address=:443 + - --certificatesresolvers.le.acme.email=${ACME_EMAIL} + - --certificatesresolvers.le.acme.storage=/acme/acme.json + - --certificatesresolvers.le.acme.tlschallenge=true + # Kein anonymes Nutzungs-Reporting. + - --global.sendAnonymousUsage=false + - --global.checkNewVersion=false + - --log.level=INFO + - --accesslog=true + ports: + - "80:80" + - "443:443" + volumes: + # Nur lesend — Traefik braucht die Socket-Verbindung zur Diensterkennung. + - /var/run/docker.sock:/var/run/docker.sock:ro + - traefik-acme:/acme + networks: + - edge + logging: *logging + + # --------------------------------------------------------------------------- + # PostgreSQL 17 mit PostGIS. Einziger Ort für relationale Daten. + # --------------------------------------------------------------------------- + postgres: + image: postgis/postgis:17-3.5 + container_name: florian-postgres + restart: unless-stopped + environment: + POSTGRES_USER: ${POSTGRES_USER:-florian} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + POSTGRES_DB: ${POSTGRES_DB:-florian} + POSTGRES_APP_PASSWORD: ${POSTGRES_APP_PASSWORD} + # Deutsche Sortierung ueber ICU — das Debian-Basisimage hat keine + # de_DE-Locale erzeugt, ICU liefert sie unabhaengig vom Betriebssystem. + POSTGRES_INITDB_ARGS: "--locale-provider=icu --icu-locale=de-DE --locale=C.UTF-8 --encoding=UTF8" + TZ: ${TZ:-Europe/Berlin} + volumes: + - postgres-data:/var/lib/postgresql/data + # Legt beim ERSTEN Start den RLS-pflichtigen Anwendungsbenutzer an. + - ./docker/postgres:/docker-entrypoint-initdb.d:ro + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-florian} -d ${POSTGRES_DB:-florian}"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 30s + networks: + - intern + logging: *logging + + # --------------------------------------------------------------------------- + # Redis. Warteschlangen (BullMQ) und Rate-Limiting. Keine Nutzdaten. + # --------------------------------------------------------------------------- + redis: + image: redis:7-alpine + container_name: florian-redis + restart: unless-stopped + command: + - redis-server + - --requirepass + - ${REDIS_PASSWORD} + - --appendonly + - "yes" + - --maxmemory-policy + - noeviction + volumes: + - redis-data:/data + healthcheck: + test: ["CMD", "redis-cli", "-a", "${REDIS_PASSWORD}", "ping"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 10s + networks: + - intern + logging: *logging + + # --------------------------------------------------------------------------- + # MinIO. Ablage ALLER Dateien — Fotos, Prüfprotokolle, Rechnungen, Exporte. + # In Produktion auf ein festes RELEASE-Tag pinnen. + # --------------------------------------------------------------------------- + minio: + image: minio/minio:latest + container_name: florian-minio + restart: unless-stopped + command: server /data --console-address ":9001" + environment: + MINIO_ROOT_USER: ${MINIO_ROOT_USER} + MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD} + MINIO_BROWSER_REDIRECT_URL: https://${MINIO_CONSOLE_DOMAIN} + MINIO_SERVER_URL: https://${MINIO_DOMAIN} + MINIO_UPDATE: "off" + TZ: ${TZ:-Europe/Berlin} + volumes: + - minio-data:/data + healthcheck: + test: ["CMD", "mc", "ready", "local"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 20s + networks: + - intern + - edge + labels: + - traefik.enable=true + # S3-API + - traefik.http.routers.minio-api.rule=Host(`${MINIO_DOMAIN}`) + - traefik.http.routers.minio-api.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure} + - traefik.http.routers.minio-api.tls.certresolver=le + - traefik.http.routers.minio-api.service=minio-api + - traefik.http.services.minio-api.loadbalancer.server.port=9000 + # Weboberfläche + - traefik.http.routers.minio-console.rule=Host(`${MINIO_CONSOLE_DOMAIN}`) + - traefik.http.routers.minio-console.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure} + - traefik.http.routers.minio-console.tls.certresolver=le + - traefik.http.routers.minio-console.service=minio-console + - traefik.http.services.minio-console.loadbalancer.server.port=9001 + logging: *logging + + # --------------------------------------------------------------------------- + # Einmaliger Job: legt den Bucket an und setzt ihn auf privat. + # --------------------------------------------------------------------------- + minio-init: + image: minio/mc:latest + container_name: florian-minio-init + depends_on: + minio: + condition: service_healthy + entrypoint: /bin/sh + command: + - -c + - | + mc alias set florian http://minio:9000 "$${MINIO_ROOT_USER}" "$${MINIO_ROOT_PASSWORD}" && + mc mb --ignore-existing florian/"$${S3_BUCKET}" && + mc anonymous set none florian/"$${S3_BUCKET}" && + mc version enable florian/"$${S3_BUCKET}" && + echo "Bucket $${S3_BUCKET} bereit." + environment: + MINIO_ROOT_USER: ${MINIO_ROOT_USER} + MINIO_ROOT_PASSWORD: ${MINIO_ROOT_PASSWORD} + S3_BUCKET: ${S3_BUCKET:-florian} + networks: + - intern + restart: "no" + logging: *logging + + # --------------------------------------------------------------------------- + # Gotenberg. Rendert HTML zu PDF (Prüfprotokolle, Berichte). + # --------------------------------------------------------------------------- + gotenberg: + image: gotenberg/gotenberg:8 + container_name: florian-gotenberg + restart: unless-stopped + command: + - gotenberg + - --chromium-disable-javascript=false + - --chromium-allow-list=file:///tmp/.* + - --api-timeout=${GOTENBERG_TIMEOUT:-30}s + environment: + TZ: ${TZ:-Europe/Berlin} + networks: + - intern + logging: *logging + + # --------------------------------------------------------------------------- + # Die Anwendung selbst (Next.js, Standalone-Build). + # Führt vor dem Start die ausstehenden Prisma-Migrationen aus. + # --------------------------------------------------------------------------- + app: + build: + context: . + dockerfile: docker/Dockerfile + target: runner + image: florian-app:${IMAGE_TAG:-latest} + container_name: florian-app + restart: unless-stopped + environment: + <<: *app-env + PORT: "3000" + HOSTNAME: "0.0.0.0" + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + minio: + condition: service_healthy + networks: + - intern + - edge + labels: + - traefik.enable=true + - traefik.http.routers.app.rule=Host(`${APP_DOMAIN}`) + - traefik.http.routers.app.entrypoints=${TRAEFIK_ENTRYPOINT:-websecure} + - traefik.http.routers.app.tls.certresolver=le + - traefik.http.services.app.loadbalancer.server.port=3000 + logging: *logging + + # --------------------------------------------------------------------------- + # Hintergrundjobs: Fristenberechnung, Erinnerungen, PDF-Erzeugung, Exporte. + # Gleiches Image wie die App, anderer Einstiegspunkt. + # --------------------------------------------------------------------------- + worker: + build: + context: . + dockerfile: docker/Dockerfile + target: runner + image: florian-app:${IMAGE_TAG:-latest} + container_name: florian-worker + restart: unless-stopped + command: ["node", "--enable-source-maps", "dist/worker/index.js"] + environment: + <<: *app-env + ROLLE: worker + healthcheck: + test: ["CMD", "node", "-e", "process.exit(0)"] + interval: 30s + timeout: 5s + retries: 3 + start_period: 20s + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_healthy + app: + condition: service_started + networks: + - intern + logging: *logging + +volumes: + postgres-data: + redis-data: + minio-data: + traefik-acme: + +networks: + # Nur was von außen erreichbar sein muss, hängt hier drin. + edge: + name: florian-edge + # Datenbank, Redis, Gotenberg: intern, kein Zugang von außen. + intern: + name: florian-intern + internal: false diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 0000000..596bae7 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,111 @@ +# ============================================================================= +# FLORIAN — Mehrstufiger Build. +# deps : Abhängigkeiten installieren (wird gecacht) +# builder : Prisma-Client erzeugen, Next.js bauen, Worker transpilieren +# runner : schlankes Laufzeit-Image, non-root, mit Healthcheck +# ============================================================================= + +ARG NODE_VERSION=22.20.0 + +# ----------------------------------------------------------------------------- +# Stufe 1: Abhängigkeiten +# ----------------------------------------------------------------------------- +FROM node:${NODE_VERSION}-bookworm-slim AS deps +WORKDIR /app + +# openssl wird von Prisma benötigt, ca-certificates für ausgehendes TLS. +RUN apt-get update \ + && apt-get install -y --no-install-recommends openssl ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +ENV NEXT_TELEMETRY_DISABLED=1 +COPY package.json package-lock.json* .npmrc ./ +RUN npm ci --include=dev + +# ----------------------------------------------------------------------------- +# Stufe 2: Build +# ----------------------------------------------------------------------------- +FROM node:${NODE_VERSION}-bookworm-slim AS builder +WORKDIR /app + +RUN apt-get update \ + && apt-get install -y --no-install-recommends openssl ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +ENV NEXT_TELEMETRY_DISABLED=1 +ENV NODE_ENV=production + +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Prisma-Client aus dem Schema erzeugen — ohne Datenbankverbindung. +RUN npx prisma generate + +# Der Next.js-Build braucht formal gültige Variablen, verbindet sich aber nicht. +# Die echten Werte kommen zur Laufzeit aus der Umgebung. +RUN DATABASE_URL="postgresql://build:build@localhost:5432/build?schema=public" \ + DIRECT_DATABASE_URL="postgresql://build:build@localhost:5432/build?schema=public" \ + AUTH_SECRET="build_time_platzhalter_mindestens_32_zeichen" \ + KIOSK_DEVICE_SECRET="build_time_platzhalter" \ + SKIP_ENV_VALIDATION=1 \ + npm run build + +# Worker separat nach CommonJS transpilieren, damit im Runtime-Image kein +# TypeScript-Toolchain mehr nötig ist. +RUN npx esbuild src/worker/index.ts \ + --bundle \ + --platform=node \ + --target=node22 \ + --format=cjs \ + --packages=external \ + --sourcemap \ + --outfile=dist/worker/index.js + +# ----------------------------------------------------------------------------- +# Stufe 3: Laufzeit +# ----------------------------------------------------------------------------- +FROM node:${NODE_VERSION}-bookworm-slim AS runner +WORKDIR /app + +RUN apt-get update \ + && apt-get install -y --no-install-recommends openssl ca-certificates tini \ + && rm -rf /var/lib/apt/lists/* + +ENV NODE_ENV=production +ENV NEXT_TELEMETRY_DISABLED=1 +ENV PORT=3000 +ENV HOSTNAME=0.0.0.0 +ENV TZ=Europe/Berlin + +# Eigener, unprivilegierter Benutzer. Läuft NICHT als root. +RUN groupadd --system --gid 1001 florian \ + && useradd --system --uid 1001 --gid florian --home /app --shell /usr/sbin/nologin florian + +# Standalone-Ausgabe von Next.js: enthält nur die tatsächlich benötigten Module. +COPY --from=builder --chown=florian:florian /app/.next/standalone ./ +COPY --from=builder --chown=florian:florian /app/.next/static ./.next/static +COPY --from=builder --chown=florian:florian /app/public ./public + +# Worker-Bundle und dessen Laufzeitabhängigkeiten. +COPY --from=builder --chown=florian:florian /app/dist ./dist + +# Prisma: Schema und Migrationen fürs Deployment, plus erzeugter Client. +COPY --from=builder --chown=florian:florian /app/prisma ./prisma +COPY --from=builder --chown=florian:florian /app/node_modules/.prisma ./node_modules/.prisma +COPY --from=builder --chown=florian:florian /app/node_modules/@prisma ./node_modules/@prisma +COPY --from=builder --chown=florian:florian /app/node_modules/prisma ./node_modules/prisma + +COPY --chown=florian:florian docker/entrypoint.sh /usr/local/bin/entrypoint.sh +COPY --chown=florian:florian docker/healthcheck.js /app/healthcheck.js +RUN chmod +x /usr/local/bin/entrypoint.sh + +USER florian +EXPOSE 3000 + +# Prüft den Endpunkt /api/health — der fragt Datenbank und Redis mit ab. +HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=3 \ + CMD node /app/healthcheck.js || exit 1 + +# tini als PID 1, damit Signale sauber durchgereicht werden. +ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/entrypoint.sh"] +CMD ["node", "server.js"] diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100644 index 0000000..3cd4ea7 --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,26 @@ +#!/bin/sh +# ============================================================================= +# Einstiegspunkt für App- und Worker-Container. +# Die App wendet vor dem Start ausstehende Migrationen an, der Worker nicht — +# sonst würden zwei Prozesse gleichzeitig migrieren. +# ============================================================================= +set -eu + +log() { + echo "[florian-entrypoint] $*" +} + +if [ "${ROLLE:-app}" = "app" ] && [ "${SKIP_MIGRATIONS:-false}" != "true" ]; then + log "Wende ausstehende Datenbankmigrationen an ..." + if node node_modules/prisma/build/index.js migrate deploy; then + log "Migrationen abgeschlossen." + else + log "FEHLER: Migrationen fehlgeschlagen. Container wird beendet." + exit 1 + fi +else + log "Rolle '${ROLLE:-app}' — Migrationen werden übersprungen." +fi + +log "Starte: $*" +exec "$@" diff --git a/docker/healthcheck.js b/docker/healthcheck.js new file mode 100644 index 0000000..c320f33 --- /dev/null +++ b/docker/healthcheck.js @@ -0,0 +1,25 @@ +// Healthcheck für den Docker-HEALTHCHECK. Bewusst ohne Abhängigkeiten, +// damit er auch dann läuft, wenn die Anwendung selbst nicht startet. +const http = require("node:http"); + +const port = process.env.PORT || "3000"; + +const anfrage = http.request( + { + host: "127.0.0.1", + port, + path: "/api/health", + method: "GET", + timeout: 4000, + }, + (antwort) => { + process.exit(antwort.statusCode === 200 ? 0 : 1); + }, +); + +anfrage.on("error", () => process.exit(1)); +anfrage.on("timeout", () => { + anfrage.destroy(); + process.exit(1); +}); +anfrage.end(); diff --git a/docker/postgres/10-init-app-role.sh b/docker/postgres/10-init-app-role.sh new file mode 100644 index 0000000..5090fb5 --- /dev/null +++ b/docker/postgres/10-init-app-role.sh @@ -0,0 +1,79 @@ +#!/bin/bash +# ============================================================================= +# Läuft EINMALIG beim allerersten Start des Postgres-Containers (leeres +# Datenverzeichnis). Richtet ein: +# - die benötigten Erweiterungen (PostGIS, pgcrypto, citext, unaccent, pg_trgm) +# - den Anwendungsbenutzer "florian_app" OHNE BYPASSRLS +# - Vorgabeprivilegien, damit künftige Tabellen automatisch nutzbar sind +# +# Wichtig zum Verständnis der Mandantentrennung: +# Der Eigentümer der Tabellen (POSTGRES_USER) umgeht Row Level Security — +# das ist gewollt, damit Migrationen und das Seed-Skript arbeiten können. +# Die Anwendung verbindet sich ausschließlich als "florian_app"; für den +# greift RLS, und ohne gesetztes app.current_organisation sieht er nichts. +# ============================================================================= +set -euo pipefail + +APP_ROLE="florian_app" +APP_PASSWORD="${POSTGRES_APP_PASSWORD:?POSTGRES_APP_PASSWORD muss gesetzt sein}" +# Einfache Anführungszeichen im Passwort für SQL verdoppeln. +APP_PASSWORD_SQL="${APP_PASSWORD//\'/\'\'}" + +echo "[florian-init] Richte Erweiterungen und Anwendungsrolle ein ..." + +# Erweiterungen. Reines SQL, keine Variablen nötig. +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-'EOSQL' + CREATE EXTENSION IF NOT EXISTS postgis; -- Koordinaten, Einsatzorte + CREATE EXTENSION IF NOT EXISTS pgcrypto; -- gen_random_uuid(), digest() + CREATE EXTENSION IF NOT EXISTS citext; -- E-Mail ohne Gross/Klein + CREATE EXTENSION IF NOT EXISTS unaccent; -- Suche ueber Umlaute hinweg + CREATE EXTENSION IF NOT EXISTS pg_trgm; -- unscharfe Namenssuche +EOSQL + +# Anwendungsrolle anlegen. Bewusst OHNE DO-Block: psql ersetzt seine Variablen +# nicht innerhalb dollar-quotierter Zeichenketten. Stattdessen erzeugt die +# Abfrage den CREATE-Befehl als Text, den \gexec dann ausführt — und zwar nur, +# wenn die Rolle noch nicht existiert. +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL + SELECT format( + 'CREATE ROLE %I LOGIN PASSWORD %L NOSUPERUSER NOCREATEDB NOCREATEROLE NOINHERIT NOBYPASSRLS', + '${APP_ROLE}', '${APP_PASSWORD_SQL}' + ) + WHERE NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '${APP_ROLE}') + \gexec + + ALTER ROLE "${APP_ROLE}" PASSWORD '${APP_PASSWORD_SQL}'; + ALTER ROLE "${APP_ROLE}" SET timezone TO 'Europe/Berlin'; +EOSQL + +# Rechtevergabe. Hier funktionieren psql-Variablen, weil kein DO-Block im Spiel +# ist. +psql -v ON_ERROR_STOP=1 \ + --username "$POSTGRES_USER" \ + --dbname "$POSTGRES_DB" \ + -v app_role="$APP_ROLE" \ + -v datenbank="$POSTGRES_DB" \ + -v owner="$POSTGRES_USER" <<-'EOSQL' + GRANT CONNECT ON DATABASE :"datenbank" TO :"app_role"; + GRANT USAGE ON SCHEMA public TO :"app_role"; + + -- Rechte auf alles, was heute schon existiert (bei Erstinstallation leer). + GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO :"app_role"; + GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO :"app_role"; + + -- Und auf alles, was der Eigentuemer kuenftig anlegt (Prisma-Migrationen). + ALTER DEFAULT PRIVILEGES FOR ROLE :"owner" IN SCHEMA public + GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO :"app_role"; + ALTER DEFAULT PRIVILEGES FOR ROLE :"owner" IN SCHEMA public + GRANT USAGE, SELECT ON SEQUENCES TO :"app_role"; + ALTER DEFAULT PRIVILEGES FOR ROLE :"owner" IN SCHEMA public + GRANT EXECUTE ON FUNCTIONS TO :"app_role"; + + -- Die Anwendung darf keine Tabellen anlegen oder loeschen. + REVOKE CREATE ON SCHEMA public FROM PUBLIC; + REVOKE CREATE ON SCHEMA public FROM :"app_role"; + + ALTER DATABASE :"datenbank" SET timezone TO 'Europe/Berlin'; +EOSQL + +echo "[florian-init] Anwendungsrolle '$APP_ROLE' eingerichtet (RLS wirksam)." diff --git a/docs/ANLEITUNG.md b/docs/ANLEITUNG.md new file mode 100644 index 0000000..7d65380 --- /dev/null +++ b/docs/ANLEITUNG.md @@ -0,0 +1,258 @@ +# Anleitung + +Bedienung von FLORIAN, Stand Phase 0 (Version 0.1.0). Die Bilder zeigen die +Beispielwehr aus dem Seed und werden mit `npm run screenshots` neu erzeugt. + +> Was hier noch fehlt — Mitglieder, Geräte, Prüfungen — kommt in den +> folgenden Phasen dazu. Der Stand ist bewusst schmal: Phase 0 baut das +> Fundament, nicht die Fachmodule. Siehe [ROADMAP.md](ROADMAP.md). + +--- + +## 1. Anmelden + +Aufgerufen wird FLORIAN im Browser unter der Adresse, die der Administrator +eingerichtet hat — in der Entwicklung `http://localhost:3100`. + +![Anmeldeseite](screenshots/01-anmeldung-light.png) + +Die Anmeldeseite zeigt genau die Wege, die in dieser Installation eingerichtet +sind: + +| Weg | Wann | +|---|---| +| **Anmeldung über authentik** | Der Regelweg, sobald authentik eingerichtet ist. Ein Klick, keine Passworteingabe in FLORIAN. | +| **E-Mail und Passwort** | Beim Erststart, und wenn authentik gerade nicht erreichbar ist. | +| **Terminal im Gerätehaus** | Am Wandtablet, mit PIN oder Ausweiskarte. Eigene Oberfläche, kommt in Phase 4. | + +Beim allerersten Start gibt es genau ein Konto: das aus `SEED_ADMIN_EMAIL` und +`SEED_ADMIN_PASSWORT` in der `.env`. Vorgabe: + +``` +admin@beispielwehr.de +Feuerwehr2026 +``` + +**Dieses Passwort gehört nach dem ersten Login geändert.** + +### Wenn die Anmeldung nicht klappt + +Nach mehreren Fehlversuchen wird das Konto vorübergehend gesperrt — voreingestellt +nach fünf Versuchen für fünf Minuten (`KIOSK_MAX_FEHLVERSUCHE`, +`KIOSK_SPERRDAUER`). Die Meldung nennt nicht, ob die E-Mail-Adresse oder das +Passwort falsch war; das ist Absicht und verhindert, dass sich vorhandene +Konten erraten lassen. + +--- + +## 2. Die Übersicht + +Nach der Anmeldung steht die Übersicht der eigenen Wehr. + +![Übersicht](screenshots/02-uebersicht-light.png) + +Von oben nach unten: + +**Kopfzeile.** Name der Wehr und darunter, wer angemeldet ist und mit welchen +Rollen. Wer mehrere Rollen trägt — der Gerätewart ist oft auch Ausbilder — +sieht alle. + +**Sicherheitshinweis.** Der gelbe Kasten bleibt stehen, solange die Wehr die +Anwendung nicht ausdrücklich freigegeben hat. Er ist keine Formalität: +Atemschutzüberwachung und Alarmmonitor sind sicherheitsrelevant, und bis zur +Freigabe bleibt die analoge Überwachung führend. + +**Kennzahlen.** Vier Kacheln mit aktiven Mitgliedern, Fahrzeugen, Geräten und +fälligen Prüfungen. Sie zählen nur die eigene Wehr — was eine andere Wehr auf +derselben Installation hat, ist nicht sichtbar und nicht abfragbar. + +**Stand des Aufbaus.** Welche Phasen fertig sind und was als Nächstes kommt. + +### Die Seitenleiste + +Links stehen alle Module. Was noch nicht existiert, ist ausgegraut und trägt +rechts sein Phasenkürzel — `P2` heißt „kommt in Phase 2". + +Was jemand überhaupt sieht, hängt an seinen Rechten. Ein einfaches Mitglied +sieht die Mitgliederliste, aber nicht das Protokoll. Wichtig zum Verständnis: +das Ausblenden ist nur Bequemlichkeit. Der eigentliche Schutz sitzt auf dem +Server — wer eine Adresse direkt aufruft, für die ihm das Recht fehlt, bekommt +trotzdem nichts. + +--- + +## 3. Helle und dunkle Darstellung + +Oben rechts wird zwischen hell und dunkel umgeschaltet. Voreingestellt ist die +Einstellung des Betriebssystems. + +![Übersicht in dunkler Darstellung](screenshots/02-uebersicht-dark.png) + +Die dunkle Darstellung ist bewusst kein invertiertes Schwarz, sondern ein +warmes Grau. Wer abends vom Fahrzeug hereinkommt und ans Terminal tritt, soll +nicht geblendet werden. + +--- + +## 4. Rollen und was sie dürfen + +Neun Rollen, jede mit einem festen Satz Rechte. Die Zuordnung steht zentral in +`src/lib/authz.ts` und wird beim Seed in die Datenbank gespiegelt — eine Wehr +kann sie also nachträglich zuschneiden, ohne dass jemand Code ändert. + +| Rolle | Darf im Kern | +|---|---| +| **Administrator** | alles, auch über Wehren hinweg | +| **Kommandant** | alles innerhalb der eigenen Wehr, einschließlich Rollen vergeben und Protokoll einsehen | +| **Gerätewart** | Fahrzeuge und Geräte anlegen, ändern, aussondern; Prüfungen planen und durchführen | +| **Atemschutzgerätewart** | zusätzlich Atemschutzgeräte, Träger und die Überwachung nach FwDV 7 | +| **Kassier** | Buchungen und Kassenabschluss (Modul noch nicht geplant, siehe Roadmap) | +| **Schriftführer** | Mitglieder pflegen, Einsätze und Berichte erfassen | +| **Ausbilder** | Übungen planen, Teilnahme und Qualifikationen erfassen | +| **Mitglied** | eigene Daten, Listen lesen | +| **Terminal** | nur anmelden und erfassen — nie verwalten | + +Zwei Dinge sind bewusst so gebaut: + +**Der Gerätewart kommt nicht an die Kasse, der Kassier nicht an die Geräte.** +Trennung nach Verantwortung, nicht nach Bequemlichkeit. + +**Das Terminal darf fast nichts.** Wer am Wandtablet arbeitet, meldet sich +zusätzlich mit PIN oder Karte an und handelt dann unter seiner eigenen Person. +Das Terminal selbst ist nur die Tür. + +--- + +## 5. Anmeldung über authentik einrichten + +authentik läuft **getrennt** von FLORIAN und ist nicht Teil dieses Stacks. + +### In authentik + +1. Neuen Provider anlegen, Typ **OAuth2/OpenID Provider** +2. Client-Typ: **Confidential** +3. Redirect-URI genau so eintragen: + ``` + https:///api/auth/callback/authentik + ``` +4. Scopes: `openid`, `profile`, `email` +5. Für die Rollenvorbelegung zusätzlich ein Scope-Mapping, das die Gruppen des + Benutzers als Claim `groups` ausliefert +6. Eine Application anlegen und mit dem Provider verbinden. Der Slug der + Application ist der letzte Teil der Issuer-URL. + +### In der `.env` + +```bash +AUTH_OIDC_ENABLED=true +AUTH_OIDC_ISSUER=https://auth.beispiel.de/application/o/florian/ +AUTH_OIDC_CLIENT_ID= +AUTH_OIDC_CLIENT_SECRET= +AUTH_OIDC_GROUP_CLAIM=groups +AUTH_OIDC_GROUP_MAPPING=feuerwehr-kommandant:KOMMANDANT,feuerwehr-geraetewart:GERAETEWART +AUTH_OIDC_AUTO_PROVISION=true +AUTH_OIDC_STANDARD_ORGANISATION=ff-musterhausen +``` + +Danach die Anwendung neu starten. Auf der Anmeldeseite erscheint der Knopf für +authentik. + +### Wie die Rollenvorbelegung wirkt + +Die Zuordnung greift **nur beim allerersten Login** einer Person. Danach ist +die in FLORIAN gepflegte Rolle führend, und authentik überschreibt sie nicht +mehr. Wer also einem Mitglied in FLORIAN die Rolle „Gerätewart" gibt, behält +sie — auch wenn die authentik-Gruppe etwas anderes sagt. + +Steht `AUTH_OIDC_AUTO_PROVISION=false`, muss die Person vorher in FLORIAN +angelegt sein. Verknüpft wird über die E-Mail-Adresse. + +### Wenn authentik ausfällt + +Solange `AUTH_LOCAL_ENABLED=true` steht, bleibt der Passwort-Login als +Rückfallweg erreichbar. Für den Wirkbetrieb ist es sinnvoll, mindestens ein +Konto mit Passwort zu behalten — sonst sperrt ein Ausfall von authentik die +ganze Wehr aus. + +--- + +## 6. Terminal-Anmeldung im Gerätehaus + +Die Oberfläche dazu kommt in Phase 4; der Anmeldeweg steht bereits. + +Am Terminal wird zweifach geprüft: + +1. **Das Gerät** weist sich mit `KIOSK_DEVICE_SECRET` aus. Ohne diesen Wert + nimmt der Server keine PIN und keine Karte an. +2. **Die Person** meldet sich mit einer sechsstelligen PIN (zusammen mit ihrer + Mitgliedsnummer) oder mit einer RFID-Karte an. + +Beides wird nur als Hash gespeichert — weder die PIN noch die Kartenkennung +liegen im Klartext in der Datenbank. + +PINs, die sich zu leicht raten lassen, weist FLORIAN ab: lauter gleiche +Ziffern und fortlaufende Folgen wie `123456`. + +Aus dem Seed lässt sich das ausprobieren: Mitgliedsnummer `0001` (Andreas +Huber), PIN `471203`. + +--- + +## 7. Was protokolliert wird + +Jede schreibende Änderung erzeugt einen Eintrag mit Zeitpunkt, Person, +betroffenem Datensatz und den geänderten Feldern im Vorher-Nachher-Vergleich. +Auch jede Anmeldung — erfolgreich wie gescheitert. + +Zwei Eigenschaften sind wichtig: + +**Der Log lässt sich nicht ändern.** Nicht über die Oberfläche, nicht über die +Datenbank, auch nicht vom Administrator. Die Datenbank selbst verhindert es. + +**Er lässt sich nur im Aufbewahrungslauf kürzen.** Nach der eingestellten +Frist (`AUDIT_AUFBEWAHRUNG_TAGE`, Vorgabe 10 Jahre) sichert der +Hintergrundprozess die alten Einträge und entfernt sie danach. Das ist der +einzige Weg, auf dem etwas aus dem Log verschwindet. + +**Passwörter, PINs und Kartenkennungen stehen nie im Log** — sie werden vor +dem Schreiben herausgenommen. + +Einsehbar ist der Log mit dem Recht `system.auditlog`, das Kommandant und +Administrator haben. Die Ansicht dazu kommt in Phase 1. + +--- + +## 8. Wenn etwas nicht geht + +| Beobachtung | Wahrscheinliche Ursache | Was hilft | +|---|---|---| +| Die Anwendung startet nicht und meldet fehlende Variablen | `.env` unvollständig | Die Meldung nennt jede fehlende Variable. Erklärungen dazu stehen in `.env.example`. | +| Anmeldung schlägt immer fehl, obwohl das Passwort stimmt | Konto nach Fehlversuchen gesperrt | Fünf Minuten warten, oder `gesperrtBis` in der Datenbank zurücksetzen | +| Listen sind leer, obwohl Daten da sind | Kein Mandantenkontext gesetzt | Das ist der Schutzmechanismus. Der Zugriff muss über `fuerOrganisation()` laufen. | +| Dateien lassen sich nicht hochladen | MinIO nicht erreichbar oder Bucket fehlt | `http://localhost:9011` öffnen; `docker compose logs minio-init` | +| Der Knopf für authentik fehlt | Issuer, Client-ID oder Secret nicht gesetzt | Alle drei sind nötig, sonst wird der Weg ausgeblendet | +| `docker compose up` startet Traefik mit | Override-Datei wurde nicht mitgeladen | In Produktion `-f docker-compose.yml` angeben | + +Zustand aller Randsysteme auf einen Blick: + +``` +http://localhost:3100/api/health +``` + +Antwortet mit `200` und `"status": "ok"`, wenn Datenbank, Objektspeicher und +Warteschlange erreichbar sind — sonst mit `503` und der Angabe, welcher Dienst +fehlt. + +--- + +## Bilder erneuern + +```bash +docker compose up -d +npm run dev +npm run screenshots +``` + +Die Bilder entstehen in `docs/screenshots/`, jeweils in heller und dunkler +Darstellung, und werden mit eingecheckt — die Anleitung soll auch dann lesbar +sein, wenn gerade kein Stack läuft. diff --git a/docs/AUFWANDSSCHAETZUNG.md b/docs/AUFWANDSSCHAETZUNG.md new file mode 100644 index 0000000..aa36ea5 --- /dev/null +++ b/docs/AUFWANDSSCHAETZUNG.md @@ -0,0 +1,237 @@ +# Aufwandsschätzung + +Schätzung je Phase, daneben der tatsächliche Aufwand aus dem +[Devlog](DEVLOG.md). Stand: 3. September 2026. + +## Wie hier gerechnet wird + +Es stehen bewusst **zwei** Größen nebeneinander, weil sie Verschiedenes +aussagen: + +| Größe | Bedeutung | +|---|---| +| **Sitzungszeit** | Wie lange Claude tatsächlich gearbeitet hat. Das ist die Zahl, die im Devlog steht, und die Grundlage für die Planung der nächsten Sitzung. | +| **Vergleichsaufwand** | Was eine einzelne Person mit diesem Stack für dasselbe Ergebnis gebraucht hätte, in Personentagen zu 8 Stunden. Das ist die Zahl, die gegenüber der Wehr etwas bedeutet — sie beantwortet „was wäre das sonst wert gewesen". | + +Der Vergleichsaufwand ist eine **Schätzung**, keine Messung. Er unterstellt +jemanden, der den Stack kennt, die Feuerwehr-Fachlichkeit aber erst klären +muss — also Recherche zu DGUV-Fristen, FwDV 3 und FwDV 7 einschließt. + +Faktoren, die in die Schätzung eingehen: + +- **Fachlichkeit** — wie viel muss geklärt werden, bevor gebaut werden kann? +- **Sicherheitsbezug** — Atemschutz und Prüfnachweise brauchen mehr Tests und + mehr Sorgfalt als eine Adressliste. +- **Fremdsysteme** — Gotenberg, Nominatim, tileserver-gl, Ollama, RFID-Leser. + Jedes ist eine eigene Fehlerquelle. +- **Offline und Echtzeit** — die teuersten Anforderungen im ganzen Projekt. + +## Übersicht + +| Phase | Inhalt | Vergleichsaufwand | Sitzungszeit geschätzt | Sitzungszeit tatsächlich | +|---|---|---:|---:|---:| +| 0 | Fundament | 12–16 PT | — | **1:00 h** | +| 1 | Stammdaten | 10–14 PT | 2,5–3,5 h | offen | +| 2 | Wartung und Prüfung | 18–24 PT | 4,5–6 h | offen | +| 3 | Einsätze und Übungen | 12–16 PT | 3–4 h | offen | +| 4 | Kiosk-PWA mit Offline | 16–22 PT | 4–5,5 h | offen | +| 5 | Atemschutz (FwDV 7) | 14–18 PT | 3,5–4,5 h | offen | +| 6 | Kartenmodule | 14–18 PT | 3,5–4,5 h | offen | +| 7 | Benachrichtigungen und Termine | 10–13 PT | 2,5–3,5 h | offen | +| 8 | Lokale KI | 9–12 PT | 2,5–3 h | offen | +| | **Summe** | **115–153 PT** | **26–34 h** | | + +115–153 Personentage entsprechen etwa **6 bis 8 Monaten Vollzeitarbeit** einer +einzelnen Person. Zum Vergleich: eine kommerzielle Feuerwehrverwaltung kostet +je nach Umfang 500 bis 2.500 Euro im Jahr, dauerhaft, mit den Daten beim +Anbieter. + +--- + +## Phase 0 — Fundament + +**Vergleichsaufwand: 12–16 PT** · **Tatsächlich: 1:00 h Sitzungszeit** + +| Baustein | PT | +|---|---:| +| Monorepo, Werkzeuge, TypeScript, Tailwind, shadcn | 1,5 | +| Docker Compose, Dockerfile, Traefik, Healthchecks | 2,5 | +| Prisma-Schema der Basismodelle | 2,0 | +| Row Level Security samt Rollen- und Rechtekonzept in Postgres | 2,5 | +| Auth.js mit OIDC, lokalem Login und Terminal-Anmeldung | 3,0 | +| storage, audit, warteschlange, Integrationsschnittstellen | 2,0 | +| Basis-Layout, Anmeldeseite, Übersicht, Dark Mode | 1,5 | +| Seed, Vitest, Playwright, Dokumentation | 1,5 | + +Das Teuerste war nicht der sichtbare Teil, sondern die Mandantentrennung. RLS +so aufzusetzen, dass Migrationen, Seed und Anmeldung funktionieren und die +Anwendung trotzdem nichts Fremdes sieht, verlangt zwei getrennte +Datenbankzugänge und ein sauberes Verständnis davon, wann welcher greift. Wer +das nachträglich einbaut, zahlt ein Vielfaches. + +## Phase 1 — Stammdaten + +**Vergleichsaufwand: 10–14 PT** + +| Baustein | PT | +|---|---:| +| Modelle: Standort, Geraeteraum, Geraetetyp, Qualifikation | 1,5 | +| Fahrzeug und Geraet um DIN-Typisierung und Lebensdauer erweitern | 1,5 | +| Tabellen mit Sortierung, Filter, Volltextsuche, Spaltenauswahl | 3,0 | +| QR-Code je Gerät, Etikettenbogen als PDF (65 Etiketten, DIN A4) | 1,5 | +| QR-Scan im Browser über die Kamera | 1,0 | +| CSV-/Excel-Import mit Spaltenzuordnung und Vorschau | 2,5 | +| Gerätehistorie aus dem Audit-Log | 1,0 | +| Massenbearbeitung und Tastaturbedienung | 1,0 | + +Der Import ist der Posten, der erfahrungsgemäß aus dem Ruder läuft. Jede Wehr +bringt eine andere Excel-Datei mit, und die Vorschau vor dem Übernehmen ist +kein Komfort, sondern die Bedingung dafür, dass die Wehr dem Ergebnis traut. + +## Phase 2 — Wartung und Prüfung + +**Vergleichsaufwand: 18–24 PT** — die aufwendigste Phase + +| Baustein | PT | +|---|---:| +| Modelle: Vorlage, Checklistenpunkt, Termin, Prüfung, Ergebnis, Mangel, Rückruf | 3,0 | +| Fristen-Dashboard mit Gruppierungen | 2,5 | +| Nächtlicher BullMQ-Job: Termine erzeugen, Status setzen, erinnern | 2,5 | +| Prüfungs-Wizard mit Fotoupload je Checklistenpunkt | 3,5 | +| Unterschrift auf Canvas, Touch und Maus | 1,5 | +| PDF-Protokoll über Gotenberg mit Fotos und Signatur | 3,0 | +| Unveränderlichkeit und Korrekturprotokolle mit Verweis | 1,5 | +| Lebensdauerüberwachung und Aussonderungsvorschlag | 1,5 | +| Rückrufe, externe Wartung, Mängel-Workflow | 2,5 | + +Hier entsteht der eigentliche Wert: die Software wird zum Nachweis gegenüber +der Unfallkasse. Das heißt aber auch, dass jeder Fehler teuer ist. Die +Unveränderlichkeit abgeschlossener Protokolle ist der Kern — und der Grund, +warum der Audit-Log schon in Phase 0 append-only ist. + +## Phase 3 — Einsätze und Übungen + +**Vergleichsaufwand: 12–16 PT** + +| Baustein | PT | +|---|---:| +| Modelle: Einsatz, Einsatzfahrzeug, Einsatzkraft, Material, Lagemeldung, Übung | 2,0 | +| Schnellerfassung mit Nachtragen | 1,5 | +| Geocoding über self-hosted Nominatim mit Rückfallweg | 1,5 | +| Personalauswahl nach Verfügbarkeit und Qualifikation | 2,0 | +| Stärkeberechnung nach FwDV 3 | 1,0 | +| Einsatzbericht als PDF, Jahresexport für die Gemeinde | 2,0 | +| Statistik: Kategorien, Eintreffzeiten, Beteiligung, Auslastung | 2,5 | +| Externes Portal mit signiertem QR-Link, ohne Login, rate-limited | 2,0 | + +Das externe Portal für Fremdwehren ist sicherheitsrelevant: ein Zugang ohne +Anmeldung, der zeitlich begrenzt und mengenbegrenzt sein muss. Der Aufwand +steckt weniger in der Funktion als in der Absicherung. + +## Phase 4 — Kiosk-PWA + +**Vergleichsaufwand: 16–22 PT** + +| Baustein | PT | +|---|---:| +| Eigenes Kiosk-Layout, 64px-Ziele, hoher Kontrast, kein Hover-Zwang | 2,0 | +| Anmeldung per RFID (WebHID), PIN und QR vom Handy | 2,5 | +| Wizards: Wartung, Fahrtenbuch, Mangel, Inventur, Wäsche, Anwesenheit | 4,0 | +| Hallen-Monitor mit DWD-Wetterwarnung | 2,0 | +| Service Worker, App-Shell und Stammdaten offline | 3,0 | +| IndexedDB-Outbox mit Idempotenzschlüssel und Synchronisation | 4,0 | +| Konfliktbehandlung und Konfliktliste für den Administrator | 2,0 | +| Playwright-Tests für den Offline-Fall | 1,5 | + +Offline-Fähigkeit mit schreibenden Aktionen ist die technisch schwierigste +Anforderung des ganzen Projekts. Eine Outbox ist schnell gebaut; dass sie +unter Verbindungsabbruch, Doppelklick und parallelem Zugriff von zwei +Terminals nichts verliert und nichts doppelt schreibt, ist die eigentliche +Arbeit. Die Idempotenzschlüssel sind dafür der Schlüssel, nicht die Synchronisation. + +## Phase 5 — Atemschutz nach FwDV 7 + +**Vergleichsaufwand: 14–18 PT** + +| Baustein | PT | +|---|---:| +| Modelle: Atemschutzeinsatz, Trupp, Druckmessung, G26, Belastungsübung | 2,0 | +| Truppanlage per RFID, Eingangsdruck | 1,5 | +| Serverseitig autoritative Timer, Socket.IO-Verteilung | 3,5 | +| Rückzugsdruck-Berechnung, Ampel, akustischer Alarm | 2,0 | +| Erinnerung zur Druckabfrage im Intervall | 1,0 | +| Mayday-Knopf mit Protokolleintrag | 1,0 | +| Lückenloses PDF-Protokoll am Einsatz | 1,5 | +| G26.3-Fristen, Belastungsübungen, Tauglichkeitsampel | 2,0 | +| Tests: Reload und Verbindungsabbruch überstehen | 2,0 | + +Der Aufwand liegt nicht in der Funktion, sondern im Beweis, dass sie hält. Ein +Timer, der einen Reload überlebt, ist Pflicht — deshalb muss die Zeit +serverseitig geführt werden und der Browser nur anzeigen. Diese Phase bekommt +den höchsten Testanteil im Projekt. + +## Phase 6 — Kartenmodule + +**Vergleichsaufwand: 14–18 PT** + +| Baustein | PT | +|---|---:| +| MapLibre GL, tileserver-gl, MBTiles fürs Ausrückegebiet | 2,5 | +| Hydranten mit PostGIS, Cluster, Filter, Umkreissuche | 3,0 | +| Import aus GeoJSON und Stadtwerke-CSV | 1,5 | +| Objekte und Feuerwehrpläne nach DIN 14095, Volltextsuche | 3,0 | +| Lagekarte mit taktischen Zeichen nach FwDV 102 als SVG-Set | 3,5 | +| Echtzeit-Sync der Lagekarte zwischen Geräten | 2,0 | +| Wasserförderung: Höhenprofil, Druckverlust, Verstärkerpumpen | 2,5 | + +Das SVG-Set für die taktischen Zeichen ist Fleißarbeit, aber unvermeidbar — +fertige Sets sind selten frei verwendbar. Die Wasserförderungsberechnung ist +Physik mit klaren Formeln und deshalb gut testbar. + +## Phase 7 — Benachrichtigungen und Termine + +**Vergleichsaufwand: 10–13 PT** + +| Baustein | PT | +|---|---:| +| Kanalabstraktion: E-Mail, Telegram, ntfy, Web-Push | 2,5 | +| Vorlagen mit Platzhaltern, je Ereignis und Rolle | 2,0 | +| Abonnements je Person | 1,0 | +| Kalender mit Serienterminen und Selbstanmeldung | 3,0 | +| CalDAV-/ICS-Feed je Person mit Token | 1,5 | +| Digitaler Dienstausweis mit signiertem QR und Prüfseite | 2,0 | + +Das Grundgerüst der Kanalabstraktion steht bereits aus Phase 0 +(`lib/integrations/`), das spart hier etwa einen Personentag. + +## Phase 8 — Lokale KI + +**Vergleichsaufwand: 9–12 PT** + +| Baustein | PT | +|---|---:| +| `lib/ai/` mit OpenAI-kompatiblem Client, abschaltbar | 1,0 | +| Diktat: Aufnahme im Browser, faster-whisper, Transkript | 2,5 | +| Berichtsassistent mit Übernehmen/Verwerfen | 2,0 | +| Wiki mit pgvector, semantische Suche, RAG mit Quellenangabe | 3,5 | +| Belegerfassung über ein Vision-Modell, immer bestätigen | 2,0 | + +Der Client aus Phase 0 (`lib/integrations/llm.ts`) ist bereits vorhanden. Die +Kennzeichnungspflicht — jede KI-Ausgabe ist als solche erkennbar und muss +bestätigt werden, bevor sie in ein Dokument mit Nachweischarakter gelangt — +kostet wenig Zeit, ist aber nicht verhandelbar. + +--- + +## Was hier nicht drin ist + +**Kassenmodul.** Die Rolle `KASSIER` ist in Phase 0 gefordert und angelegt, +die Rechte `kasse.lesen`, `kasse.bearbeiten` und `kasse.abschluss` stehen +bereit. Ein Kassenmodul kommt in keiner der Phasen 0 bis 8 vor. Geschätzter +Vergleichsaufwand, falls gewünscht: **8–11 PT** für Buchungen, Beiträge, +Rechnungen, Kassenabschluss und Jahresauswertung. Siehe +[ROADMAP.md](ROADMAP.md). + +**Betrieb und Einführung.** Datenübernahme aus der Altsoftware, Schulung der +Wehr, Sicherungskonzept, Monitoring: nach Erfahrung noch einmal 15 bis 25 +Prozent des Entwicklungsaufwands. diff --git a/docs/DEVLOG.md b/docs/DEVLOG.md new file mode 100644 index 0000000..fa11e9e --- /dev/null +++ b/docs/DEVLOG.md @@ -0,0 +1,133 @@ +# Devlog + +Entwicklungstagebuch mit Zeiterfassung. Jeder Arbeitsabschnitt bekommt einen +Eintrag: wann, wie lange, was entstanden ist, und was dabei schiefging. + +Die aufgewendete Zeit wandert nach jedem Abschnitt in die Spalte +„tatsächlich" der [Aufwandsschätzung](AUFWANDSSCHAETZUNG.md). + +## Zeitübersicht + +| Phase | Sitzungszeit | Sitzungen | abgeschlossen | +|---|---:|---:|---| +| Phase 0 — Fundament | 1:00 h | 1 | 03.09.2026 | +| **Summe** | **1:00 h** | **1** | | + +--- + +## 2026-09-02 / 03 · Phase 0 — Fundament + +**23:13 – 00:13 · 1:00 h · Version 0.1.0** + +### Was entstanden ist + +46 Quelldateien, rund 6.100 Zeilen. Im Einzelnen: + +| Bereich | Inhalt | +|---|---| +| Betrieb | `docker-compose.yml` (7 Dienste), Entwicklungs-Override, mehrstufiges Dockerfile, Postgres-Initialisierung, `.env.example` mit Kommentar zu jeder Variable | +| Datenmodell | Prisma-Schema mit 11 Tabellen, 3 Migrationen | +| Kern | `env`, `db`, `authz`, `storage`, `audit`, `auth`, `passwort`, `warteschlange`, 3 Integrationsschnittstellen | +| Server | tRPC v11 mit drei Prozedurarten, Kontext, Wurzel-Router | +| Oberfläche | Layout, Seitenleiste, Anmeldeseite, Übersicht, 5 shadcn-Bausteine, Farbwelt in OKLCH | +| Daten | Seed: 1 Wehr, 9 Rollen, 38 Rechte, 16 Personen, 3 Fahrzeuge, 40 Geräte | +| Tests | 32 Vitest-Tests, 10 Playwright-Tests | +| Doku | README, Anleitung, Roadmap, Aufwandsschätzung, Versionierung, Changelog | + +### Entscheidungen + +**Zwei Datenbankverbindungen statt einer.** Der erste Entwurf hatte einen +einzigen Prisma-Client. Der ist beim ersten Anmeldeversuch aufgelaufen: die +Anwendung verbindet sich als `florian_app`, für den Row Level Security greift +— und die Anmeldung muss eine E-Mail-Adresse über alle Wehren hinweg suchen, +bevor überhaupt feststeht, zu welcher Wehr sie gehört. Ohne gesetzten +Mandantenkontext liefert RLS null Zeilen, also fand der Login nie jemanden. + +Die Lösung ist eine saubere Trennung: `prismaApp` für alle Fachdaten (RLS +aktiv), `prismaSystem` für die wenigen Vorgänge, die vor der +Mandantenauflösung stattfinden. Der Name ist bewusst unbequem, damit auffällt, +wenn ihn jemand im falschen Zusammenhang benutzt. + +**Kein FORCE ROW LEVEL SECURITY.** Der Tabelleneigentümer umgeht RLS. Das ist +gewollt: Migrationen und Seed müssen über alle Mandanten hinweg arbeiten +können. Mit `FORCE` wäre der Seed nicht mehr lauffähig gewesen. + +**Audit-Log: append-only mit genau einer Ausnahme.** Der erste Trigger hat +`UPDATE` und `DELETE` ausnahmslos gesperrt. Das ist beim Aufräumen der +Testdaten aufgefallen — eine Organisation ließ sich nicht mehr löschen, weil +die Kaskade auf den Audit-Log traf. + +Das ist kein Testproblem, sondern ein echtes: nach +`AUDIT_AUFBEWAHRUNG_TAGE` muss der Worker alte Einträge exportieren und dann +entfernen, sonst wächst die Tabelle unbegrenzt und das Löschkonzept nach DSGVO +lässt sich nicht umsetzen. Die zweite Migration führt deshalb einen +ausdrücklichen Schalter ein: `SET LOCAL app.audit_bereinigung = 'an'` erlaubt +`DELETE` für die laufende Transaktion. `UPDATE` bleibt unter allen Umständen +gesperrt — ein Eintrag darf verschwinden, nachdem er gesichert wurde, aber +niemals verfälscht werden. + +**Rollen aus authentik nur als Vorbelegung.** Auf Nachfrage entschieden: der +Gruppen-Claim setzt die Rolle beim ersten Login, danach ist die in FLORIAN +gepflegte Rolle führend. Umgesetzt über das Feld `ausOidcGruppe` an +`PersonRolle` und eine Prüfung, die nur bei leerer Rollenliste greift. + +**Terminal mit zwei Schranken.** Erst weist sich das Gerät mit +`KIOSK_DEVICE_SECRET` aus, dann die Person mit PIN oder Karte. Eine erratene +sechsstellige PIN nützt ohne das Gerätegeheimnis nichts. + +### Wo es gehakt hat + +| Problem | Ursache | Lösung | +|---|---|---| +| Postgres startete nicht | `de_DE.UTF-8` gibt es im Debian-Basisimage nicht | ICU-Locale: `--locale-provider=icu --icu-locale=de-DE --locale=C.UTF-8` | +| Init-Skript brach mit Syntaxfehler ab | psql ersetzt seine Variablen nicht innerhalb dollar-quotierter Blöcke | `DO $$`-Block durch `SELECT format(...) \gexec` ersetzt | +| Anmeldung schlug immer fehl | siehe oben: RLS blockierte die E-Mail-Suche | zweite Datenbankverbindung | +| `npm install` ließ Installationsskripte aus | npm 11.17 verlangt eine ausdrückliche Freigabe | `npm approve-scripts` für Prisma, esbuild und drei weitere | +| Aufräumen der Testdaten scheiterte | Audit-Trigger blockierte die Kaskade | Aufbewahrungsschalter (siehe oben) | +| Zwei E2E-Tests rot | Next.js hängt einen eigenen Routen-Ansager mit `role="alert"` in die Seite; `CardTitle` ist ein `div`, keine Überschrift | Fehlermeldung über `data-testid` angesprochen, Anmeldetitel zu einer echten `h1` gemacht | + +### Abweichung von der Vorgabe + +**`Berechtigung` trägt kein `organisationId`.** Die Architekturregel verlangt +es für jede Tabelle. Diese Tabelle ist aber kein Mandantendatum, sondern der +systemweite Katalog dessen, was die Software überhaupt kann — für alle Wehren +identisch. Mandantenbezogen wird es erst über `RolleBerechtigung`, und die +trägt `organisationId`. Die Tabelle ist für alle lesbar und für die +Anwendungsrolle nicht beschreibbar. Die Ausnahme steht im Schema und in der +Migration kommentiert. + +**Phasenzuordnung in der Seitenleiste korrigiert.** Beim Schreiben der Roadmap +ist aufgefallen, dass ich die Module den falschen Phasen zugeordnet hatte +(Atemschutz als Phase 3, Einsätze als Phase 4). Nach `Phase-3.md` bis +`Phase-8.md` gilt: 3 Einsätze, 4 Kiosk, 5 Atemschutz, 6 Karten, 7 +Benachrichtigungen, 8 KI. Seitenleiste, Kommentare in `authz.ts` und die +Übersicht sind entsprechend berichtigt. + +**Kassenmodul kommt in keiner Phase vor.** Die Rolle `KASSIER` ist gefordert +und angelegt, ein Modul dazu gibt es in `Phase-0.md` bis `Phase-8.md` nicht. +Die Rechte stehen im Katalog, es gibt aber keinen Navigationseintrag. Offene +Entscheidung, siehe [ROADMAP.md](ROADMAP.md). + +### Nachweise + +- `npm run typecheck` — ohne Ausgabe +- `npm run test` — 32 Tests grün, davon 8 gegen eine echte Datenbank +- `npm run test:e2e` — 10 Tests grün (Desktop und Tablet) +- `npm run build` — 6 Routen, 103 kB gemeinsames JavaScript +- `docker compose up -d` — Postgres, Redis, MinIO, Gotenberg gesund + +--- + +## Vorlage für neue Einträge + +```markdown +## JJJJ-MM-TT · Phase N — Titel + +**HH:MM – HH:MM · X:XX h · Version 0.N.0** + +### Was entstanden ist +### Entscheidungen +### Wo es gehakt hat +### Abweichung von der Vorgabe +### Nachweise +``` diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md new file mode 100644 index 0000000..fe0c5a6 --- /dev/null +++ b/docs/ROADMAP.md @@ -0,0 +1,209 @@ +# Roadmap + +Was FLORIAN wann kann. Die Phasen kommen aus `Phase-0.md` bis `Phase-8.md` und +werden **einzeln** abgearbeitet — nie zwei in einem Schritt. + +Aufwände je Phase: [AUFWANDSSCHAETZUNG.md](AUFWANDSSCHAETZUNG.md). +Versionszuordnung: [VERSIONIERUNG.md](VERSIONIERUNG.md). + +## Stand + +``` +Phase 0 ████████████████████ fertig 0.1.0 +Phase 1 ░░░░░░░░░░░░░░░░░░░░ offen 0.2.0 +Phase 2 ░░░░░░░░░░░░░░░░░░░░ offen 0.3.0 +Phase 3 ░░░░░░░░░░░░░░░░░░░░ offen 0.4.0 +Phase 4 ░░░░░░░░░░░░░░░░░░░░ offen 0.5.0 +Phase 5 ░░░░░░░░░░░░░░░░░░░░ offen 0.6.0 +Phase 6 ░░░░░░░░░░░░░░░░░░░░ offen 0.7.0 +Phase 7 ░░░░░░░░░░░░░░░░░░░░ offen 0.8.0 +Phase 8 ░░░░░░░░░░░░░░░░░░░░ offen 0.9.0 +Freigabe ░░░░░░░░░░░░░░░░░░░░ offen 1.0.0 +``` + +--- + +## Phase 0 — Fundament · fertig + +Alles, worauf der Rest aufsetzt. + +- Monorepo, Docker Compose mit Traefik, PostgreSQL 17 + PostGIS, Redis, MinIO, + Gotenberg +- Mandantentrennung über Row Level Security, geprüft gegen eine echte Datenbank +- Append-only-Audit-Log mit kontrolliertem Aufbewahrungslauf +- Anmeldung: OIDC gegen authentik, lokaler Passwort-Login, Terminal per + PIN und RFID +- 9 Rollen, 38 Einzelrechte, zentral in `lib/authz.ts` +- Dateiablage in MinIO, Zugriff nur über `lib/storage.ts` +- Basis-Layout mit Seitenleiste und dunkler Darstellung +- Seed mit einer vollständigen Beispielwehr + +## Phase 1 — Stammdaten · als Nächstes + +Die Wehr trägt ein, was sie hat. + +- Standort, Geraeteraum, Geraetetyp, Qualifikationstyp, Qualifikation +- Fahrzeug um Fahrgestellnummer, DIN-Typ und TÜV-Termin erweitern +- Geraet um Lebensdauerkategorie und die Zuordnung + „Standort ODER Fahrzeug + Geräteraum" erweitern +- Tabellen mit Sortierung, Filter, Volltextsuche und Spaltenauswahl +- QR-Code je Gerät, Etikettenbogen als PDF (DIN A4, 65 Etiketten) +- QR-Scan über die Kamera öffnet direkt die Gerätedetailseite +- CSV-/Excel-Import mit Spaltenzuordnung und Vorschau +- Gerätehistorie aus dem Audit-Log +- Qualifikationen mit Ablaufwarnung auf der Übersicht + +**Danach kann die Wehr:** ihren Bestand vollständig führen und jedes Gerät per +QR-Code am Regal wiederfinden. + +## Phase 2 — Wartung und Prüfung + +Das wichtigste Modul. Ab hier ersetzt FLORIAN den Ordner. + +- Wartungsvorlagen mit Checklisten, Intervall und Vorwarnzeit +- Fristen-Dashboard: überfällig / diese Woche / diesen Monat / nächste 90 Tage +- Nächtlicher Job erzeugt Termine, setzt Status, erinnert bei T-30, T-7 und T-0 +- Prüfungs-Wizard mit Foto je Checklistenpunkt, Zeiterfassung und Unterschrift +- PDF-Prüfprotokoll über Gotenberg, unveränderlich in MinIO +- Lebensdauerüberwachung mit Aussonderungsvorschlag +- Rückrufe markieren betroffene Geräte automatisch +- Mängel-Workflow: melden → zugewiesen → in Arbeit → behoben + +**Danach kann die Wehr:** einen DGUV-Nachweis führen, der einer Prüfung durch +die Unfallkasse standhält. + +## Phase 3 — Einsätze und Übungen + +- Einsatz mit allen Zeitstempeln: alarmiert, ausgerückt, eingetroffen, + Einsatzende, eingerückt, einsatzbereit +- Schnellerfassung: Stichwort, Adresse, Fahrzeuge — Rest später +- Geocoding über self-hosted Nominatim +- Personalauswahl mit Vorschlag nach Verfügbarkeit und Qualifikation +- Stärkeberechnung nach FwDV 3 +- Einsatzbericht als PDF, Jahresexport für die Gemeinde +- Statistik: Kategorien, Eintreffzeiten, Beteiligung, Fahrzeugauslastung +- Externes Portal für Fremdwehren über signierten QR-Link, ohne Anmeldung + +**Danach kann die Wehr:** ihre Jahresstatistik auf Knopfdruck erzeugen. + +## Phase 4 — Kiosk-PWA + +Für die Wandtablets in der Fahrzeughalle. + +- Eigenes Layout: große Kacheln, mindestens 64px, mit Handschuhen bedienbar +- Anmeldung per RFID, PIN oder QR vom Handy, Auto-Logout nach 90 Sekunden +- Wizards: Wartung, Fahrtenbuch, Mangel melden, Inventur, Wäsche, Anwesenheit +- Hallen-Monitor: Vollbild mit Terminen, überfälligen Prüfungen, + Fahrzeugstatus, Wetterwarnung +- Offline-Betrieb mit IndexedDB-Outbox und Synchronisation bei Reconnect + +**Danach kann die Wehr:** einen Mangel in unter 30 Sekunden melden, ohne den +Rechner im Büro einzuschalten — auch wenn das Netz gerade weg ist. + +## Phase 5 — Atemschutz nach FwDV 7 + +Sicherheitsrelevant. Jede Ansicht zeigt dauerhaft den Hinweis, dass die +analoge Überwachung führend bleibt — abschaltbar erst über eine Option, die +eine dokumentierte Freigabe durch den Kommandanten voraussetzt. + +- Truppanlage per RFID, Eingangsdruck erfassen +- Serverseitig autoritative Timer, per Socket.IO verteilt +- Rückzugsdruck-Berechnung mit Ampel und akustischem Alarm +- Erinnerung zur Druckabfrage im konfigurierbaren Intervall +- Mayday-Knopf je Trupp +- Lückenloses PDF-Protokoll am Einsatz +- G26.3-Fristen, Belastungsübungen, Tauglichkeitsübersicht + +**Danach kann die Wehr:** die Überwachung digital mitführen — parallel zur +analogen, bis die Wehr ausdrücklich anders entscheidet. + +## Phase 6 — Kartenmodule + +- MapLibre GL mit selbst gehosteten Kacheln fürs Ausrückegebiet +- Hydranten mit Typ, Nennweite, Durchfluss, letzter Prüfung; Import aus + GeoJSON und Stadtwerke-CSV +- Objekte und Feuerwehrpläne nach DIN 14095, vom Einsatz aus erreichbar +- Lagekarte mit taktischen Zeichen nach FwDV 102, Echtzeit-Sync +- Wasserförderung: Druckverlust, Verstärkerpumpen, druckbares Schema + +**Danach kann die Wehr:** auf der Anfahrt sehen, wo der nächste Hydrant liegt +und was ihn hergibt. + +## Phase 7 — Benachrichtigungen und Termine + +- Kanäle: E-Mail, Telegram, ntfy, Web-Push. WhatsApp bewusst nicht. +- Vorlagen mit Platzhaltern, je Ereignis und Rolle konfigurierbar +- Kalender: Dienste, Übungen, Lehrgänge, Arbeitsdienste; Serientermine, + Selbstanmeldung mit Teilnehmerlimit, Anwesenheitsliste zum Ausdrucken +- CalDAV-/ICS-Feed je Person +- Digitaler Dienstausweis mit signiertem QR und Prüfseite + +### Warum kein WhatsApp + +Die WhatsApp Business API kostet je Konversation und verlangt einen +zugelassenen Anbieter dazwischen — laufende Kosten für eine Wehr, die keinen +Etat dafür hat. Der schwerere Grund ist der Datenschutz: die Nummern aller +Mitglieder gingen an Meta, was sich mit einer Anwendung, die bewusst im +Gerätehaus steht, nicht verträgt. Telegram, ntfy und Web-Push decken denselben +Bedarf ab, ntfy sogar vollständig selbst gehostet. + +## Phase 8 — Lokale KI + +Alles gegen Ollama, kein externer Anbieter, komplett abschaltbar. + +- Diktat: Aufnahme im Browser → faster-whisper → Transkript im Berichtsfeld +- Berichtsassistent: aus Stichpunkten einen Entwurf, immer als Vorschlag +- Wiki mit pgvector: semantische Suche mit Quellenangabe +- Belegerfassung: Foto einer Quittung → Vorschlag für Betrag, Datum, Händler + +Jede KI-Ausgabe ist gekennzeichnet und muss bestätigt werden, bevor sie in ein +Dokument mit Nachweischarakter gelangt. + +## 1.0.0 — Freigabe + +Kein neues Modul, sondern eine Entscheidung: die Wehr setzt FLORIAN im +Echtbetrieb ein. Voraussetzungen: + +- Datenübernahme aus der bisherigen Verwaltung abgeschlossen und abgeglichen +- Sicherungskonzept steht und ist einmal wiederhergestellt worden +- Schulung von Kommandant, Gerätewart und Atemschutzgerätewart erfolgt +- Löschkonzept nach DSGVO abgenommen +- Der Hinweis zur analogen Atemschutzüberwachung wird bewusst und + dokumentiert abgeschaltet — oder bewusst stehen gelassen + +--- + +## Offene Punkte + +### Kassenmodul fehlt in der Phasenplanung + +Phase 0 verlangt die Rolle `KASSIER`, und sie ist angelegt — mit den Rechten +`kasse.lesen`, `kasse.bearbeiten` und `kasse.abschluss`. Ein Kassenmodul kommt +in keiner der Phasen 0 bis 8 vor. Die Rolle hätte also dauerhaft Rechte ohne +zugehörige Oberfläche. + +Zwei Wege: + +1. **Phase 9 ergänzen** — Buchungen, Beiträge, Rechnungen, Kassenabschluss, + Jahresauswertung. Geschätzt 8–11 PT. +2. **Rolle belassen, Modul streichen** — der Kassier nutzt weiterhin seine + bisherige Lösung, die Rechte bleiben als Platzhalter stehen. + +Solange das nicht entschieden ist, bleiben die Rechte im Katalog, aber es gibt +keinen Navigationseintrag dafür. + +### Weitere Kandidaten für später + +| Idee | Nutzen | grob | +|---|---|---:| +| Fahrtenbuch als eigenes Modul | derzeit nur als Kiosk-Wizard in Phase 4 vorgesehen | 3–4 PT | +| Kleiderkammer und Wäscheverwaltung | in Phase 4 nur als Wizard, ohne Bestandsführung | 4–6 PT | +| Jugendfeuerwehr mit eigener Abzeichenverwaltung | eigene Fachlichkeit | 5–7 PT | +| Schnittstelle zur Integrierten Leitstelle | Alarmdaten automatisch übernehmen | 6–10 PT, stark abhängig von der ILS | +| Mehrere Wehren je Installation | technisch bereits möglich, Oberfläche fehlt | 4–6 PT | + +Die Mandantenfähigkeit ist der Punkt, der ohne Zusatzaufwand am weitesten +trägt: das Datenmodell und RLS können bereits mehrere Wehren. Was fehlt, ist +eine Oberfläche zum Umschalten und ein Konzept für gemeindeübergreifende +Auswertungen. Für einen Landkreis, der mehrere Wehren betreiben will, wäre das +der nächste sinnvolle Schritt. diff --git a/docs/VERSIONIERUNG.md b/docs/VERSIONIERUNG.md new file mode 100644 index 0000000..db1c3a5 --- /dev/null +++ b/docs/VERSIONIERUNG.md @@ -0,0 +1,117 @@ +# Versionierung + +## Versionsnummern + +FLORIAN folgt [Semantic Versioning](https://semver.org/lang/de/) in der Form +`MAJOR.MINOR.PATCH`. + +Solange die Hauptversion `0` ist, gilt die Software als im Aufbau: es gibt +keine Zusage, dass eine Schnittstelle stabil bleibt. + +| Stelle | wird erhöht, wenn | +|---|---| +| `MAJOR` | Bestehendes bricht — Datenmodell, REST-API unter `/api/v1` oder Konfiguration ändern sich so, dass eine bestehende Installation Handarbeit braucht. Ab `1.0.0`. | +| `MINOR` | Eine Phase ist abgeschlossen oder ein Funktionsbereich kommt hinzu. | +| `PATCH` | Fehlerbehebungen, Textkorrekturen, Abhängigkeits-Updates — alles ohne neue Funktion. | + +### Zuordnung Phase zu Version + +Jede abgeschlossene Phase bekommt eine Minor-Version. Das macht aus der +Versionsnummer eine Aussage über den Funktionsumfang. + +| Version | Phase | +|---|---| +| `0.1.0` | Phase 0 — Fundament | +| `0.2.0` | Phase 1 — Stammdaten | +| `0.3.0` | Phase 2 — Wartung und Prüfung | +| `0.4.0` | Phase 3 — Einsätze und Übungen | +| `0.5.0` | Phase 4 — Kiosk-PWA | +| `0.6.0` | Phase 5 — Atemschutz | +| `0.7.0` | Phase 6 — Kartenmodule | +| `0.8.0` | Phase 7 — Benachrichtigungen und Termine | +| `0.9.0` | Phase 8 — Lokale KI | +| `1.0.0` | Freigabe für den Wirkbetrieb durch die Wehr | + +`1.0.0` ist bewusst an die **Freigabe** gebunden, nicht an Phase 8. Erst wenn +eine Wehr die Software im Echtbetrieb einsetzt, darf die analoge Überwachung +zurücktreten — und erst dann ist eine Zusage über Stabilität ehrlich. + +## Wo die Version steht + +- `package.json` → `version` ist die Wahrheit. +- `CHANGELOG.md` bekommt je Version einen eigenen Abschnitt. +- Git-Tag in der Form `v0.1.0`. + +```bash +npm version minor -m "Version %s — Phase 1 abgeschlossen" +git push --follow-tags +``` + +## Changelog + +Format nach [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), auf +Deutsch. Überschriften je Abschnitt: + +- **Hinzugefügt** — neue Funktionen +- **Geändert** — Verhalten, das anders geworden ist +- **Veraltet** — bleibt vorerst, verschwindet aber +- **Entfernt** — ist weg +- **Behoben** — Fehlerbehebungen +- **Sicherheit** — alles, was Sicherheitsbezug hat + +Einträge beschreiben, was sich für die **Wehr** ändert, nicht welche Datei +angefasst wurde. Also: „Prüfprotokolle lassen sich als PDF ablegen" statt +„PdfService ergänzt". + +Was noch nicht veröffentlicht ist, sammelt sich unter `[Unveröffentlicht]` und +bekommt beim Setzen des Tags die Versionsnummer und das Datum. + +## Branches + +| Branch | Zweck | +|---|---| +| `main` | immer lauffähig. Tests grün, Build durch. | +| `feat/-` | neue Funktion, `` ist die Issue-Nummer | +| `fix/-` | Fehlerbehebung | +| `chore/` | Aufräumen, Abhängigkeiten, Werkzeuge | + +Nie direkt auf `main` committen. Jede Änderung läuft über einen Branch und +einen Pull Request. + +## Commits + +Deutsch, Imperativ, erste Zeile höchstens 72 Zeichen. Die letzte Zeile +verweist auf das Issue: + +``` +Prüfprotokoll als PDF über Gotenberg erzeugen + +Das Protokoll wird nach dem Abschluss unveränderlich in MinIO abgelegt. +Korrekturen erzeugen ein neues Protokoll mit Verweis auf das ursprüngliche. + +Refs #42 +``` + +Der Text des Pull Requests enthält `Closes #42`. + +## Datenbankmigrationen + +- Eine Migration wird nach dem Anwenden **nie** verändert. Ein Fehler wird mit + einer neuen Migration behoben. +- Der Ordnername sagt, worum es geht: `20260902000100_rls_policies`. +- Jede Migration beginnt mit einem Kommentarblock, der erklärt **warum** sie + existiert — nicht nur was sie tut. +- Migrationen, die Daten verlieren können, werden im Changelog unter + **Sicherheit** oder **Geändert** ausdrücklich genannt. + +## Was vor einem Tag erledigt sein muss + +1. `npm run typecheck` ohne Ausgabe +2. `npm run test` grün +3. `npm run test:e2e` grün +4. `npm run build` durch +5. `docker compose -f docker-compose.yml up -d` startet sauber +6. Changelog-Abschnitt geschrieben +7. Devlog-Eintrag mit der aufgewendeten Zeit ergänzt +8. Aufwandsschätzung um den Ist-Wert ergänzt +9. Bildschirmfotos neu erzeugt, falls sich die Oberfläche geändert hat diff --git a/docs/screenshots/01-anmeldung-dark.png b/docs/screenshots/01-anmeldung-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..1d50f72e614895214ee7c0a1a602d31e921ecdf9 GIT binary patch literal 64307 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

_%=HSe<;mZr-2d#q<*z{wKWxuLTO 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

CuPRZ)R)k-M@f-m7tM|pAYxS4-%wu}W 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 Yz>lZ$)5K7v&2RdiLhO0fJx8qW}N^ literal 0 HcmV?d00001 diff --git a/docs/screenshots/02-uebersicht-dark.png b/docs/screenshots/02-uebersicht-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..23f89dd199b7429dabed13af0a0b4cb9b8c7514c GIT binary patch literal 188007 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

s=2048 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

m<4C38 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 A&;S4c literal 0 HcmV?d00001 diff --git a/next.config.ts b/next.config.ts new file mode 100644 index 0000000..7ca3a3b --- /dev/null +++ b/next.config.ts @@ -0,0 +1,27 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + // Standalone-Build für ein schlankes Runtime-Image im Dockerfile. + output: "standalone", + reactStrictMode: true, + // Keine externen CDNs: Bilder kommen ausschließlich aus MinIO bzw. lokal. + images: { remotePatterns: [] }, + serverExternalPackages: ["@prisma/client", "bcryptjs", "minio", "bullmq"], + eslint: { ignoreDuringBuilds: true }, + async headers() { + return [ + { + source: "/:path*", + headers: [ + { key: "X-Content-Type-Options", value: "nosniff" }, + { key: "X-Frame-Options", value: "DENY" }, + { key: "Referrer-Policy", value: "no-referrer" }, + // Keine Telemetrie, keine externen Fonts/CDNs. + { key: "Permissions-Policy", value: "geolocation=(self), camera=(self), microphone=()" }, + ], + }, + ]; + }, +}; + +export default nextConfig; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..631fafb --- /dev/null +++ b/package-lock.json @@ -0,0 +1,12557 @@ +{ + "name": "florian", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "florian", + "version": "0.1.0", + "dependencies": { + "@prisma/client": "^6.16.2", + "@radix-ui/react-avatar": "^1.1.10", + "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-label": "^2.1.7", + "@radix-ui/react-separator": "^1.1.7", + "@radix-ui/react-slot": "^1.2.3", + "@tanstack/react-query": "^5.90.2", + "@trpc/client": "^11.6.0", + "@trpc/react-query": "^11.6.0", + "@trpc/server": "^11.6.0", + "bcryptjs": "^3.0.2", + "bullmq": "^5.61.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "date-fns": "^4.1.0", + "date-fns-tz": "^3.2.0", + "ioredis": "^5.8.1", + "lucide-react": "^0.544.0", + "minio": "^8.0.6", + "next": "^15.5.4", + "next-auth": "^5.0.0-beta.29", + "next-themes": "^0.4.6", + "react": "^19.1.1", + "react-dom": "^19.1.1", + "server-only": "^0.0.1", + "superjson": "^2.2.6", + "tailwind-merge": "^3.3.1", + "zod": "^3.25.76" + }, + "devDependencies": { + "@playwright/test": "^1.55.1", + "@tailwindcss/postcss": "^4.1.13", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.0", + "@types/node": "^22.18.6", + "@types/react": "^19.1.13", + "@types/react-dom": "^19.1.9", + "@vitejs/plugin-react": "^5.0.3", + "dotenv": "^17.4.2", + "esbuild": "^0.25.10", + "eslint": "^9.36.0", + "eslint-config-next": "^15.5.4", + "jsdom": "^27.0.0", + "prisma": "^6.16.2", + "tailwindcss": "^4.1.13", + "tsx": "^4.20.6", + "tw-animate-css": "^1.4.0", + "typescript": "^5.9.2", + "vite-tsconfig-paths": "^5.1.4", + "vitest": "^3.2.4" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@acemir/cssom": { + "version": "0.9.31", + "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.31.tgz", + "integrity": "sha512-ZnR3GSaH+/vJ0YlHau21FjfLYjMpYVIzTD8M8vIEQvIGxeOXyXdzCI140rrCY862p/C/BbzWsjc1dgnM9mkoTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@adobe/css-tools": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.5.0.tgz", + "integrity": "sha512-6OzddxPio9UiWTCemp4N8cYLV2ZN1ncRnV1cVGtve7dhPOtRkleRyx32GQCYSwDYgaHU3USMm84tNsvKzRCa1Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@alloc/quick-lru": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.3.0.tgz", + "integrity": "sha512-U4+70Pc5ZS9osnCBCE5Jha/ciHM+Yp+CNMNC/7HvYbNRk1Ldd+f7qO65W5qfhu/TCv+/ozljlXXe9Nj8419DMA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.2.tgz", + "integrity": "sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^3.0.0", + "@csstools/css-color-parser": "^4.0.1", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0", + "lru-cache": "^11.2.5" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz", + "integrity": "sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.1.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.6" + } + }, + "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@auth/core": { + "version": "0.41.3", + "resolved": "https://registry.npmjs.org/@auth/core/-/core-0.41.3.tgz", + "integrity": "sha512-sJ3JMHHkXMD3aOjopv7mOBTO1Ocw4b0fAEXJBz6k7YHLpYQI6C40jCUPc5fNvUKxXRXNE1/sRISA15UrwWJBTw==", + "license": "ISC", + "dependencies": { + "@panva/hkdf": "^1.2.1", + "jose": "^6.0.6", + "oauth4webapi": "^3.3.0", + "preact": "10.24.3", + "preact-render-to-string": "6.5.11" + }, + "peerDependencies": { + "@simplewebauthn/browser": "^9.0.1", + "@simplewebauthn/server": "^9.0.2", + "nodemailer": "^7.0.7 || ^8.0.5" + }, + "peerDependenciesMeta": { + "@simplewebauthn/browser": { + "optional": true + }, + "@simplewebauthn/server": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.1.tgz", + "integrity": "sha512-gLNsunvwf3mCi5u5o46/Z/JcJMnhbHSaZ69rkgPzNM3J4s8hWwpPUQB6/tt0EDFyCiWzxANlx+2LJwpYj4zS1w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.3.0.tgz", + "integrity": "sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.2.2.tgz", + "integrity": "sha512-3QKjR/vxyjcSXBLgb6lP0S3MGdvwbmqSsvLPbYdVORqPDc8FX1HAJ0Spk38bxaRXgvENTA47tlhhbb5Z2e8hEg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.1.1", + "@csstools/css-calc": "^3.3.0" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.12.tgz", + "integrity": "sha512-3vLQK+dXxhBMR2Wx99PTCifE+vHtW2ndZWyla8yK813ev6oGhyn8Lja8jCyGAWTJ+LEYZK7EVtJxrDj8ztevJw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@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.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "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/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.7.tgz", + "integrity": "sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.3.2", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@exodus/bytes": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", + "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "node_modules/@floating-ui/core": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz", + "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz", + "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.8.0", + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.9.tgz", + "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.8.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz", + "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", + "license": "MIT" + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.4.tgz", + "integrity": "sha512-Uhfl4V4lhP2nbUVF9+hyH1+luj86f1gUFeo8ALYxFoULoU+G87D43BfeMP8XHsk9boxAnCY/bf2EHwhA7MuGsA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.3" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.4.tgz", + "integrity": "sha512-hWniXY3bG5qKpkKrAwPe4y+VTPmf086YQAnkxWh7uA1YrlRouWGa0M0Mxj3ZjnXFkv7/TD1bTy9lGUK26vRvWw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.3" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.4.tgz", + "integrity": "sha512-lIsKw/BU+kjB4eZjxrYrZmwOJYi3Ajrv66iAlBmUPyKc3HpnloevB1g3wxGD9P/5BbQ1brBGl65VRRrCvQDEqA==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.4" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.3.tgz", + "integrity": "sha512-suTBPTDGrI9WodccaDdwZItTSaBYASlBk1NSfElSHrUfzu3szG6lvIF58+WiFvnfzuK8ZBFS5zE00PxqxnRiPg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.3.tgz", + "integrity": "sha512-FVJZ5mITMobmXIz/hPDTw0EintTW5H3WfrxwLqEqjiIihlu+hVRyGrFQ60xl0Lxn7Bt3zdpevPaQi0HEzqz9fw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.3.tgz", + "integrity": "sha512-3rbU4vqXXc3hY/OiXdl52xZvT0F1yEngWfvqudtPJg/KkyiaQw2DRsFrNzpmLvfavbwOq3qXn36GP8obHRULQA==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.3.tgz", + "integrity": "sha512-0DaL0A6Xu6sQSQFwe4iVCrKWU2cCTItnRsYsCdxAMm9NF6twAA9BKnoqy4hqz4+azQ0JHuA26qiUKsf1XJ/v5A==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.3.tgz", + "integrity": "sha512-cdn1OvUBwsXhbC0zSzJnNzf5MZ/mTrobawDvNXBTxe8VtqKAm0sRuEY2Evzovb/w9JMk4TvRxqt1mekSuJz64w==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.3.tgz", + "integrity": "sha512-HjPVx7yKz+0lqdhDlTw1tt90wamBoxhiXpvl1XZpJLiHH4RCJ5yDTqH+VlYPv2fwFs89JFw4c1IexYOcQUi4IQ==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.3.tgz", + "integrity": "sha512-neWLh+3yCNThxnfy3c4BbVBeGgt9aftno+XbT56iK28RgeDs3UOFWviLWlUu0bArYVYJaFDK+RRohbicUNCm8Q==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.3.tgz", + "integrity": "sha512-4vKmvAst9nrowcqquKFAyZJUDolUaIp8uRiN0mWFguJ1IplC9/pitXtlnnlU4aa/eJw3J7i67V+pwUL+wZGdsA==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.3.tgz", + "integrity": "sha512-Y9kQaLMuNoB0bPYOOdcZMaseNrFpPodIWWMrx+CZyydf2xn68j9WYc6sWWRrDwNkzCQjKYfc68L7jKjGlHMibw==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.3.tgz", + "integrity": "sha512-fj8Mv0HHfD1Rr+4I68+3agJynxDWtBFgicTbSOb9Bke6pIwzGcJ+RX/yHjmiEGFMCavY/dxvem7MyNaJF+wDiw==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.4.tgz", + "integrity": "sha512-7OAS8gI0EReKGVN2HssHlM6umJgxF5VI3xN0p9FA91p/YO+ou5hiNghLdZ5BEHztwaaK5+bLKRf8x/o2L2nk9A==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.3" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.4.tgz", + "integrity": "sha512-De4jpEnAU8Hd5oT0j1G3uL4ZvTuipVMn7YC6vPaJhy6/7EwEae0SVAoBrUMYQbkLGDm85taVWwuPc1a44LTzCQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.3" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.4.tgz", + "integrity": "sha512-2oYZJeIl4kCcMGk4ouZVjnkCtFrpQFlNEtJ6GbxzhHQchwH0NH/qEb9ykmOl29dqwMq+JhFdZn+1ak2FKhI9fQ==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.3" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.4.tgz", + "integrity": "sha512-cPbNChoRURAWdebDIHSenxRpgEdy7JkPydSnUxRm9VvKD7m0/xVaR/8Fzlu81pk5nHEvHH87UZUA7cTtwnbJSA==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.3" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.4.tgz", + "integrity": "sha512-RY0JFY8Fd6RonCBtHz+DvadaPkXDSI1AUn6yWL9TipqkZ1vY8w8evqdgyDFnkm4/K1ve1TvZiaePP5oSd4+WVQ==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.3" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.4.tgz", + "integrity": "sha512-9qvvEAuk8k89TfWUoX2htWjbAMX8p+NxCppjpcg5k6xMsjhBQPTsoIh36h9Qde4WRuGpJeYnOjdosDn/cnv+OA==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.3" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.4.tgz", + "integrity": "sha512-KB5jxpfWQTr0nc3xdHtWChdbifHrBGsd2SM62Eyxrl8afikm+f5qGBU75SJIZBT/S1MC8XyacdlXBMSWq6OURA==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.3" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.4.tgz", + "integrity": "sha512-f+eZJZIQNEEd26RPSW+76chwOf1XtA2Y/O+5ocVyLliHkeih3e+jhLVBdNTd2rS3IbNXK8+ug93Vf5ZXtF5Lxg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.3" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.4.tgz", + "integrity": "sha512-zQnl4Kwp7Q6NHsENtU2T/00Zi+w3AQNwz3+UaTyVBy2FpXrzXzGjndpK61onhZjRtRpQXxCTeqw19bVyXOh7jA==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.4.tgz", + "integrity": "sha512-ESfNkywmCfPNyaZjxooddJQiQ+l/nTpGEOGthxiLnIHXC/CmcBixnfwUleX9mCz9ovrUUvKMap/pm8RYbzfwaA==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.4" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.4.tgz", + "integrity": "sha512-iNdlBX9gLVvqe2I3uIJSIKTq6wckP/DYxZtcqxm09x5Gi24DnFBmPAWZmr60ZyYMG0xlzo6goG3670ar+RXvRw==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.4.tgz", + "integrity": "sha512-kqRsbaa5CS6KHlpxnN7WhE6vAAugXyZButpRdvDWetlv6Qv4N9WTcrWzF7tXfB9T7MsoadqdI8hmwLq6UlLvtw==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.4.tgz", + "integrity": "sha512-XtmnYhBcrORsJ4XJngyzr/EWP0hRZLAZRFaApdKuviyqF78+ylxh2y06ZmtULAMOnObJ3ucpN0AcwSWnMowTRg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@ioredis/commands": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.10.0.tgz", + "integrity": "sha512-UmeW7z4LfctwoQ5wkhVzgq8tXkreED2xZGpX+Bg+zA+WJFZCT6c062AfCK/Dfk81xZnnwdhJCUMkitihRaoC2Q==", + "license": "MIT" + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "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/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz", + "integrity": "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.4.tgz", + "integrity": "sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.4.tgz", + "integrity": "sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.4.tgz", + "integrity": "sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.4.tgz", + "integrity": "sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.4.tgz", + "integrity": "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.3.tgz", + "integrity": "sha512-UMduMbqO5s5zF2NkNacMT/yK5Y5QiKvWr2+50bzIIxFDwVJ2h49b+oyjaCGPhJxd2/gC2x39EHv/gHVuu36x2Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.4", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.4" + } + }, + "node_modules/@next/env": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.25.tgz", + "integrity": "sha512-42h1lLr07vl4gawALP1hsgRZjHB1xYa58JfUfHwr0f7jG/zhPakh5GHkADHXOC9ZxUvlQFOPIrp7s6qX4DezPQ==", + "license": "MIT" + }, + "node_modules/@next/eslint-plugin-next": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.25.tgz", + "integrity": "sha512-dAzOqZQCAOgIq5yQpsuMBZcwxK0AtzGqHMQpxNWYLQlvf79rsP3SDwdGPNQC4ySaMSihOQXMO/VXubQ3JWW+3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-glob": "3.3.1" + } + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.25.tgz", + "integrity": "sha512-w+RR0v/QuApnWEjRGm1z6gcObKwGMb5YPA7V3bzBEVSBpMFUXprer0tS27UxjUcEnqbhL7Zuzohej79B6rYmBg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.25.tgz", + "integrity": "sha512-QiGGBUSakt8S1H4Lt9Ehsh6Ja87axiBnQQgysOObvCbI7iUfJnRGntF1P64S4/ijuHFnSB8KLsEddkY3nN26uw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.25.tgz", + "integrity": "sha512-ehLos/66zo0d/mJCU5u96a/VDcr01aaUrX0o/i16UdInxz8qPTCDSxGtjk/Lps1sIr1RJFdiX3hxc0fxJo+cPA==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.25.tgz", + "integrity": "sha512-ZVMrqLiJ7DiChgmbkQwFtdhAnUkSH/4p7tB29QY+giATb0Q/XGHNRSKAb/B8XGDHRUaA67NepOW5W8u3ZRJBAA==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.25.tgz", + "integrity": "sha512-UOewtDGkTMJTiODrEdeLZ50yGb59xCZSriNpXkfPMxRRgwDkGc7i8mLWqV5076wEdb+Ca/XN7MhJyM3CupyNyQ==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.25.tgz", + "integrity": "sha512-UBHwA8AhkCZgtRfU1aJpunuAJe/6gZv6jDESQe4p5MjTb5V0YEeJBWCdNqx15Vj3x+5jmauRfeMJSjfQj9HGFQ==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.25.tgz", + "integrity": "sha512-QcFcPRr16djk5IqK5+e8O80eZfgWzIvVBXfitIq0tQ/uc+eyfdoZ0NmKc0cnbIJyfVwREapKuG97YcxWA9gcpA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.25.tgz", + "integrity": "sha512-zREeykps3ndWr9egJgvJKqVkkDuaw6Zrrg23cYBos0ygydFkAWYU4+PaPVwXzP1eAYQJe53ShSK45iDM529BOg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodable/entities": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-3.0.0.tgz", + "integrity": "sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, + "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/@panva/hkdf": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz", + "integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/@playwright/test": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.62.1.tgz", + "integrity": "sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.62.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@prisma/client": { + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.3.tgz", + "integrity": "sha512-mKq3jQFhjvko5LTJFHGilsuQs+W+T3Gm451NzuTDGQxwCzwXHYnIu2zGkRoW+Exq3Rob7yp2MfzSrdIiZVhrBg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/config": { + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.3.tgz", + "integrity": "sha512-CBPT44BjlQxEt8kiMEauji2WHTDoVBOKl7UlewXmUgBPnr/oPRZC3psci5chJnYmH0ivEIog2OU9PGWoki3DLQ==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "c12": "3.1.0", + "deepmerge-ts": "7.1.5", + "effect": "3.21.0", + "empathic": "2.0.0" + } + }, + "node_modules/@prisma/debug": { + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.3.tgz", + "integrity": "sha512-ljkJ+SgpXNktLG0Q/n4JGYCkKf0f8oYLyjImS2I8e2q2WCfdRRtWER062ZV/ixaNP2M2VKlWXVJiGzZaUgbKZw==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.3.tgz", + "integrity": "sha512-RSYxtlYFl5pJ8ZePgMv0lZ9IzVCOdTPOegrs2qcbAEFrBI1G33h6wyC9kjQvo0DnYEhEVY0X4LsuFHXLKQk88g==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.3", + "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "@prisma/fetch-engine": "6.19.3", + "@prisma/get-platform": "6.19.3" + } + }, + "node_modules/@prisma/engines-version": { + "version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7.tgz", + "integrity": "sha512-03bgb1VD5gvuumNf+7fVGBzfpJPjmqV423l/WxsWk2cNQ42JD0/SsFBPhN6z8iAvdHs07/7ei77SKu7aZfq8bA==", + "devOptional": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.3.tgz", + "integrity": "sha512-tKtl/qco9Nt7LU5iKhpultD8O4vMCZcU2CHjNTnRrL1QvSUr5W/GcyFPjNL87GtRrwBc7ubXXD9xy4EvLvt8JA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.3", + "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", + "@prisma/get-platform": "6.19.3" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.3.tgz", + "integrity": "sha512-xFj1VcJ1N3MKooOQAGO0W5tsd0W2QzIvW7DD7c/8H14Zmp4jseeWAITm+w2LLoLrlhoHdPPh0NMZ8mfL6puoHA==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.19.3" + } + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.7.tgz", + "integrity": "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.15.tgz", + "integrity": "sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.10" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-avatar": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.2.6.tgz", + "integrity": "sha512-4ULOTJ/mqy2hT9GlWa/MFHxHSvH3nJzHnZM1waNsc5Bonv7i70aNenghXmD97S6OJ81ekXONGGt4nT1r0PfEdA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-is-hydrated": "0.1.3", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.15.tgz", + "integrity": "sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz", + "integrity": "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.2.2.tgz", + "integrity": "sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.4.tgz", + "integrity": "sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.19.tgz", + "integrity": "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-effect-event": "0.0.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.24.tgz", + "integrity": "sha512-geq8l2rJkxvkXsT9RMgtUE3P8pITFpTsvYpbySi1IH4fZEABD/Gp85myayFgxk0ktljGMJnCbeFkyTusvSvv7g==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-menu": "2.1.24", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.6.tgz", + "integrity": "sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.16.tgz", + "integrity": "sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.4.tgz", + "integrity": "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.15.tgz", + "integrity": "sha512-o/rdYEwZTTo5tjknnPeyQFU45kUC4i/XyeDPP+HGyi6XqpOP6Zf5Ya5vh/Yfe9Id5JiuWnnAx2XqIeD3UYZt0g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.10" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.24.tgz", + "integrity": "sha512-uW7RVuU6Lp/ZtfeY4b3kL32zccgEWvPv1+cf17ubYzHa9cL8AHokmk36cG/XEiH/smbQvumnieXX9j/e9RqJWA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-callback-ref": "1.1.4", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.3.7.tgz", + "integrity": "sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-rect": "1.1.4", + "@radix-ui/react-use-size": "1.1.4", + "@radix-ui/rect": "1.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.17.tgz", + "integrity": "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.10.tgz", + "integrity": "sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.10.tgz", + "integrity": "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.3.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.19.tgz", + "integrity": "sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-is-hydrated": "0.1.3", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-separator": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.15.tgz", + "integrity": "sha512-jOLO4lssEzWpoDu7G+Ze4VjwMRUBt291pnZD0gmalREZipnTX3wadQo7Fy48GCTfe14/YRN6rw/rOJqrE85Wxw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.10" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.3.3.tgz", + "integrity": "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.4.tgz", + "integrity": "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.6.tgz", + "integrity": "sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-use-effect-event": "0.0.5", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.5.tgz", + "integrity": "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-is-hydrated": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.3.tgz", + "integrity": "sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz", + "integrity": "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.4.tgz", + "integrity": "sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.4.tgz", + "integrity": "sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.3.tgz", + "integrity": "sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==", + "license": "MIT" + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.3", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.3.tgz", + "integrity": "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.1.tgz", + "integrity": "sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.1.tgz", + "integrity": "sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.1.tgz", + "integrity": "sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.1.tgz", + "integrity": "sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.1.tgz", + "integrity": "sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.1.tgz", + "integrity": "sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.1.tgz", + "integrity": "sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.1.tgz", + "integrity": "sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.1.tgz", + "integrity": "sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.1.tgz", + "integrity": "sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.1.tgz", + "integrity": "sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.1.tgz", + "integrity": "sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.1.tgz", + "integrity": "sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.1.tgz", + "integrity": "sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.1.tgz", + "integrity": "sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.1.tgz", + "integrity": "sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.1.tgz", + "integrity": "sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.1.tgz", + "integrity": "sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.1.tgz", + "integrity": "sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.1.tgz", + "integrity": "sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.1.tgz", + "integrity": "sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.1.tgz", + "integrity": "sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.1.tgz", + "integrity": "sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.1.tgz", + "integrity": "sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.1.tgz", + "integrity": "sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "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/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", + "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.24.1", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", + "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", + "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", + "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", + "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", + "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", + "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", + "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", + "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", + "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", + "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", + "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", + "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", + "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/postcss": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.3.3.tgz", + "integrity": "sha512-JTSZZGQi1AyKirbLN3azmjVzef92tcX7h+iSqPdaeStyFpGpDlKvvpxeOE8njhbUanbRwr3z8DyzhICWnMtQeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "postcss": "^8.5.16", + "tailwindcss": "4.3.3" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.102.8", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.102.8.tgz", + "integrity": "sha512-ZNjkJ33CqvPNec/6lZBnHqLc3EVGPZ9ySLhYahU9TcuRFdmwXewuj0c4hwSWcGHqEUwcSrKeZ+oGcvPBqXcQcg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.102.8", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.102.8.tgz", + "integrity": "sha512-TYBea4OuXWD7MhaSHq069TWbFe7rcwWN6kzT7JF0OKi1K6c1gTv2IzD6A6ExJsCMozdkqBWeuIUZmu4KQg0O5A==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.102.8" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/react": { + "version": "16.3.3", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.3.tgz", + "integrity": "sha512-Uo193NgQbPMz6lrrhtRQQFcMC6Re/ELLFbbuVL30WDlZxlpZf9/lMHTAVxPRLw1q1iu9OJmR1c2BLiENRstdBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@trpc/client": { + "version": "11.18.0", + "resolved": "https://registry.npmjs.org/@trpc/client/-/client-11.18.0.tgz", + "integrity": "sha512-wOqeg3Fvl25V1ZisQhUD3K8G60ZJDlSGJNSyeXrLH24xAo5w6GSR2Kzb1cSNY9Y+IQ2YZvYGZstBU+V/ulo/ow==", + "funding": [ + "https://trpc.io/sponsor" + ], + "license": "MIT", + "bin": { + "intent": "bin/intent.js" + }, + "peerDependencies": { + "@trpc/server": "11.18.0", + "typescript": ">=5.7.2" + } + }, + "node_modules/@trpc/react-query": { + "version": "11.18.0", + "resolved": "https://registry.npmjs.org/@trpc/react-query/-/react-query-11.18.0.tgz", + "integrity": "sha512-C1+Wwm2pCeUJucI+bnFpxGYjNuvV+ko1BC1T9tUxBVdrhHRCdn9ubxdevdLSAa49XRJRJiZnSuzl3Ys/yvs1vg==", + "funding": [ + "https://trpc.io/sponsor" + ], + "license": "MIT", + "peerDependencies": { + "@tanstack/react-query": "^5.80.3", + "@trpc/client": "11.18.0", + "@trpc/server": "11.18.0", + "react": ">=18.2.0", + "typescript": ">=5.7.2" + } + }, + "node_modules/@trpc/server": { + "version": "11.18.0", + "resolved": "https://registry.npmjs.org/@trpc/server/-/server-11.18.0.tgz", + "integrity": "sha512-JAvXOuNTxgXjIDfQaOvDq1j66LMNfDJUH1IU7Slfn8EvRv2EkH6ehu3A7zpYhjO0syHHiYg77v2lG2JFJgvw7Q==", + "funding": [ + "https://trpc.io/sponsor" + ], + "license": "MIT", + "bin": { + "intent": "bin/intent.js" + }, + "peerDependencies": { + "typescript": ">=5.7.2" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/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": "22.20.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", + "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.18.tgz", + "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.5.tgz", + "integrity": "sha512-fMPwH9v7r/pp43yUd2/Mbiex5KouJwwR3dzHkhLREUC6764VyDsqxhAxv6OFEYR1RhjOyD1naqba8ECDBe7ZQg==", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.69.0.tgz", + "integrity": "sha512-t5jQTKPIgVW1PE6dR6H6Qz5gm8zjMlX5/2gRaOGd9eO6V7J+tQc6iWKukEe7dY8u9HyYasQ0yfF0/FSSTEO2gA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.69.0", + "@typescript-eslint/type-utils": "8.69.0", + "@typescript-eslint/utils": "8.69.0", + "@typescript-eslint/visitor-keys": "8.69.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.69.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.8.tgz", + "integrity": "sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.69.0.tgz", + "integrity": "sha512-l4b0DhWioGg6Gt2ebGlvfkFMOjRsauxtsnDRwUSRX1qHq3HdTfQHV8wW9zEXeciai6HfeaKOedQn2Zoofx3WBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.69.0", + "@typescript-eslint/types": "8.69.0", + "@typescript-eslint/typescript-estree": "8.69.0", + "@typescript-eslint/visitor-keys": "8.69.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.69.0.tgz", + "integrity": "sha512-yi4obFrHMmnsesWehHbkg9zMA7Jt8cXT+mKM08G999pH1yT6nqgsHx7MYm0uY1wAj8CqiBXYRJ7WAT0QdQHQXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.69.0", + "@typescript-eslint/types": "^8.69.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.69.0.tgz", + "integrity": "sha512-ewfspqWvSxKSOaplqAUNbaSFO0eB6w1EtQ+esfYFRm3614Ty4uNtExkcbgd6nWsXphbqKyf9ZYdbZdv2xEoWEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.69.0", + "@typescript-eslint/visitor-keys": "8.69.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.69.0.tgz", + "integrity": "sha512-xNqK7YTDZsLniQMV/4rpFR8Z5JlqeRvVjuG1YgF/mdPVH84HSD19L8CczMA0qg2RfwEV231GHH3VnToJDo4MfQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.69.0.tgz", + "integrity": "sha512-ZfoJAVg3JZndQEpEl9petVlxau3lRuElc4HRMuAlLCf8to04/iHz692RUSNmXKDjEuJmIL+KZ2/BsOcBc16dsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.69.0", + "@typescript-eslint/typescript-estree": "8.69.0", + "@typescript-eslint/utils": "8.69.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.69.0.tgz", + "integrity": "sha512-K3VrubUPhlo9VDBS6QdI8YB5j7ClpqLRdefcz6PFrhnwicehBweqQ9Evhl4l+FYz0HdDmMqIiSX0aldGRYtDCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.69.0.tgz", + "integrity": "sha512-AdFkgqck3Vudb/kWnxlyafU/4aBhHrbQ9locP2N4psXTy5mOBg0SHJumnLvx7r6g1gV4DKvUFwV2nJZBoqOD8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.69.0", + "@typescript-eslint/tsconfig-utils": "8.69.0", + "@typescript-eslint/types": "8.69.0", + "@typescript-eslint/visitor-keys": "8.69.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.69.0.tgz", + "integrity": "sha512-tUbx60BBqQa31kXF5MCsOOLL5E/WzUuxIn7YpAvq+eaUlqvk8/NXnXMBNAdLCr0icjkzem7iUA5QqWHe/hJ1aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.69.0", + "@typescript-eslint/types": "8.69.0", + "@typescript-eslint/typescript-estree": "8.69.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.69.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.69.0.tgz", + "integrity": "sha512-+rmdgPA+EXkNgKYvHvFfhrs35utXbwaC5PGpDquSXcoXQDKUA5UjV0LmTucG/4JXkM31BTu4TilHtrN8IVBe8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.69.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@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, + "libc": [ + "glibc" + ], + "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, + "libc": [ + "musl" + ], + "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, + "libc": [ + "glibc" + ], + "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, + "libc": [ + "musl" + ], + "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, + "libc": [ + "glibc" + ], + "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, + "libc": [ + "glibc" + ], + "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, + "libc": [ + "musl" + ], + "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, + "libc": [ + "glibc" + ], + "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, + "libc": [ + "glibc" + ], + "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, + "libc": [ + "musl" + ], + "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-wasm32-wasi/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/@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/@vitejs/plugin-react": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.2.0.tgz", + "integrity": "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-rc.3", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@vitest/expect": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.7.tgz", + "integrity": "sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.7.tgz", + "integrity": "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.7", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.7.tgz", + "integrity": "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.7.tgz", + "integrity": "sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.7", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.7.tgz", + "integrity": "sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.7", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.7.tgz", + "integrity": "sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.7.tgz", + "integrity": "sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.7", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "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", + "peer": true, + "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/anynum": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz", + "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "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-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/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.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/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-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": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "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/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.13.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.13.0.tgz", + "integrity": "sha512-UzGt8zg7Ny8djbYMhxl2zuEevVa7r2gJjYY5Lwr1xM7+XU2nd6CkIWFTVcCIbAP63vSz71NaVyyuSk9lHKcy0A==", + "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.11.20", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.20.tgz", + "integrity": "sha512-H0ulySigv6icDJ1F7SjtdCD6PrhTpdYCmP0CactWy1+ekh0AFd0o1Wn5T8b+hnTmdBx19u9yhL6wvCylXMY7zw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bcryptjs": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz", + "integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==", + "license": "BSD-3-Clause", + "bin": { + "bcrypt": "bin/bcrypt" + } + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/block-stream2": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/block-stream2/-/block-stream2-2.1.0.tgz", + "integrity": "sha512-suhjmLI57Ewpmq00qaygS8UgEq2ly2PCItenIyhMqVjo4t4pGzqMvfgJuX8iWTeSDdfSSqS6j38fL4ToNL7Pfg==", + "license": "MIT", + "dependencies": { + "readable-stream": "^3.4.0" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "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/browser-or-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/browser-or-node/-/browser-or-node-2.1.1.tgz", + "integrity": "sha512-8CVjaLJGuSKMVTxJ2DpBl5XnlNDiT4cQFeuCJJrvJmts9YrTZDizTX7PjC2s6W4x+MBGZeEY6dGMrF04/6Hgqg==", + "license": "MIT" + }, + "node_modules/browserslist": { + "version": "4.28.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz", + "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", + "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.11.12", + "caniuse-lite": "^1.0.30001809", + "electron-to-chromium": "^1.5.402", + "node-releases": "^2.0.53", + "update-browserslist-db": "^1.3.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/bullmq": { + "version": "5.81.4", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.81.4.tgz", + "integrity": "sha512-n+WHSzz20KooBGoJyASre6oJNz/p5f1IJRRN2ibD+NWPQaNpNQmDrwYuPO+bsXrQeM1MQzUxXGbdjmyOFKP2xQ==", + "license": "MIT", + "dependencies": { + "cron-parser": "4.9.0", + "ioredis": "5.11.1", + "msgpackr": "2.0.5", + "node-abort-controller": "3.1.1", + "semver": "7.8.5", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=12.22.0" + }, + "peerDependencies": { + "redis": ">=5.0.0" + }, + "peerDependenciesMeta": { + "redis": { + "optional": true + } + } + }, + "node_modules/bullmq/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/c12": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", + "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.3", + "confbox": "^0.2.2", + "defu": "^6.1.4", + "dotenv": "^16.6.1", + "exsolve": "^1.0.7", + "giget": "^2.0.0", + "jiti": "^2.4.2", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^1.0.0", + "pkg-types": "^2.2.0", + "rc9": "^2.1.2" + }, + "peerDependencies": { + "magicast": "^0.3.5" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, + "node_modules/c12/node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "devOptional": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/caniuse-lite": { + "version": "1.0.30001810", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", + "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/citty": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", + "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "consola": "^3.2.3" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "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/cluster-key-slot": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz", + "integrity": "sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10.0" + } + }, + "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/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/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/consola": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", + "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/copy-anything": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.1.0.tgz", + "integrity": "sha512-ufbM3smX/Jbnpk5wcQjzd1MgBpzmqfNETUAyZNrGwU9foRlyHoGzMMBBCRzEhQLBjZfFDE1W2ufPXX2vdWkV8Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/cron-parser": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", + "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", + "deprecated": "v4 is no longer maintained, upgrade to v5", + "license": "MIT", + "dependencies": { + "luxon": "^3.2.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.7.tgz", + "integrity": "sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^4.1.1", + "@csstools/css-syntax-patches-for-csstree": "^1.0.21", + "css-tree": "^3.1.0", + "lru-cache": "^11.2.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cssstyle/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "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==", + "devOptional": true, + "license": "MIT" + }, + "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-urls": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-6.0.1.tgz", + "integrity": "sha512-euIQENZg6x8mj3fO6o9+fOW8MimUI4PpD/fZBhJfeioZVy9TUpM4UY7KjQNVZFlqwJ0UdzRDzkycB997HEq1BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^15.1.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/data-urls/node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/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": "4.4.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.4.0.tgz", + "integrity": "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/date-fns-tz": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-3.2.0.tgz", + "integrity": "sha512-sg8HqoTEulcbbbVXeg84u5UnlsQa8GS5QXMqjjYIhS4abEVVKIUwe0/l/UhrZdKaL/W5eWZNlbTeEIiOXTcsBQ==", + "license": "MIT", + "peerDependencies": { + "date-fns": "^3.0.0 || ^4.0.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/deepmerge-ts": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", + "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", + "devOptional": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=16.0.0" + } + }, + "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/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "license": "Apache-2.0", + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "devOptional": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, + "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/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/dotenv": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "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/effect": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/effect/-/effect-3.21.0.tgz", + "integrity": "sha512-PPN80qRokCd1f015IANNhrwOnLO7GrrMQfk4/lnZRE/8j7UPWrNNjPV0uBrZutI/nHzernbW+J0hdqQysHiSnQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "fast-check": "^3.23.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.420", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.420.tgz", + "integrity": "sha512-2yD6XreGusOfNV+dUcvipJEXc3n/n7fgr7996aszTG+YY5E4mqM4tOq/3uhP129cazL9YHbVWSpc79ePotWtPA==", + "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/empathic": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", + "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.24.5", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", + "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-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-abstract-get": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz", + "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.2", + "is-callable": "^1.2.7", + "object-inspect": "^1.13.4" + }, + "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.4.0", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.4.0.tgz", + "integrity": "sha512-c/A0P0oxkACDc+cKWw8evLXK83oBKgn0qPOqCYT4x9uolpCIJAcYvJC9QYKNDRPsTeGyCrQ326jrvgZWdCdK5Q==", + "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-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "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.4", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.4.tgz", + "integrity": "sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-abstract-get": "^1.0.0", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "is-callable": "^1.2.7", + "is-date-object": "^1.1.0", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", + "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", + "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.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.6", + "@eslint/js": "9.39.5", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-next": { + "version": "15.5.25", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.25.tgz", + "integrity": "sha512-hB1oClZzRO3vMAeMmBMch0FHNJ4irH74LvQWDEMHkJtG/Us9+SAsg1f5Pvcw9jz4tc8wSGnvbmF050BRAAIbBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@next/eslint-plugin-next": "15.5.25", + "@rushstack/eslint-patch": "^1.10.3", + "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "eslint-import-resolver-node": "^0.3.6", + "eslint-import-resolver-typescript": "^3.5.2", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsx-a11y": "^6.10.0", + "eslint-plugin-react": "^7.37.0", + "eslint-plugin-react-hooks": "^5.0.0" + }, + "peerDependencies": { + "eslint": "^7.23.0 || ^8.0.0 || ^9.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.14.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.14.0.tgz", + "integrity": "sha512-W2WCRZ9Dqntd+2u8jJcVMV2PKulc6RdLgUUoh/yQr3uB6lo/ZOeGx11sv60/8S4QFFKNslAlWhr9u0Ef7ZW6Ig==", + "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-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-jsx-a11y/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/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.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "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 || ^9.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/exsolve": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.1.tgz", + "integrity": "sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/fast-check": { + "version": "3.23.2", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", + "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", + "devOptional": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT", + "dependencies": { + "pure-rand": "^6.1.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "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.4" + }, + "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/fast-xml-builder": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.3.1.tgz", + "integrity": "sha512-pIM/1n3ntFXKYrUZwW7QCK0gAW7XY+wzj1YMIV3tLDvPj/V+zTGJK5e3/4WJfwj0qWw2ElNXiTixda/R+3YSug==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.6.2", + "xml-naming": "^0.3.0" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.11.1.tgz", + "integrity": "sha512-TBw6K/fxoQGGjCmZDw9w/ZwP3uDcnTM4YH/g+PFRWr8sbe5idXtxNN6vITh4+1ruCZaho6uBFurElsA7F0zzgw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "@nodable/entities": "^3.0.0", + "fast-xml-builder": "^1.2.0", + "is-unsafe": "^2.0.0", + "path-expression-matcher": "^1.6.2", + "strnum": "^2.4.2", + "xml-naming": "^0.3.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastq": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", + "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/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/filter-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz", + "integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "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/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "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/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-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-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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.3", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.3.tgz", + "integrity": "sha512-++QEw4DIY7WGoukz+/+A/8dGYPT9l9yIadnmSgZ8Rjr3YVSVDipQSO9CdnJo9ePqFqUUqh+wk9uIaoiAwsiPkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/giget": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", + "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "citty": "^0.1.6", + "consola": "^3.4.0", + "defu": "^6.1.4", + "node-fetch-native": "^1.6.6", + "nypm": "^0.6.0", + "pathe": "^2.0.3" + }, + "bin": { + "giget": "dist/cli.mjs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true, + "license": "MIT" + }, + "node_modules/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==", + "dev": true, + "license": "ISC" + }, + "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/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "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/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "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/ioredis": { + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.11.1.tgz", + "integrity": "sha512-ehuGcf94bQXhfagULNXrJdfnWO38v070jxSx/qE87Kjzmu2fU7ro5EFAb+OPituLqgfyuQaym5DlrNydW2sJ9A==", + "license": "MIT", + "dependencies": { + "@ioredis/commands": "1.10.0", + "cluster-key-slot": "1.1.1", + "debug": "4.4.3", + "denque": "2.1.0", + "redis-errors": "1.2.0", + "redis-parser": "3.0.0", + "standard-as-callback": "2.1.0" + }, + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ioredis" + } + }, + "node_modules/ipaddr.js": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.5.0.tgz", + "integrity": "sha512-aq+t5NAc+cS6rZQQVWC2x98CPqGtKKTMDd4Gaodv0wShnItdKg/51djkGJ1hqH+Oy0ivDftCbSLCQob8zso01w==", + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "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-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-bun-module/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/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-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-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-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-unsafe": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-2.0.2.tgz", + "integrity": "sha512-HgbIHPBH0KHHCcjLfGsCvhtPTVxjaAZlXjwdz7/GQC40SjSe4sfQsar8J5VFo8JOSbarkpV0OLG95bbaNd9aAQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "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/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "devOptional": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/jose": { + "version": "6.2.10", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.10.tgz", + "integrity": "sha512-iiW7J9qRFlGxvCOIBDBDxFePQSn7ZMAnrYGhrrOo6siO/MIqwfyilLR27pkfDgUk+raLuzADS8A3S/KLBisc0g==", + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.4.0.tgz", + "integrity": "sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@acemir/cssom": "^0.9.28", + "@asamuzakjp/dom-selector": "^6.7.6", + "@exodus/bytes": "^1.6.0", + "cssstyle": "^5.3.4", + "data-urls": "^6.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "parse5": "^8.0.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.0", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.1.0", + "ws": "^8.18.3", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/lodash.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==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.544.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.544.0.tgz", + "integrity": "sha512-t5tS44bqd825zAW45UQxpG2CvcC4urOwn2TrwSH8u+MjeE+1NnWl6QqeQ/6NdjMqdOygyiT9p3Ev0p1NJykxjw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/luxon": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz", + "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/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/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/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/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/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/minio": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/minio/-/minio-8.0.7.tgz", + "integrity": "sha512-E737MgufW8CeQAsTAtnEMrxZ9scMSf29kkhZoXzDTKj/Jszzo2SfeZUH9wbDQH2Rsq6TCtl/yQL0+XdVKZansQ==", + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.4", + "block-stream2": "^2.1.0", + "browser-or-node": "^2.1.1", + "buffer-crc32": "^1.0.0", + "eventemitter3": "^5.0.1", + "fast-xml-parser": "^5.3.4", + "ipaddr.js": "^2.0.1", + "lodash": "^4.17.21", + "mime-types": "^2.1.35", + "query-string": "^7.1.3", + "stream-json": "^1.8.0", + "through2": "^4.0.2", + "xml2js": "^0.5.0 || ^0.6.2" + }, + "engines": { + "node": "^16 || ^18 || >=20" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/msgpackr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-2.0.5.tgz", + "integrity": "sha512-cef05H/dSYpLpqp3sj/qyZh5vhUYCalnaLO7j1yOmpsR0y/XwLVtK7r5gn+U/F7CTEfMowcGhlUQJDLcLf7jcA==", + "license": "MIT", + "optionalDependencies": { + "msgpackr-extract": "^3.0.4" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.4.tgz", + "integrity": "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4" + } + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "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": "15.5.25", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.25.tgz", + "integrity": "sha512-OMWNulIIqKM2ykvC2qMjIt0IoavB4UB2SCs4iXJ6z6847FvyH8jBmBWcvrF5iuhTu8Przh20Fo/aoszIdqx4PA==", + "license": "MIT", + "dependencies": { + "@next/env": "15.5.25", + "@swc/helpers": "0.5.15", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "15.5.25", + "@next/swc-darwin-x64": "15.5.25", + "@next/swc-linux-arm64-gnu": "15.5.25", + "@next/swc-linux-arm64-musl": "15.5.25", + "@next/swc-linux-x64-gnu": "15.5.25", + "@next/swc-linux-x64-musl": "15.5.25", + "@next/swc-win32-arm64-msvc": "15.5.25", + "@next/swc-win32-x64-msvc": "15.5.25", + "sharp": "^0.34.3 || ^0.35.4" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.51.1", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next-auth": { + "version": "5.0.0-beta.32", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-5.0.0-beta.32.tgz", + "integrity": "sha512-CGlChIEWZ6LltNVxrE5yiySMID+Idpmry47JYA5lLwgD8Sx02a8M65VL0TWVz9nbnOioS/tCW/rP/0+mE7Qp4Q==", + "license": "ISC", + "dependencies": { + "@auth/core": "0.41.3" + }, + "peerDependencies": { + "@simplewebauthn/browser": "^9.0.1", + "@simplewebauthn/server": "^9.0.2", + "next": "^14.0.0-0 || ^15.0.0 || ^16.0.0", + "nodemailer": "^7.0.7 || ^8.0.5", + "react": "^18.2.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@simplewebauthn/browser": { + "optional": true + }, + "@simplewebauthn/server": { + "optional": true + }, + "nodemailer": { + "optional": true + } + } + }, + "node_modules/next-themes": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", + "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, + "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-abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", + "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", + "license": "MIT" + }, + "node_modules/node-exports-info": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/node-exports-info/-/node-exports-info-1.6.2.tgz", + "integrity": "sha512-kXs9Go0cah0qHVV2v389IXQLdLCeE1xfFtjOAF+iobu0OIoG1pje8At2vMHyaPMiPMnG/LWP50twML21eMcAag==", + "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-fetch-native": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", + "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/node-releases": { + "version": "2.0.54", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz", + "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/nypm": { + "version": "0.6.9", + "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.9.tgz", + "integrity": "sha512-zxlE2yvSWZWmHcNdT3+5zV2lrCogeE9YOklHrR3dFjqutq5wO7GFDYLFDRXLsYnJzwvy/im9fYoxePvS0VTW0w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "citty": "^0.2.2", + "pathe": "^2.0.3", + "tinyexec": "^1.2.4" + }, + "bin": { + "nypm": "dist/cli.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/nypm/node_modules/citty": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.2.tgz", + "integrity": "sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/oauth4webapi": { + "version": "3.8.7", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.8.7.tgz", + "integrity": "sha512-4RxcKxXjuItDFZ20RRPf4YTw3kpeXJyCgJFxVzJ068A7PNJ18st2Dg90tlC1LkSDS0GecroagCLHYEIVUhCAkw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/ohash": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.12.tgz", + "integrity": "sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/own-keys": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.2.tgz", + "integrity": "sha512-19YVAg7T+WTrxggPukVq7DjTv6+PJ867TmhCvBsYwmbFCsZd344rq2Ld1p0wo8f8Qrrhgp82c6FJRqdXWtSEhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "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/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-expression-matcher": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz", + "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.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/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "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/pkg-types": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.1.tgz", + "integrity": "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.4", + "exsolve": "^1.0.8", + "pathe": "^2.0.3" + } + }, + "node_modules/playwright": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz", + "integrity": "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.62.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz", + "integrity": "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "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.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "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.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/preact": { + "version": "10.24.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.24.3.tgz", + "integrity": "sha512-Z2dPnBnMUfyQfSQ+GBdsGa16hz35YmLmtTLhM169uW944hYL6xzTYkJjC07j+Wosz733pMWx0fgON3JNw1jJQA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/preact-render-to-string": { + "version": "6.5.11", + "resolved": "https://registry.npmjs.org/preact-render-to-string/-/preact-render-to-string-6.5.11.tgz", + "integrity": "sha512-ubnauqoGczeGISiOh6RjX0/cdaF8v/oDXIjO85XALCQjwQP+SB4RDXXtvZ6yTYSjG+PC1QRP2AhPgCEsM2EvUw==", + "license": "MIT", + "peerDependencies": { + "preact": ">=10" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prisma": { + "version": "6.19.3", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.3.tgz", + "integrity": "sha512-++ZJ0ijLrDJF6hNB4t4uxg2br3fC4H9Yc9tcbjr2fcNFP3rh/SBNrAgjhsqBU4Ght8JPrVofG/ZkXfnSfnYsFg==", + "devOptional": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/config": "6.19.3", + "@prisma/engines": "6.19.3" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/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==", + "dev": true, + "license": "MIT" + }, + "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/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "devOptional": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/query-string": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", + "integrity": "sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==", + "license": "MIT", + "dependencies": { + "decode-uri-component": "^0.2.2", + "filter-obj": "^1.1.0", + "split-on-first": "^1.0.0", + "strict-uri-encode": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/rc9": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", + "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "destr": "^2.0.3" + } + }, + "node_modules/react": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.8", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.8" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/redis-errors": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", + "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/redis-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", + "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", + "license": "MIT", + "dependencies": { + "redis-errors": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "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/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "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/rollup": { + "version": "4.63.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.63.1.tgz", + "integrity": "sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.63.1", + "@rollup/rollup-android-arm64": "4.63.1", + "@rollup/rollup-darwin-arm64": "4.63.1", + "@rollup/rollup-darwin-x64": "4.63.1", + "@rollup/rollup-freebsd-arm64": "4.63.1", + "@rollup/rollup-freebsd-x64": "4.63.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.63.1", + "@rollup/rollup-linux-arm-musleabihf": "4.63.1", + "@rollup/rollup-linux-arm64-gnu": "4.63.1", + "@rollup/rollup-linux-arm64-musl": "4.63.1", + "@rollup/rollup-linux-loong64-gnu": "4.63.1", + "@rollup/rollup-linux-loong64-musl": "4.63.1", + "@rollup/rollup-linux-ppc64-gnu": "4.63.1", + "@rollup/rollup-linux-ppc64-musl": "4.63.1", + "@rollup/rollup-linux-riscv64-gnu": "4.63.1", + "@rollup/rollup-linux-riscv64-musl": "4.63.1", + "@rollup/rollup-linux-s390x-gnu": "4.63.1", + "@rollup/rollup-linux-x64-gnu": "4.63.1", + "@rollup/rollup-linux-x64-musl": "4.63.1", + "@rollup/rollup-openbsd-x64": "4.63.1", + "@rollup/rollup-openharmony-arm64": "4.63.1", + "@rollup/rollup-win32-arm64-msvc": "4.63.1", + "@rollup/rollup-win32-ia32-msvc": "4.63.1", + "@rollup/rollup-win32-x64-gnu": "4.63.1", + "@rollup/rollup-win32-x64-msvc": "4.63.1", + "fsevents": "~2.3.2" + } + }, + "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-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "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/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/sax": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/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/server-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz", + "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==", + "license": "MIT" + }, + "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/sharp": { + "version": "0.35.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.4.tgz", + "integrity": "sha512-n++8XWcj+jCOr2IOl7h8LbKnGBDY4aPbmprMONBNFdn0ImXqpGVv5zliDs0V9HbmbCQLpbuo2ej9rAoOQTvMDA==", + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.4", + "@img/sharp-darwin-x64": "0.35.4", + "@img/sharp-freebsd-wasm32": "0.35.4", + "@img/sharp-libvips-darwin-arm64": "1.3.3", + "@img/sharp-libvips-darwin-x64": "1.3.3", + "@img/sharp-libvips-linux-arm": "1.3.3", + "@img/sharp-libvips-linux-arm64": "1.3.3", + "@img/sharp-libvips-linux-ppc64": "1.3.3", + "@img/sharp-libvips-linux-riscv64": "1.3.3", + "@img/sharp-libvips-linux-s390x": "1.3.3", + "@img/sharp-libvips-linux-x64": "1.3.3", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.3", + "@img/sharp-libvips-linuxmusl-x64": "1.3.3", + "@img/sharp-linux-arm": "0.35.4", + "@img/sharp-linux-arm64": "0.35.4", + "@img/sharp-linux-ppc64": "0.35.4", + "@img/sharp-linux-riscv64": "0.35.4", + "@img/sharp-linux-s390x": "0.35.4", + "@img/sharp-linux-x64": "0.35.4", + "@img/sharp-linuxmusl-arm64": "0.35.4", + "@img/sharp-linuxmusl-x64": "0.35.4", + "@img/sharp-webcontainers-wasm32": "0.35.4", + "@img/sharp-win32-arm64": "0.35.4", + "@img/sharp-win32-ia32": "0.35.4", + "@img/sharp-win32-x64": "0.35.4" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/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/split-on-first": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz", + "integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/standard-as-callback": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", + "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "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/stream-chain": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/stream-chain/-/stream-chain-2.2.5.tgz", + "integrity": "sha512-1TJmBx6aSWqZ4tx7aTpBDXK0/e2hhcNSTV8+CbFJtDjbb+I1mZ8lHit0Grw9GRT+6JbIrrDd8esncgBi8aBXGA==", + "license": "BSD-3-Clause" + }, + "node_modules/stream-json": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/stream-json/-/stream-json-1.9.1.tgz", + "integrity": "sha512-uWkjJ+2Nt/LO9Z/JyKZbMusL8Dkh97uUBTv3AJQ74y07lVahLY4eEFsPsE97pxYBwr8nnjMAIch5eqI0gPShyw==", + "license": "BSD-3-Clause", + "dependencies": { + "stream-chain": "^2.2.5" + } + }, + "node_modules/strict-uri-encode": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz", + "integrity": "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "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.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.1.0.tgz", + "integrity": "sha512-tHNHTxInrYLCga9O9YGxWA3G9/nnzQw8UGAyqGx3Ar1pSTTzIuM4woFSq4SowkXCjJIwq5sIiQvEfRI9tCH1qQ==", + "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-object-atoms": "^1.1.2", + "get-intrinsic": "^1.3.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.4", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.1" + }, + "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-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-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/strnum": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.2.tgz", + "integrity": "sha512-rDG3Ah4TV0k1hWvLSzkZtMmLN9+eS+h3knq4MP6A42Y3Yh5qGNnOUs1jJkoSr8FG5dsL28c7KgkIBzSEykqtuw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "anynum": "^1.0.1" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "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 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/superjson": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz", + "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==", + "license": "MIT", + "dependencies": { + "copy-anything": "^4" + }, + "engines": { + "node": ">=16" + } + }, + "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/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tailwind-merge": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.6.0.tgz", + "integrity": "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", + "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/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.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "7.4.11", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.11.tgz", + "integrity": "sha512-aBiNayCfTQxuIJBm06M+xR14cYaYlDlSXZbgsnKzKNxDKUVq7KFwTjwBSsb7m9Y5xO8WfPnBc63WaYFMTGlvqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.4.11" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.4.11", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.11.tgz", + "integrity": "sha512-CW3WN2rIIE/Of21mulhgnGOwoDyEFNygyIBOONSdyAuSATgMMUCpLeUlB+E8sAwA5xRV9hYPl+kyZ9citHCaKg==", + "dev": true, + "license": "MIT" + }, + "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/tough-cookie": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz", + "integrity": "sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tsconfck": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", + "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", + "deprecated": "unmaintained", + "dev": true, + "license": "MIT", + "bin": { + "tsconfck": "bin/tsconfck.js" + }, + "engines": { + "node": "^18 || >=20" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "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/tsconfig-paths/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/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/tsx": { + "version": "4.23.13", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.13.tgz", + "integrity": "sha512-BL5MGkRln6aDYhb0xbQlEAGw743BaZYWdbWtdJOBriYJboKgUUYCadFp2/FpBBZquBC/ezNBn7wMMPx7FDZUDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tsx/node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsx/node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/tsx/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/tw-animate-css": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz", + "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Wombosvideo" + } + }, + "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/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==", + "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.3.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz", + "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "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==", + "license": "MIT" + }, + "node_modules/vite": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite-tsconfig-paths": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", + "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "globrex": "^0.1.2", + "tsconfck": "^3.0.3" + }, + "peerDependencies": { + "vite": "*" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/vite/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/vite/node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitest": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.7.tgz", + "integrity": "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.7", + "@vitest/mocker": "3.2.7", + "@vitest/pretty-format": "^3.2.7", + "@vitest/runner": "3.2.7", + "@vitest/snapshot": "3.2.7", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.7", + "@vitest/ui": "3.2.7", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitest/node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz", + "integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "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/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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/ws": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xml-naming": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz", + "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "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..c4294ee --- /dev/null +++ b/package.json @@ -0,0 +1,88 @@ +{ + "name": "florian", + "version": "0.1.0", + "private": true, + "description": "FLORIAN — Feuerwehr-Verwaltungsplattform", + "engines": { + "node": ">=22" + }, + "scripts": { + "dev": "next dev -p 3100", + "build": "next build", + "start": "next start -p 3100", + "worker": "tsx src/worker/index.ts", + "worker:dev": "tsx watch src/worker/index.ts", + "lint": "next lint", + "typecheck": "tsc --noEmit", + "test": "vitest run", + "test:watch": "vitest", + "test:e2e": "playwright test", + "db:generate": "prisma generate", + "db:migrate": "prisma migrate deploy", + "db:migrate:dev": "prisma migrate dev", + "db:push": "prisma db push", + "db:seed": "tsx prisma/seed.ts", + "db:studio": "prisma studio", + "screenshots": "tsx scripts/screenshots.ts" + }, + "dependencies": { + "@prisma/client": "^6.16.2", + "@radix-ui/react-avatar": "^1.1.10", + "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-label": "^2.1.7", + "@radix-ui/react-separator": "^1.1.7", + "@radix-ui/react-slot": "^1.2.3", + "@tanstack/react-query": "^5.90.2", + "@trpc/client": "^11.6.0", + "@trpc/react-query": "^11.6.0", + "@trpc/server": "^11.6.0", + "bcryptjs": "^3.0.2", + "bullmq": "^5.61.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "date-fns": "^4.1.0", + "date-fns-tz": "^3.2.0", + "ioredis": "^5.8.1", + "lucide-react": "^0.544.0", + "minio": "^8.0.6", + "next": "^15.5.4", + "next-auth": "^5.0.0-beta.29", + "next-themes": "^0.4.6", + "react": "^19.1.1", + "react-dom": "^19.1.1", + "server-only": "^0.0.1", + "superjson": "^2.2.6", + "tailwind-merge": "^3.3.1", + "zod": "^3.25.76" + }, + "devDependencies": { + "@playwright/test": "^1.55.1", + "@tailwindcss/postcss": "^4.1.13", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.0", + "@types/node": "^22.18.6", + "@types/react": "^19.1.13", + "@types/react-dom": "^19.1.9", + "@vitejs/plugin-react": "^5.0.3", + "dotenv": "^17.4.2", + "esbuild": "^0.25.10", + "eslint": "^9.36.0", + "eslint-config-next": "^15.5.4", + "jsdom": "^27.0.0", + "prisma": "^6.16.2", + "tailwindcss": "^4.1.13", + "tsx": "^4.20.6", + "tw-animate-css": "^1.4.0", + "typescript": "^5.9.2", + "vite-tsconfig-paths": "^5.1.4", + "vitest": "^3.2.4" + }, + "allowScripts": { + "prisma": true, + "@prisma/client": true, + "@prisma/engines": true, + "esbuild": true, + "unrs-resolver": true, + "msgpackr-extract": true + } +} diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..7d9e94a --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,49 @@ +import { defineConfig, devices } from "@playwright/test"; + +/** + * End-to-End-Tests. + * + * Voraussetzung: der Stack läuft (docker compose up -d) und die Datenbank ist + * migriert und geseedet. Der Entwicklungsserver wird bei Bedarf selbst + * gestartet — ein bereits laufender wird wiederverwendet. + */ +const BASIS_URL = process.env.E2E_BASIS_URL ?? "http://localhost:3100"; + +export default defineConfig({ + testDir: "./tests/e2e", + fullyParallel: true, + forbidOnly: Boolean(process.env.CI), + retries: process.env.CI ? 2 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: process.env.CI ? [["list"], ["html", { open: "never" }]] : [["list"]], + timeout: 30_000, + expect: { timeout: 10_000 }, + + use: { + baseURL: BASIS_URL, + trace: "on-first-retry", + screenshot: "only-on-failure", + video: "retain-on-failure", + // Die Oberfläche ist durchgängig deutsch — auch der Browser soll es sein. + locale: "de-DE", + timezoneId: "Europe/Berlin", + }, + + projects: [ + { name: "chromium", use: { ...devices["Desktop Chrome"] } }, + // Am Terminal im Gerätehaus steht ein Touchgerät — deshalb mitgetestet. + // Bewusst mit Chromium statt WebKit: das Terminal läuft auf einem + // Android-Tablet im Kiosk-Browser, nicht auf einem iPad. + { + name: "tablet", + use: { ...devices["Galaxy Tab S4 landscape"], browserName: "chromium" }, + }, + ], + + webServer: { + command: "npm run dev", + url: BASIS_URL, + reuseExistingServer: !process.env.CI, + timeout: 120_000, + }, +}); diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 0000000..61e3684 --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1,7 @@ +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/prisma.config.ts b/prisma.config.ts new file mode 100644 index 0000000..66a0bfd --- /dev/null +++ b/prisma.config.ts @@ -0,0 +1,14 @@ +// Prisma-Konfiguration. Ersetzt den veralteten "prisma"-Block in package.json. +// Anders als früher lädt Prisma die .env hier nicht mehr von selbst — deshalb +// der ausdrückliche dotenv-Aufruf. +import path from "node:path"; +import "dotenv/config"; +import { defineConfig } from "prisma/config"; + +export default defineConfig({ + schema: path.join("prisma", "schema.prisma"), + migrations: { + path: path.join("prisma", "migrations"), + seed: "tsx prisma/seed.ts", + }, +}); diff --git a/prisma/migrations/20260902000000_init/migration.sql b/prisma/migrations/20260902000000_init/migration.sql new file mode 100644 index 0000000..e03a479 --- /dev/null +++ b/prisma/migrations/20260902000000_init/migration.sql @@ -0,0 +1,409 @@ +-- CreateSchema +CREATE SCHEMA IF NOT EXISTS "public"; + +-- CreateExtension +CREATE EXTENSION IF NOT EXISTS "citext"; + +-- CreateExtension +CREATE EXTENSION IF NOT EXISTS "pg_trgm"; + +-- CreateExtension +CREATE EXTENSION IF NOT EXISTS "pgcrypto"; + +-- CreateExtension +CREATE EXTENSION IF NOT EXISTS "postgis"; + +-- CreateExtension +CREATE EXTENSION IF NOT EXISTS "unaccent"; + +-- CreateEnum +CREATE TYPE "RolleSchluessel" AS ENUM ('SUPERADMIN', 'KOMMANDANT', 'GERAETEWART', 'ATEMSCHUTZGERAETEWART', 'KASSIER', 'SCHRIFTFUEHRER', 'AUSBILDER', 'MITGLIED', 'KIOSK'); + +-- CreateEnum +CREATE TYPE "AusweisTokenTyp" AS ENUM ('PIN', 'RFID'); + +-- CreateEnum +CREATE TYPE "AnmeldeArt" AS ENUM ('LOKAL', 'OIDC', 'KIOSK_PIN', 'KIOSK_RFID', 'SYSTEM'); + +-- CreateEnum +CREATE TYPE "AuditAktion" AS ENUM ('ERSTELLT', 'GEAENDERT', 'GELOESCHT', 'WIEDERHERGESTELLT', 'ANGEMELDET', 'ABGEMELDET', 'ANMELDUNG_FEHLGESCHLAGEN', 'EXPORTIERT', 'DATEI_HOCHGELADEN', 'DATEI_ABGERUFEN', 'BERECHTIGUNG_GEAENDERT'); + +-- CreateEnum +CREATE TYPE "AkteurTyp" AS ENUM ('PERSON', 'SYSTEM', 'WORKER', 'KIOSK', 'API'); + +-- CreateEnum +CREATE TYPE "GeraetStatus" AS ENUM ('EINSATZBEREIT', 'PRUEFUNG_FAELLIG', 'GESPERRT', 'AUSGESONDERT', 'IN_REPARATUR'); + +-- CreateEnum +CREATE TYPE "FahrzeugArt" AS ENUM ('LOESCHFAHRZEUG', 'DREHLEITER', 'RUESTWAGEN', 'GERAETEWAGEN', 'MANNSCHAFTSTRANSPORT', 'EINSATZLEITWAGEN', 'WECHSELLADER', 'ANHAENGER', 'SONSTIGES'); + +-- CreateEnum +CREATE TYPE "Dienstgrad" AS ENUM ('FEUERWEHRANWAERTER', 'FEUERWEHRMANN', 'OBERFEUERWEHRMANN', 'HAUPTFEUERWEHRMANN', 'LOESCHMEISTER', 'OBERLOESCHMEISTER', 'HAUPTLOESCHMEISTER', 'BRANDMEISTER', 'OBERBRANDMEISTER', 'HAUPTBRANDMEISTER', 'KEIN'); + +-- CreateTable +CREATE TABLE "organisation" ( + "id" UUID NOT NULL, + "slug" VARCHAR(64) NOT NULL, + "name" VARCHAR(200) NOT NULL, + "kurzname" VARCHAR(60) NOT NULL, + "strasse" VARCHAR(200), + "plz" VARCHAR(10), + "ort" VARCHAR(120), + "landkreis" VARCHAR(120), + "bundesland" VARCHAR(60) NOT NULL DEFAULT 'Bayern', + "telefon" VARCHAR(40), + "email" VARCHAR(200), + "ilsKennung" VARCHAR(40), + "funkPraefix" VARCHAR(80), + "zeitzone" VARCHAR(60) NOT NULL DEFAULT 'Europe/Berlin', + "aktiv" BOOLEAN NOT NULL DEFAULT true, + "erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "geaendertAm" TIMESTAMPTZ(3) NOT NULL, + "geloeschtAm" TIMESTAMPTZ(3), + + CONSTRAINT "organisation_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "person" ( + "id" UUID NOT NULL, + "organisationId" UUID NOT NULL, + "vorname" VARCHAR(100) NOT NULL, + "nachname" VARCHAR(100) NOT NULL, + "mitgliedsnummer" VARCHAR(30), + "geburtsdatum" DATE, + "dienstgrad" "Dienstgrad" NOT NULL DEFAULT 'KEIN', + "email" VARCHAR(200), + "telefon" VARCHAR(40), + "mobil" VARCHAR(40), + "strasse" VARCHAR(200), + "plz" VARCHAR(10), + "ort" VARCHAR(120), + "eintrittsdatum" DATE, + "austrittsdatum" DATE, + "abteilung" VARCHAR(60), + "passwortHash" VARCHAR(120), + "passwortGeaendertAm" TIMESTAMPTZ(3), + "oidcSubject" VARCHAR(255), + "oidcGruppen" TEXT[] DEFAULT ARRAY[]::TEXT[], + "letzteAnmeldung" TIMESTAMPTZ(3), + "fehlversuche" INTEGER NOT NULL DEFAULT 0, + "gesperrtBis" TIMESTAMPTZ(3), + "aktiv" BOOLEAN NOT NULL DEFAULT true, + "avatarDateiId" UUID, + "erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "geaendertAm" TIMESTAMPTZ(3) NOT NULL, + "geloeschtAm" TIMESTAMPTZ(3), + + CONSTRAINT "person_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "rolle" ( + "id" UUID NOT NULL, + "organisationId" UUID NOT NULL, + "schluessel" "RolleSchluessel" NOT NULL, + "name" VARCHAR(100) NOT NULL, + "beschreibung" VARCHAR(500), + "systemRolle" BOOLEAN NOT NULL DEFAULT false, + "erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "geaendertAm" TIMESTAMPTZ(3) NOT NULL, + + CONSTRAINT "rolle_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "person_rolle" ( + "id" UUID NOT NULL, + "organisationId" UUID NOT NULL, + "personId" UUID NOT NULL, + "rolleId" UUID NOT NULL, + "zugewiesenAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "zugewiesenVonId" UUID, + "gueltigBis" TIMESTAMPTZ(3), + "ausOidcGruppe" BOOLEAN NOT NULL DEFAULT false, + + CONSTRAINT "person_rolle_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "berechtigung" ( + "id" UUID NOT NULL, + "schluessel" VARCHAR(80) NOT NULL, + "bereich" VARCHAR(60) NOT NULL, + "bezeichnung" VARCHAR(160) NOT NULL, + "beschreibung" VARCHAR(500), + "erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "berechtigung_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "rolle_berechtigung" ( + "id" UUID NOT NULL, + "organisationId" UUID NOT NULL, + "rolleId" UUID NOT NULL, + "berechtigungId" UUID NOT NULL, + "gewaehrt" BOOLEAN NOT NULL DEFAULT true, + "erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + + CONSTRAINT "rolle_berechtigung_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "ausweis_token" ( + "id" UUID NOT NULL, + "organisationId" UUID NOT NULL, + "personId" UUID NOT NULL, + "typ" "AusweisTokenTyp" NOT NULL, + "tokenHash" VARCHAR(120) NOT NULL, + "kennzeichen" VARCHAR(20), + "bezeichnung" VARCHAR(120), + "aktiv" BOOLEAN NOT NULL DEFAULT true, + "gueltigAb" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "gueltigBis" TIMESTAMPTZ(3), + "letzteNutzung" TIMESTAMPTZ(3), + "fehlversuche" INTEGER NOT NULL DEFAULT 0, + "gesperrtBis" TIMESTAMPTZ(3), + "erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "geaendertAm" TIMESTAMPTZ(3) NOT NULL, + "geloeschtAm" TIMESTAMPTZ(3), + + CONSTRAINT "ausweis_token_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "audit_log" ( + "id" UUID NOT NULL, + "organisationId" UUID NOT NULL, + "zeitpunkt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "akteurTyp" "AkteurTyp" NOT NULL DEFAULT 'PERSON', + "akteurPersonId" UUID, + "akteurName" VARCHAR(200), + "anmeldeArt" "AnmeldeArt", + "aktion" "AuditAktion" NOT NULL, + "entitaet" VARCHAR(80) NOT NULL, + "entitaetId" UUID, + "beschreibung" VARCHAR(500), + "vorher" JSONB, + "nachher" JSONB, + "kontext" JSONB, + "ipAdresse" INET, + "userAgent" VARCHAR(400), + + CONSTRAINT "audit_log_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "datei" ( + "id" UUID NOT NULL, + "organisationId" UUID NOT NULL, + "bucket" VARCHAR(80) NOT NULL, + "objektSchluessel" VARCHAR(500) NOT NULL, + "dateiname" VARCHAR(300) NOT NULL, + "mimeTyp" VARCHAR(160) NOT NULL, + "groesseBytes" BIGINT NOT NULL, + "pruefsumme" VARCHAR(64), + "entitaet" VARCHAR(80), + "entitaetId" UUID, + "kategorie" VARCHAR(60), + "hochgeladenVonId" UUID, + "erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "geaendertAm" TIMESTAMPTZ(3) NOT NULL, + "geloeschtAm" TIMESTAMPTZ(3), + + CONSTRAINT "datei_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "fahrzeug" ( + "id" UUID NOT NULL, + "organisationId" UUID NOT NULL, + "funkrufname" VARCHAR(120) NOT NULL, + "bezeichnung" VARCHAR(120) NOT NULL, + "art" "FahrzeugArt" NOT NULL DEFAULT 'LOESCHFAHRZEUG', + "kennzeichen" VARCHAR(20), + "hersteller" VARCHAR(120), + "baujahr" INTEGER, + "tuevBis" DATE, + "aktiv" BOOLEAN NOT NULL DEFAULT true, + "erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "geaendertAm" TIMESTAMPTZ(3) NOT NULL, + "geloeschtAm" TIMESTAMPTZ(3), + + CONSTRAINT "fahrzeug_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "geraet" ( + "id" UUID NOT NULL, + "organisationId" UUID NOT NULL, + "inventarnummer" VARCHAR(60) NOT NULL, + "bezeichnung" VARCHAR(160) NOT NULL, + "hersteller" VARCHAR(120), + "seriennummer" VARCHAR(120), + "herstellungsdatum" DATE, + "indienststellung" DATE, + "status" "GeraetStatus" NOT NULL DEFAULT 'EINSATZBEREIT', + "pruefintervallMonate" INTEGER, + "naechstePruefung" DATE, + "lebensdauerJahre" INTEGER, + "fahrzeugId" UUID, + "lagerort" VARCHAR(160), + "erstelltAm" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "geaendertAm" TIMESTAMPTZ(3) NOT NULL, + "geloeschtAm" TIMESTAMPTZ(3), + + CONSTRAINT "geraet_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "organisation_slug_key" ON "organisation"("slug"); + +-- CreateIndex +CREATE INDEX "organisation_aktiv_idx" ON "organisation"("aktiv"); + +-- CreateIndex +CREATE INDEX "person_organisationId_nachname_vorname_idx" ON "person"("organisationId", "nachname", "vorname"); + +-- CreateIndex +CREATE INDEX "person_organisationId_aktiv_idx" ON "person"("organisationId", "aktiv"); + +-- CreateIndex +CREATE INDEX "person_oidcSubject_idx" ON "person"("oidcSubject"); + +-- CreateIndex +CREATE UNIQUE INDEX "person_organisationId_email_key" ON "person"("organisationId", "email"); + +-- CreateIndex +CREATE UNIQUE INDEX "person_organisationId_oidcSubject_key" ON "person"("organisationId", "oidcSubject"); + +-- CreateIndex +CREATE UNIQUE INDEX "person_organisationId_mitgliedsnummer_key" ON "person"("organisationId", "mitgliedsnummer"); + +-- CreateIndex +CREATE INDEX "rolle_organisationId_idx" ON "rolle"("organisationId"); + +-- CreateIndex +CREATE UNIQUE INDEX "rolle_organisationId_schluessel_key" ON "rolle"("organisationId", "schluessel"); + +-- CreateIndex +CREATE INDEX "person_rolle_organisationId_personId_idx" ON "person_rolle"("organisationId", "personId"); + +-- CreateIndex +CREATE INDEX "person_rolle_organisationId_rolleId_idx" ON "person_rolle"("organisationId", "rolleId"); + +-- CreateIndex +CREATE UNIQUE INDEX "person_rolle_personId_rolleId_key" ON "person_rolle"("personId", "rolleId"); + +-- CreateIndex +CREATE UNIQUE INDEX "berechtigung_schluessel_key" ON "berechtigung"("schluessel"); + +-- CreateIndex +CREATE INDEX "berechtigung_bereich_idx" ON "berechtigung"("bereich"); + +-- CreateIndex +CREATE INDEX "rolle_berechtigung_organisationId_rolleId_idx" ON "rolle_berechtigung"("organisationId", "rolleId"); + +-- CreateIndex +CREATE UNIQUE INDEX "rolle_berechtigung_rolleId_berechtigungId_key" ON "rolle_berechtigung"("rolleId", "berechtigungId"); + +-- CreateIndex +CREATE INDEX "ausweis_token_organisationId_personId_idx" ON "ausweis_token"("organisationId", "personId"); + +-- CreateIndex +CREATE INDEX "ausweis_token_organisationId_typ_aktiv_idx" ON "ausweis_token"("organisationId", "typ", "aktiv"); + +-- CreateIndex +CREATE INDEX "audit_log_organisationId_zeitpunkt_idx" ON "audit_log"("organisationId", "zeitpunkt" DESC); + +-- CreateIndex +CREATE INDEX "audit_log_organisationId_entitaet_entitaetId_idx" ON "audit_log"("organisationId", "entitaet", "entitaetId"); + +-- CreateIndex +CREATE INDEX "audit_log_organisationId_akteurPersonId_idx" ON "audit_log"("organisationId", "akteurPersonId"); + +-- CreateIndex +CREATE INDEX "datei_organisationId_entitaet_entitaetId_idx" ON "datei"("organisationId", "entitaet", "entitaetId"); + +-- CreateIndex +CREATE INDEX "datei_organisationId_kategorie_idx" ON "datei"("organisationId", "kategorie"); + +-- CreateIndex +CREATE UNIQUE INDEX "datei_bucket_objektSchluessel_key" ON "datei"("bucket", "objektSchluessel"); + +-- CreateIndex +CREATE INDEX "fahrzeug_organisationId_aktiv_idx" ON "fahrzeug"("organisationId", "aktiv"); + +-- CreateIndex +CREATE UNIQUE INDEX "fahrzeug_organisationId_funkrufname_key" ON "fahrzeug"("organisationId", "funkrufname"); + +-- CreateIndex +CREATE INDEX "geraet_organisationId_status_idx" ON "geraet"("organisationId", "status"); + +-- CreateIndex +CREATE INDEX "geraet_organisationId_naechstePruefung_idx" ON "geraet"("organisationId", "naechstePruefung"); + +-- CreateIndex +CREATE INDEX "geraet_organisationId_fahrzeugId_idx" ON "geraet"("organisationId", "fahrzeugId"); + +-- CreateIndex +CREATE UNIQUE INDEX "geraet_organisationId_inventarnummer_key" ON "geraet"("organisationId", "inventarnummer"); + +-- AddForeignKey +ALTER TABLE "person" ADD CONSTRAINT "person_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "person" ADD CONSTRAINT "person_avatarDateiId_fkey" FOREIGN KEY ("avatarDateiId") REFERENCES "datei"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "rolle" ADD CONSTRAINT "rolle_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "person_rolle" ADD CONSTRAINT "person_rolle_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "person_rolle" ADD CONSTRAINT "person_rolle_personId_fkey" FOREIGN KEY ("personId") REFERENCES "person"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "person_rolle" ADD CONSTRAINT "person_rolle_rolleId_fkey" FOREIGN KEY ("rolleId") REFERENCES "rolle"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "person_rolle" ADD CONSTRAINT "person_rolle_zugewiesenVonId_fkey" FOREIGN KEY ("zugewiesenVonId") REFERENCES "person"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "rolle_berechtigung" ADD CONSTRAINT "rolle_berechtigung_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "rolle_berechtigung" ADD CONSTRAINT "rolle_berechtigung_rolleId_fkey" FOREIGN KEY ("rolleId") REFERENCES "rolle"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "rolle_berechtigung" ADD CONSTRAINT "rolle_berechtigung_berechtigungId_fkey" FOREIGN KEY ("berechtigungId") REFERENCES "berechtigung"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ausweis_token" ADD CONSTRAINT "ausweis_token_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "ausweis_token" ADD CONSTRAINT "ausweis_token_personId_fkey" FOREIGN KEY ("personId") REFERENCES "person"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "audit_log" ADD CONSTRAINT "audit_log_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "audit_log" ADD CONSTRAINT "audit_log_akteurPersonId_fkey" FOREIGN KEY ("akteurPersonId") REFERENCES "person"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "datei" ADD CONSTRAINT "datei_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "datei" ADD CONSTRAINT "datei_hochgeladenVonId_fkey" FOREIGN KEY ("hochgeladenVonId") REFERENCES "person"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "fahrzeug" ADD CONSTRAINT "fahrzeug_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "geraet" ADD CONSTRAINT "geraet_organisationId_fkey" FOREIGN KEY ("organisationId") REFERENCES "organisation"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "geraet" ADD CONSTRAINT "geraet_fahrzeugId_fkey" FOREIGN KEY ("fahrzeugId") REFERENCES "fahrzeug"("id") ON DELETE SET NULL ON UPDATE CASCADE; + diff --git a/prisma/migrations/20260902000100_rls_policies/migration.sql b/prisma/migrations/20260902000100_rls_policies/migration.sql new file mode 100644 index 0000000..aa148fb --- /dev/null +++ b/prisma/migrations/20260902000100_rls_policies/migration.sql @@ -0,0 +1,156 @@ +-- ============================================================================= +-- Row Level Security: Mandantentrennung auf Datenbankebene. +-- +-- Warum auf Datenbankebene und nicht nur im Code? +-- Weil ein vergessener WHERE-Filter sonst Daten einer fremden Wehr ausliefert. +-- Mit RLS ist der schlimmste Fall ein leeres Ergebnis, nicht ein Datenleck. +-- +-- Funktionsweise: +-- Die Anwendung verbindet sich als "florian_app" (NOBYPASSRLS) und setzt zu +-- Beginn jeder Transaktion +-- SET LOCAL app.current_organisation = ''; +-- Siehe src/lib/db.ts. Ohne gesetzten Wert liefert jede Abfrage null Zeilen. +-- +-- Der Eigentümer der Tabellen (POSTGRES_USER) umgeht RLS. Das ist gewollt: +-- Migrationen und das Seed-Skript müssen über alle Mandanten hinweg +-- arbeiten. Deshalb steht hier bewusst kein FORCE ROW LEVEL SECURITY. +-- ============================================================================= + +-- ----------------------------------------------------------------------------- +-- Hilfsfunktionen +-- ----------------------------------------------------------------------------- + +-- Liest die aktive Organisation aus der Sitzung. Ist nichts gesetzt oder steht +-- dort kein gültiges UUID, kommt NULL zurück — und NULL vergleicht sich mit +-- nichts, die Abfrage liefert also keine Zeilen. +CREATE OR REPLACE FUNCTION aktuelle_organisation() +RETURNS uuid +LANGUAGE plpgsql +STABLE +AS $$ +DECLARE + wert text; +BEGIN + wert := current_setting('app.current_organisation', true); + IF wert IS NULL OR wert = '' THEN + RETURN NULL; + END IF; + RETURN wert::uuid; +EXCEPTION + WHEN invalid_text_representation THEN + RETURN NULL; +END; +$$; + +COMMENT ON FUNCTION aktuelle_organisation() IS + 'Aktive Organisation der Sitzung, gesetzt per SET LOCAL app.current_organisation.'; + +-- Verhindert Änderungen am Audit-Log — auch durch den Tabelleneigentümer. +CREATE OR REPLACE FUNCTION audit_log_unveraenderlich() +RETURNS trigger +LANGUAGE plpgsql +AS $$ +BEGIN + RAISE EXCEPTION + 'Der Audit-Log ist unveraenderlich. % ist auf audit_log nicht zulaessig.', + TG_OP + USING ERRCODE = 'insufficient_privilege'; +END; +$$; + +-- ----------------------------------------------------------------------------- +-- Mandantenpflichtige Tabellen +-- ----------------------------------------------------------------------------- + +-- Die Organisation selbst: hier ist die id die Mandantenspalte. +ALTER TABLE "organisation" ENABLE ROW LEVEL SECURITY; + +CREATE POLICY organisation_mandant ON "organisation" + FOR ALL + USING ("id" = aktuelle_organisation()) + WITH CHECK ("id" = aktuelle_organisation()); + +-- Alle übrigen Tabellen hängen über organisationId am Mandanten. +DO $$ +DECLARE + tabelle text; +BEGIN + FOREACH tabelle IN ARRAY ARRAY[ + 'person', + 'rolle', + 'person_rolle', + 'rolle_berechtigung', + 'ausweis_token', + 'datei', + 'fahrzeug', + 'geraet' + ] + LOOP + EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', tabelle); + EXECUTE format( + 'CREATE POLICY %I ON %I FOR ALL ' + 'USING ("organisationId" = aktuelle_organisation()) ' + 'WITH CHECK ("organisationId" = aktuelle_organisation())', + tabelle || '_mandant', tabelle + ); + END LOOP; +END; +$$; + +-- ----------------------------------------------------------------------------- +-- Audit-Log: mandantengetrennt UND append-only +-- Bewusst nur SELECT und INSERT — es gibt keine Policy für UPDATE und DELETE, +-- die Anwendungsrolle kann also gar nicht ändern oder löschen. +-- ----------------------------------------------------------------------------- +ALTER TABLE "audit_log" ENABLE ROW LEVEL SECURITY; + +CREATE POLICY audit_log_lesen ON "audit_log" + FOR SELECT + USING ("organisationId" = aktuelle_organisation()); + +CREATE POLICY audit_log_schreiben ON "audit_log" + FOR INSERT + WITH CHECK ("organisationId" = aktuelle_organisation()); + +CREATE TRIGGER audit_log_kein_update + BEFORE UPDATE ON "audit_log" + FOR EACH ROW EXECUTE FUNCTION audit_log_unveraenderlich(); + +CREATE TRIGGER audit_log_kein_delete + BEFORE DELETE ON "audit_log" + FOR EACH ROW EXECUTE FUNCTION audit_log_unveraenderlich(); + +COMMENT ON TABLE "audit_log" IS + 'Append-only. Aenderungen und Loeschungen sind per Trigger gesperrt. ' + 'Die Aufbewahrungsfrist raeumt der Worker ab, nachdem er exportiert hat.'; + +-- ----------------------------------------------------------------------------- +-- Berechtigungskatalog: systemweit, kein Mandantenbezug. +-- Er beschreibt, was die Software kann — nicht, was eine Wehr besitzt. +-- Deshalb kein RLS, aber nur Lesezugriff für die Anwendung. +-- ----------------------------------------------------------------------------- +COMMENT ON TABLE "berechtigung" IS + 'Systemweiter Katalog der Einzelrechte. Bewusst ohne organisationId: ' + 'mandantenbezogen wird es erst ueber rolle_berechtigung.'; + +-- ----------------------------------------------------------------------------- +-- Rechte für die Anwendungsrolle. Existiert die Rolle nicht (z. B. bei einer +-- Entwicklungsdatenbank ohne den Init-Container), wird der Block übersprungen. +-- ----------------------------------------------------------------------------- +DO $$ +BEGIN + IF EXISTS (SELECT FROM pg_roles WHERE rolname = 'florian_app') THEN + EXECUTE 'GRANT USAGE ON SCHEMA public TO florian_app'; + EXECUTE 'GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO florian_app'; + EXECUTE 'GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO florian_app'; + EXECUTE 'GRANT EXECUTE ON FUNCTION aktuelle_organisation() TO florian_app'; + + -- Der Katalog ist für die Anwendung nur lesbar. + EXECUTE 'REVOKE INSERT, UPDATE, DELETE ON "berechtigung" FROM florian_app'; + -- Der Audit-Log ist für die Anwendung nur schreib- und lesbar. + EXECUTE 'REVOKE UPDATE, DELETE ON "audit_log" FROM florian_app'; + ELSE + RAISE NOTICE 'Rolle florian_app nicht vorhanden — Rechtevergabe uebersprungen.'; + END IF; +END; +$$; diff --git a/prisma/migrations/20260902000200_audit_bereinigung/migration.sql b/prisma/migrations/20260902000200_audit_bereinigung/migration.sql new file mode 100644 index 0000000..aa26e9c --- /dev/null +++ b/prisma/migrations/20260902000200_audit_bereinigung/migration.sql @@ -0,0 +1,60 @@ +-- ============================================================================= +-- Kontrollierte Ausnahme vom Änderungsverbot des Audit-Logs. +-- +-- Ausgangslage: Der Trigger aus 20260902000100_rls_policies verbietet UPDATE +-- und DELETE auf audit_log. Das ist richtig — nachträglich verändern soll den +-- Log niemand. +-- +-- Es gibt aber genau einen Vorgang, der löschen können muss: der +-- Aufbewahrungsjob des Workers. Nach AUDIT_AUFBEWAHRUNG_TAGE exportiert er die +-- alten Einträge nach MinIO und räumt sie danach ab. Ohne diese Möglichkeit +-- wächst die Tabelle unbegrenzt, und das Löschkonzept nach DSGVO ließe sich +-- nicht umsetzen. +-- +-- Der Ausweg ist ein ausdrücklicher Schalter, den nur der Eigentümer-Zugang +-- setzen kann und der nur für die laufende Transaktion gilt: +-- +-- BEGIN; +-- SELECT set_config('app.audit_bereinigung', 'an', true); +-- DELETE FROM audit_log WHERE zeitpunkt < now() - interval '10 years'; +-- COMMIT; +-- +-- UPDATE bleibt unter allen Umständen verboten. Ein Eintrag kann verschwinden, +-- nachdem er gesichert wurde — verfälscht werden kann er nie. +-- ============================================================================= + +CREATE OR REPLACE FUNCTION audit_log_unveraenderlich() +RETURNS trigger +LANGUAGE plpgsql +AS $$ +BEGIN + -- Änderungen sind ausnahmslos gesperrt. + IF TG_OP = 'UPDATE' THEN + RAISE EXCEPTION + 'Der Audit-Log ist unveraenderlich. UPDATE ist auf audit_log nicht zulaessig.' + USING ERRCODE = 'insufficient_privilege'; + END IF; + + -- Löschen nur im ausdrücklich gekennzeichneten Aufbewahrungslauf. + IF TG_OP = 'DELETE' THEN + IF coalesce(current_setting('app.audit_bereinigung', true), '') = 'an' THEN + RETURN OLD; + END IF; + + RAISE EXCEPTION + 'Der Audit-Log ist unveraenderlich. DELETE ist nur im Aufbewahrungslauf zulaessig ' + '(SET LOCAL app.audit_bereinigung = ''an'').' + USING ERRCODE = 'insufficient_privilege'; + END IF; + + RETURN NULL; +END; +$$; + +COMMENT ON FUNCTION audit_log_unveraenderlich() IS + 'Sperrt UPDATE auf audit_log dauerhaft und DELETE ausserhalb des Aufbewahrungslaufs.'; + +-- Die Anwendungsrolle darf den Schalter gar nicht erst setzen: sie hat kein +-- DELETE-Recht auf audit_log (siehe vorherige Migration). Der Schalter wirkt +-- also nur fuer den Eigentuemer-Zugang, den ausschliesslich Worker und +-- Migrationen benutzen. diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 0000000..7b0a624 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,528 @@ +// ============================================================================= +// FLORIAN — Datenmodell, Phase 0 (Fundament) +// +// Grundregeln, die für JEDES Modell gelten: +// 1. Mandantentrennung: jede Tabelle trägt organisationId. Postgres Row Level +// Security setzt das durch (siehe migrations/.../rls). Ausnahme ist der +// systemweite Berechtigungskatalog, siehe Kommentar dort. +// 2. Jede schreibende Operation erzeugt einen AuditLog-Eintrag (lib/audit.ts). +// 3. Prüf-, Wartungs- und Buchungsdaten werden nie hart gelöscht: +// geloeschtAm setzen statt DELETE. +// 4. Alle Zeitstempel in UTC speichern, Anzeige in Europe/Berlin. +// +// Bezeichner sind englisch, wo es Technik ist, und deutsch, wo es Fachlichkeit +// ist — so wie es in der Wehr auch gesprochen wird. +// ============================================================================= + +generator client { + provider = "prisma-client-js" + binaryTargets = ["native", "linux-musl-openssl-3.0.x", "debian-openssl-3.0.x"] + previewFeatures = ["postgresqlExtensions"] +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + directUrl = env("DIRECT_DATABASE_URL") + extensions = [postgis, pgcrypto, citext, unaccent, pg_trgm(map: "pg_trgm")] +} + +// ============================================================================= +// Aufzählungen +// ============================================================================= + +/// Rollen im Sinne der Feuerwehrorganisation. Die Berechtigungen dahinter +/// stehen in lib/authz.ts und werden beim Seed in die Datenbank gespiegelt. +enum RolleSchluessel { + SUPERADMIN + KOMMANDANT + GERAETEWART + ATEMSCHUTZGERAETEWART + KASSIER + SCHRIFTFUEHRER + AUSBILDER + MITGLIED + KIOSK +} + +/// Art des Ausweises, mit dem sich jemand am Terminal im Gerätehaus anmeldet. +enum AusweisTokenTyp { + PIN + RFID +} + +/// Wie sich jemand angemeldet hat — landet im Audit-Log. +enum AnmeldeArt { + LOKAL + OIDC + KIOSK_PIN + KIOSK_RFID + SYSTEM +} + +/// Aktionen im Audit-Log. +enum AuditAktion { + ERSTELLT + GEAENDERT + GELOESCHT + WIEDERHERGESTELLT + ANGEMELDET + ABGEMELDET + ANMELDUNG_FEHLGESCHLAGEN + EXPORTIERT + DATEI_HOCHGELADEN + DATEI_ABGERUFEN + BERECHTIGUNG_GEAENDERT +} + +/// Wer die Aktion ausgelöst hat. +enum AkteurTyp { + PERSON + SYSTEM + WORKER + KIOSK + API +} + +/// Betriebsstatus eines Geräts nach DGUV-Logik. +enum GeraetStatus { + EINSATZBEREIT + PRUEFUNG_FAELLIG + GESPERRT + AUSGESONDERT + IN_REPARATUR +} + +/// Grobe Fahrzeugkategorie. Die vollständige DIN-Typisierung kommt in Phase 1. +enum FahrzeugArt { + LOESCHFAHRZEUG + DREHLEITER + RUESTWAGEN + GERAETEWAGEN + MANNSCHAFTSTRANSPORT + EINSATZLEITWAGEN + WECHSELLADER + ANHAENGER + SONSTIGES +} + +/// Dienstgrad nach bayerischer Feuerwehr-Dienstgradverordnung (Auswahl). +enum Dienstgrad { + FEUERWEHRANWAERTER + FEUERWEHRMANN + OBERFEUERWEHRMANN + HAUPTFEUERWEHRMANN + LOESCHMEISTER + OBERLOESCHMEISTER + HAUPTLOESCHMEISTER + BRANDMEISTER + OBERBRANDMEISTER + HAUPTBRANDMEISTER + KEIN +} + +// ============================================================================= +// Mandant +// ============================================================================= + +/// Eine Feuerwehr. Oberste Klammer — an ihr hängt alles andere. +model Organisation { + id String @id @default(uuid()) @db.Uuid + /// Kurzform für URLs und Konfiguration, z. B. "ff-musterhausen". + slug String @unique @db.VarChar(64) + name String @db.VarChar(200) + kurzname String @db.VarChar(60) + + strasse String? @db.VarChar(200) + plz String? @db.VarChar(10) + ort String? @db.VarChar(120) + landkreis String? @db.VarChar(120) + bundesland String @default("Bayern") @db.VarChar(60) + telefon String? @db.VarChar(40) + email String? @db.VarChar(200) + /// Kennung im Einsatzleitsystem der Integrierten Leitstelle. + ilsKennung String? @db.VarChar(40) + /// Funkrufname-Präfix der Wehr, z. B. "Florian Musterhausen". + funkPraefix String? @db.VarChar(80) + + zeitzone String @default("Europe/Berlin") @db.VarChar(60) + aktiv Boolean @default(true) + + erstelltAm DateTime @default(now()) @db.Timestamptz(3) + geaendertAm DateTime @updatedAt @db.Timestamptz(3) + geloeschtAm DateTime? @db.Timestamptz(3) + + personen Person[] + rollen Rolle[] + personRollen PersonRolle[] + rolleRechte RolleBerechtigung[] + ausweisTokens AusweisToken[] + auditEintraege AuditLog[] + dateien Datei[] + fahrzeuge Fahrzeug[] + geraete Geraet[] + + @@index([aktiv]) + @@map("organisation") +} + +// ============================================================================= +// Personen und Rechte +// ============================================================================= + +/// Ein Mitglied der Wehr. Gleichzeitig der Anmeldebenutzer — es gibt bewusst +/// keine getrennte "User"-Tabelle, damit Dienstgrad, Qualifikation und Zugang +/// nicht auseinanderlaufen. +model Person { + id String @id @default(uuid()) @db.Uuid + organisationId String @db.Uuid + + vorname String @db.VarChar(100) + nachname String @db.VarChar(100) + /// Fortlaufende Mitgliedsnummer innerhalb der Wehr. + mitgliedsnummer String? @db.VarChar(30) + geburtsdatum DateTime? @db.Date + dienstgrad Dienstgrad @default(KEIN) + + email String? @db.VarChar(200) + telefon String? @db.VarChar(40) + mobil String? @db.VarChar(40) + + strasse String? @db.VarChar(200) + plz String? @db.VarChar(10) + ort String? @db.VarChar(120) + + eintrittsdatum DateTime? @db.Date + austrittsdatum DateTime? @db.Date + /// Aktiver Dienst, passiv, Jugendfeuerwehr, Ehrenmitglied. + abteilung String? @db.VarChar(60) + + /// bcrypt-Hash für den lokalen Login. Null, wenn nur OIDC genutzt wird. + passwortHash String? @db.VarChar(120) + passwortGeaendertAm DateTime? @db.Timestamptz(3) + /// "sub"-Claim aus authentik. Verknüpft die Person mit dem OIDC-Konto. + oidcSubject String? @db.VarChar(255) + /// Gruppen aus dem letzten OIDC-Login — nur zur Nachvollziehbarkeit. + oidcGruppen String[] @default([]) + + letzteAnmeldung DateTime? @db.Timestamptz(3) + fehlversuche Int @default(0) + gesperrtBis DateTime? @db.Timestamptz(3) + aktiv Boolean @default(true) + + /// Profilbild. Liegt wie alles andere in MinIO. + avatarDateiId String? @db.Uuid + + erstelltAm DateTime @default(now()) @db.Timestamptz(3) + geaendertAm DateTime @updatedAt @db.Timestamptz(3) + geloeschtAm DateTime? @db.Timestamptz(3) + + organisation Organisation @relation(fields: [organisationId], references: [id], onDelete: Cascade) + avatar Datei? @relation("PersonAvatar", fields: [avatarDateiId], references: [id], onDelete: SetNull) + rollen PersonRolle[] @relation("PersonRollenInhaber") + vergebeneRollen PersonRolle[] @relation("PersonRollenVergeber") + ausweisTokens AusweisToken[] + auditEintraege AuditLog[] + hochgeladeneDateien Datei[] @relation("DateiHochgeladenVon") + + /// E-Mail und OIDC-Subject sind je Wehr eindeutig, nicht global — dieselbe + /// Person kann in zwei Wehren Mitglied sein. + @@unique([organisationId, email]) + @@unique([organisationId, oidcSubject]) + @@unique([organisationId, mitgliedsnummer]) + @@index([organisationId, nachname, vorname]) + @@index([organisationId, aktiv]) + @@index([oidcSubject]) + @@map("person") +} + +/// Eine Rolle innerhalb einer Wehr. Die Systemrollen legt der Seed an; eigene +/// Rollen kann der Kommandant ergänzen. +model Rolle { + id String @id @default(uuid()) @db.Uuid + organisationId String @db.Uuid + + schluessel RolleSchluessel + name String @db.VarChar(100) + beschreibung String? @db.VarChar(500) + /// Systemrollen dürfen nicht gelöscht werden. + systemRolle Boolean @default(false) + + erstelltAm DateTime @default(now()) @db.Timestamptz(3) + geaendertAm DateTime @updatedAt @db.Timestamptz(3) + + organisation Organisation @relation(fields: [organisationId], references: [id], onDelete: Cascade) + personen PersonRolle[] + rechte RolleBerechtigung[] + + @@unique([organisationId, schluessel]) + @@index([organisationId]) + @@map("rolle") +} + +/// Zuweisung Person zu Rolle. Eine Person kann mehrere Rollen tragen — der +/// Gerätewart ist meist auch aktives Mitglied. +model PersonRolle { + id String @id @default(uuid()) @db.Uuid + organisationId String @db.Uuid + personId String @db.Uuid + rolleId String @db.Uuid + + zugewiesenAm DateTime @default(now()) @db.Timestamptz(3) + zugewiesenVonId String? @db.Uuid + /// Befristete Zuweisung, z. B. Vertretung während Urlaub. + gueltigBis DateTime? @db.Timestamptz(3) + /// true, wenn die Rolle aus einer authentik-Gruppe vorbelegt wurde. Sobald + /// jemand sie in FLORIAN ändert, steht hier false und OIDC überschreibt nicht + /// mehr. + ausOidcGruppe Boolean @default(false) + + organisation Organisation @relation(fields: [organisationId], references: [id], onDelete: Cascade) + person Person @relation("PersonRollenInhaber", fields: [personId], references: [id], onDelete: Cascade) + rolle Rolle @relation(fields: [rolleId], references: [id], onDelete: Cascade) + zugewiesenVon Person? @relation("PersonRollenVergeber", fields: [zugewiesenVonId], references: [id], onDelete: SetNull) + + @@unique([personId, rolleId]) + @@index([organisationId, personId]) + @@index([organisationId, rolleId]) + @@map("person_rolle") +} + +/// Systemweiter Katalog der Einzelrechte, z. B. "geraet.bearbeiten". +/// +/// BEWUSSTE AUSNAHME von der organisationId-Regel: das hier sind keine +/// Mandantendaten, sondern eine Liste dessen, was die Software überhaupt kann. +/// Der Katalog wird vom Seed aus lib/authz.ts befüllt und ist für alle Wehren +/// identisch. Mandantenbezogen wird es erst durch RolleBerechtigung, und die +/// trägt organisationId. Die Tabelle ist deshalb für alle lesbar und für die +/// Anwendungsrolle nicht beschreibbar. +model Berechtigung { + id String @id @default(uuid()) @db.Uuid + schluessel String @unique @db.VarChar(80) + /// Fachbereich für die Gruppierung in der Oberfläche. + bereich String @db.VarChar(60) + bezeichnung String @db.VarChar(160) + beschreibung String? @db.VarChar(500) + + erstelltAm DateTime @default(now()) @db.Timestamptz(3) + + rollen RolleBerechtigung[] + + @@index([bereich]) + @@map("berechtigung") +} + +/// Welche Rolle welches Recht hat — je Wehr getrennt, damit eine Wehr dem +/// Gerätewart mehr erlauben kann als eine andere. +model RolleBerechtigung { + id String @id @default(uuid()) @db.Uuid + organisationId String @db.Uuid + rolleId String @db.Uuid + berechtigungId String @db.Uuid + + /// false setzt ein Recht gezielt ab, das die Rolle sonst hätte. + gewaehrt Boolean @default(true) + + erstelltAm DateTime @default(now()) @db.Timestamptz(3) + + organisation Organisation @relation(fields: [organisationId], references: [id], onDelete: Cascade) + rolle Rolle @relation(fields: [rolleId], references: [id], onDelete: Cascade) + berechtigung Berechtigung @relation(fields: [berechtigungId], references: [id], onDelete: Cascade) + + @@unique([rolleId, berechtigungId]) + @@index([organisationId, rolleId]) + @@map("rolle_berechtigung") +} + +/// Anmeldemittel für das Terminal im Gerätehaus: PIN oder RFID-Karte. +/// Gespeichert wird immer nur ein Hash, nie die PIN oder die UID im Klartext. +model AusweisToken { + id String @id @default(uuid()) @db.Uuid + organisationId String @db.Uuid + personId String @db.Uuid + + typ AusweisTokenTyp + /// bcrypt-Hash der PIN bzw. der RFID-UID. + tokenHash String @db.VarChar(120) + /// Letzte vier Stellen der Karten-UID, damit man Karten unterscheiden kann. + kennzeichen String? @db.VarChar(20) + bezeichnung String? @db.VarChar(120) + + aktiv Boolean @default(true) + gueltigAb DateTime @default(now()) @db.Timestamptz(3) + gueltigBis DateTime? @db.Timestamptz(3) + letzteNutzung DateTime? @db.Timestamptz(3) + fehlversuche Int @default(0) + gesperrtBis DateTime? @db.Timestamptz(3) + + erstelltAm DateTime @default(now()) @db.Timestamptz(3) + geaendertAm DateTime @updatedAt @db.Timestamptz(3) + geloeschtAm DateTime? @db.Timestamptz(3) + + organisation Organisation @relation(fields: [organisationId], references: [id], onDelete: Cascade) + person Person @relation(fields: [personId], references: [id], onDelete: Cascade) + + /// Je Person und Art höchstens ein aktives Mittel dieser Bezeichnung. + @@index([organisationId, personId]) + @@index([organisationId, typ, aktiv]) + @@map("ausweis_token") +} + +// ============================================================================= +// Nachvollziehbarkeit +// ============================================================================= + +/// Append-only. Es gibt keine UPDATE- oder DELETE-Policy für die Anwendung; +/// zusätzlich verhindert ein Trigger Änderungen auch für den Eigentümer. +model AuditLog { + id String @id @default(uuid()) @db.Uuid + organisationId String @db.Uuid + + zeitpunkt DateTime @default(now()) @db.Timestamptz(3) + + akteurTyp AkteurTyp @default(PERSON) + akteurPersonId String? @db.Uuid + /// Klarname zum Zeitpunkt der Aktion — bleibt lesbar, auch wenn die Person + /// später anonymisiert wird. + akteurName String? @db.VarChar(200) + anmeldeArt AnmeldeArt? + + aktion AuditAktion + /// Modellname, z. B. "Geraet". + entitaet String @db.VarChar(80) + entitaetId String? @db.Uuid + /// Menschenlesbare Kurzfassung für die Oberfläche. + beschreibung String? @db.VarChar(500) + + vorher Json? @db.JsonB + nachher Json? @db.JsonB + /// Zusatzangaben: Anfrage-ID, Endpunkt, betroffene Felder. + kontext Json? @db.JsonB + + ipAdresse String? @db.Inet + userAgent String? @db.VarChar(400) + + organisation Organisation @relation(fields: [organisationId], references: [id], onDelete: Cascade) + akteur Person? @relation(fields: [akteurPersonId], references: [id], onDelete: SetNull) + + @@index([organisationId, zeitpunkt(sort: Desc)]) + @@index([organisationId, entitaet, entitaetId]) + @@index([organisationId, akteurPersonId]) + @@map("audit_log") +} + +// ============================================================================= +// Dateien +// ============================================================================= + +/// Metadaten zu einem Objekt in MinIO. Der Inhalt liegt NIE im Dateisystem und +/// nie in der Datenbank — hier steht nur, wo er zu finden ist. +model Datei { + id String @id @default(uuid()) @db.Uuid + organisationId String @db.Uuid + + bucket String @db.VarChar(80) + /// Objektschlüssel in MinIO, z. B. "ff-musterhausen/geraet//foto.jpg". + objektSchluessel String @db.VarChar(500) + + dateiname String @db.VarChar(300) + mimeTyp String @db.VarChar(160) + groesseBytes BigInt + /// SHA-256 des Inhalts. Erkennt Dubletten und belegt Unverändertheit. + pruefsumme String? @db.VarChar(64) + + /// Woran die Datei hängt (z. B. "Geraet") und an welchem Datensatz. + entitaet String? @db.VarChar(80) + entitaetId String? @db.Uuid + /// Freie Einordnung: "pruefprotokoll", "foto", "rechnung", "export". + kategorie String? @db.VarChar(60) + + hochgeladenVonId String? @db.Uuid + erstelltAm DateTime @default(now()) @db.Timestamptz(3) + geaendertAm DateTime @updatedAt @db.Timestamptz(3) + /// Soft-Delete: das Objekt in MinIO räumt erst der Aufbewahrungs-Job ab. + geloeschtAm DateTime? @db.Timestamptz(3) + + organisation Organisation @relation(fields: [organisationId], references: [id], onDelete: Cascade) + hochgeladenVon Person? @relation("DateiHochgeladenVon", fields: [hochgeladenVonId], references: [id], onDelete: SetNull) + alsAvatarGenutzt Person[] @relation("PersonAvatar") + + @@unique([bucket, objektSchluessel]) + @@index([organisationId, entitaet, entitaetId]) + @@index([organisationId, kategorie]) + @@map("datei") +} + +// ============================================================================= +// Vorgriff auf Phase 1 +// Diese beiden Modelle sind bewusst schmal gehalten. Sie existieren, damit das +// Seed-Skript eine realistische Wehr aufbauen kann und die Mandantentrennung +// an echten Daten prüfbar ist. Phase 1 erweitert sie um Funkrufname-Systematik, +// DIN-Typisierung, Geräteräume, Prüfintervalle und Lebensdauerkategorien. +// ============================================================================= + +/// Ein Einsatzfahrzeug der Wehr. +model Fahrzeug { + id String @id @default(uuid()) @db.Uuid + organisationId String @db.Uuid + + /// Funkrufname, z. B. "Florian Musterhausen 40/1". + funkrufname String @db.VarChar(120) + bezeichnung String @db.VarChar(120) + art FahrzeugArt @default(LOESCHFAHRZEUG) + kennzeichen String? @db.VarChar(20) + hersteller String? @db.VarChar(120) + baujahr Int? + /// Nächste Hauptuntersuchung. + tuevBis DateTime? @db.Date + aktiv Boolean @default(true) + + erstelltAm DateTime @default(now()) @db.Timestamptz(3) + geaendertAm DateTime @updatedAt @db.Timestamptz(3) + geloeschtAm DateTime? @db.Timestamptz(3) + + organisation Organisation @relation(fields: [organisationId], references: [id], onDelete: Cascade) + geraete Geraet[] + + @@unique([organisationId, funkrufname]) + @@index([organisationId, aktiv]) + @@map("fahrzeug") +} + +/// Ein prüfpflichtiges Gerät — Pressluftatmer, Schlauch, Leiter, Motorsäge. +model Geraet { + id String @id @default(uuid()) @db.Uuid + organisationId String @db.Uuid + + inventarnummer String @db.VarChar(60) + bezeichnung String @db.VarChar(160) + hersteller String? @db.VarChar(120) + seriennummer String? @db.VarChar(120) + + herstellungsdatum DateTime? @db.Date + indienststellung DateTime? @db.Date + + status GeraetStatus @default(EINSATZBEREIT) + /// Prüfintervall in Monaten aus Vorschrift oder Herstellerangabe. + pruefintervallMonate Int? + naechstePruefung DateTime? @db.Date + /// Maximale Nutzungsdauer in Jahren (Helme, Seile, Schutzkleidung). + lebensdauerJahre Int? + + /// Verlastet auf einem Fahrzeug — oder im Gerätehaus, dann null. + fahrzeugId String? @db.Uuid + lagerort String? @db.VarChar(160) + + erstelltAm DateTime @default(now()) @db.Timestamptz(3) + geaendertAm DateTime @updatedAt @db.Timestamptz(3) + geloeschtAm DateTime? @db.Timestamptz(3) + + organisation Organisation @relation(fields: [organisationId], references: [id], onDelete: Cascade) + fahrzeug Fahrzeug? @relation(fields: [fahrzeugId], references: [id], onDelete: SetNull) + + @@unique([organisationId, inventarnummer]) + @@index([organisationId, status]) + @@index([organisationId, naechstePruefung]) + @@index([organisationId, fahrzeugId]) + @@map("geraet") +} diff --git a/prisma/seed.ts b/prisma/seed.ts new file mode 100644 index 0000000..f4022ac --- /dev/null +++ b/prisma/seed.ts @@ -0,0 +1,595 @@ +/** + * Seed — legt eine vollständige Beispielwehr an. + * + * Inhalt: Berechtigungskatalog, 9 Systemrollen, 15 Personen mit Rollen, + * 3 Fahrzeuge, 40 Geräte, PIN und RFID-Ausweise für das Terminal. + * + * Das Skript läuft über DIRECT_DATABASE_URL, also mit dem Eigentümer-Zugang, + * der Row Level Security umgehen darf. Anders ginge es nicht: der Seed legt + * die Organisation ja erst an, deren Kontext RLS bräuchte. + * + * Es ist idempotent — mehrfaches Ausführen ändert nichts an bereits + * vorhandenen Daten. + * + * npm run db:seed + */ +import "dotenv/config"; +import { + Dienstgrad, + FahrzeugArt, + GeraetStatus, + PrismaClient, + RolleSchluessel, + AusweisTokenTyp, +} from "@prisma/client"; +import bcrypt from "bcryptjs"; + +const prisma = new PrismaClient({ + datasources: { + db: { url: process.env.DIRECT_DATABASE_URL ?? process.env.DATABASE_URL ?? "" }, + }, +}); + +const RUNDEN = Number(process.env.AUTH_BCRYPT_ROUNDS ?? 12); +const ORGANISATION_SLUG = "ff-musterhausen"; + +function log(nachricht: string): void { + console.info(`[seed] ${nachricht}`); +} + +/** Deterministisches Datum, damit zwei Läufe dieselben Daten erzeugen. */ +function datum(jahr: number, monat: number, tag: number): Date { + return new Date(Date.UTC(jahr, monat - 1, tag, 12, 0, 0)); +} + +/** Verschiebt ein Datum um Monate — für Prüffristen. */ +function plusMonate(basis: Date, monate: number): Date { + const neu = new Date(basis); + neu.setUTCMonth(neu.getUTCMonth() + monate); + return neu; +} + +// ----------------------------------------------------------------------------- +// Berechtigungskatalog +// ----------------------------------------------------------------------------- + +/** + * Der Katalog wird aus lib/authz.ts gespiegelt. Der Import geht bewusst über + * einen relativen Pfad, weil das Seed-Skript außerhalb des Next.js-Alias läuft. + */ +async function katalogAbgleichen(): Promise> { + const { RECHTE } = await import("../src/lib/authz"); + + const karte = new Map(); + + for (const [schluessel, angaben] of Object.entries(RECHTE)) { + const eintrag = await prisma.berechtigung.upsert({ + where: { schluessel }, + update: { bereich: angaben.bereich, bezeichnung: angaben.bezeichnung }, + create: { + schluessel, + bereich: angaben.bereich, + bezeichnung: angaben.bezeichnung, + }, + }); + karte.set(schluessel, eintrag.id); + } + + log(`Berechtigungskatalog: ${karte.size} Einzelrechte`); + return karte; +} + +// ----------------------------------------------------------------------------- +// Organisation und Rollen +// ----------------------------------------------------------------------------- + +async function organisationAnlegen() { + const organisation = await prisma.organisation.upsert({ + where: { slug: ORGANISATION_SLUG }, + update: {}, + create: { + slug: ORGANISATION_SLUG, + name: "Freiwillige Feuerwehr Musterhausen", + kurzname: "FF Musterhausen", + strasse: "Hauptstraße 12", + plz: "85354", + ort: "Musterhausen", + landkreis: "Freising", + bundesland: "Bayern", + telefon: "08161 123456", + email: "kommandant@ff-musterhausen.de", + ilsKennung: "FS-MUS-01", + funkPraefix: "Florian Musterhausen", + }, + }); + + log(`Organisation: ${organisation.name} (${organisation.id})`); + return organisation; +} + +async function rollenAnlegen(organisationId: string, katalog: Map) { + const { ROLLEN, ROLLEN_BEZEICHNUNG, ROLLEN_BESCHREIBUNG, ROLLEN_RECHTE, ALLE_RECHTE } = + await import("../src/lib/authz"); + + const rollenKarte = new Map(); + + for (const schluessel of ROLLEN) { + const rolle = await prisma.rolle.upsert({ + where: { + organisationId_schluessel: { + organisationId, + schluessel: schluessel as RolleSchluessel, + }, + }, + update: { + name: ROLLEN_BEZEICHNUNG[schluessel], + beschreibung: ROLLEN_BESCHREIBUNG[schluessel], + }, + create: { + organisationId, + schluessel: schluessel as RolleSchluessel, + name: ROLLEN_BEZEICHNUNG[schluessel], + beschreibung: ROLLEN_BESCHREIBUNG[schluessel], + systemRolle: true, + }, + }); + rollenKarte.set(schluessel, rolle.id); + + // Rechte der Rolle spiegeln — der Administrator bekommt alles. + const rechte = + schluessel === "SUPERADMIN" + ? ALLE_RECHTE + : (ROLLEN_RECHTE[schluessel as Exclude] ?? []); + + for (const recht of rechte) { + const berechtigungId = katalog.get(recht); + if (!berechtigungId) continue; + + await prisma.rolleBerechtigung.upsert({ + where: { rolleId_berechtigungId: { rolleId: rolle.id, berechtigungId } }, + update: { gewaehrt: true }, + create: { organisationId, rolleId: rolle.id, berechtigungId, gewaehrt: true }, + }); + } + } + + log(`Rollen: ${rollenKarte.size} Systemrollen samt Rechtezuordnung`); + return rollenKarte; +} + +// ----------------------------------------------------------------------------- +// Personen +// ----------------------------------------------------------------------------- + +interface PersonVorlage { + vorname: string; + nachname: string; + dienstgrad: Dienstgrad; + rollen: RolleSchluessel[]; + eintritt: Date; + abteilung: string; + /** PIN fürs Terminal. Nur im Seed im Klartext, in der Datenbank gehasht. */ + pin?: string; + /** RFID-Kennung der Ausweiskarte. */ + rfid?: string; +} + +const PERSONEN: PersonVorlage[] = [ + { + vorname: "Andreas", + nachname: "Huber", + dienstgrad: Dienstgrad.HAUPTBRANDMEISTER, + rollen: [RolleSchluessel.KOMMANDANT], + eintritt: datum(1998, 4, 1), + abteilung: "Aktive", + pin: "471203", + rfid: "04A3B7C1D2", + }, + { + vorname: "Bernhard", + nachname: "Maier", + dienstgrad: Dienstgrad.OBERBRANDMEISTER, + rollen: [RolleSchluessel.KOMMANDANT], + eintritt: datum(2001, 9, 15), + abteilung: "Aktive", + pin: "938157", + }, + { + vorname: "Christian", + nachname: "Bauer", + dienstgrad: Dienstgrad.HAUPTLOESCHMEISTER, + rollen: [RolleSchluessel.GERAETEWART], + eintritt: datum(2005, 3, 1), + abteilung: "Aktive", + pin: "620394", + rfid: "04B8C2D3E4", + }, + { + vorname: "Daniela", + nachname: "Schmid", + dienstgrad: Dienstgrad.OBERLOESCHMEISTER, + rollen: [RolleSchluessel.ATEMSCHUTZGERAETEWART], + eintritt: datum(2008, 7, 1), + abteilung: "Aktive", + pin: "184926", + rfid: "04C9D3E4F5", + }, + { + vorname: "Elisabeth", + nachname: "Wagner", + dienstgrad: Dienstgrad.LOESCHMEISTER, + rollen: [RolleSchluessel.KASSIER], + eintritt: datum(2010, 1, 15), + abteilung: "Aktive", + pin: "573081", + }, + { + vorname: "Franz", + nachname: "Fischer", + dienstgrad: Dienstgrad.LOESCHMEISTER, + rollen: [RolleSchluessel.SCHRIFTFUEHRER], + eintritt: datum(2009, 5, 20), + abteilung: "Aktive", + pin: "306159", + }, + { + vorname: "Gabriele", + nachname: "Weber", + dienstgrad: Dienstgrad.BRANDMEISTER, + rollen: [RolleSchluessel.AUSBILDER], + eintritt: datum(2004, 11, 1), + abteilung: "Aktive", + pin: "849265", + }, + { + vorname: "Hans", + nachname: "Becker", + dienstgrad: Dienstgrad.HAUPTFEUERWEHRMANN, + rollen: [RolleSchluessel.GERAETEWART, RolleSchluessel.AUSBILDER], + eintritt: datum(2012, 2, 1), + abteilung: "Aktive", + pin: "715384", + }, + { + vorname: "Ingrid", + nachname: "Hofmann", + dienstgrad: Dienstgrad.OBERFEUERWEHRMANN, + rollen: [RolleSchluessel.MITGLIED], + eintritt: datum(2015, 6, 1), + abteilung: "Aktive", + }, + { + vorname: "Josef", + nachname: "Schneider", + dienstgrad: Dienstgrad.OBERFEUERWEHRMANN, + rollen: [RolleSchluessel.MITGLIED], + eintritt: datum(2016, 4, 1), + abteilung: "Aktive", + }, + { + vorname: "Katharina", + nachname: "Koch", + dienstgrad: Dienstgrad.FEUERWEHRMANN, + rollen: [RolleSchluessel.MITGLIED], + eintritt: datum(2019, 9, 1), + abteilung: "Aktive", + }, + { + vorname: "Ludwig", + nachname: "Richter", + dienstgrad: Dienstgrad.FEUERWEHRMANN, + rollen: [RolleSchluessel.MITGLIED], + eintritt: datum(2020, 3, 1), + abteilung: "Aktive", + }, + { + vorname: "Martina", + nachname: "Klein", + dienstgrad: Dienstgrad.FEUERWEHRANWAERTER, + rollen: [RolleSchluessel.MITGLIED], + eintritt: datum(2024, 1, 15), + abteilung: "Aktive", + }, + { + vorname: "Nikolaus", + nachname: "Wolf", + dienstgrad: Dienstgrad.FEUERWEHRANWAERTER, + rollen: [RolleSchluessel.MITGLIED], + eintritt: datum(2025, 2, 1), + abteilung: "Jugendfeuerwehr", + }, + { + vorname: "Otto", + nachname: "Neumann", + dienstgrad: Dienstgrad.HAUPTBRANDMEISTER, + rollen: [RolleSchluessel.MITGLIED], + eintritt: datum(1975, 5, 1), + abteilung: "Passive", + }, +]; + +async function personenAnlegen( + organisationId: string, + rollenKarte: Map, +): Promise { + let nummer = 1; + + for (const vorlage of PERSONEN) { + const mitgliedsnummer = String(nummer).padStart(4, "0"); + const email = `${vorlage.vorname}.${vorlage.nachname}` + .toLowerCase() + .replace(/ä/g, "ae") + .replace(/ö/g, "oe") + .replace(/ü/g, "ue") + .replace(/ß/g, "ss") + "@ff-musterhausen.de"; + + const person = await prisma.person.upsert({ + where: { organisationId_mitgliedsnummer: { organisationId, mitgliedsnummer } }, + update: {}, + create: { + organisationId, + mitgliedsnummer, + vorname: vorlage.vorname, + nachname: vorlage.nachname, + dienstgrad: vorlage.dienstgrad, + email, + telefon: `08161 ${100000 + nummer * 7}`, + strasse: `Beispielweg ${nummer}`, + plz: "85354", + ort: "Musterhausen", + eintrittsdatum: vorlage.eintritt, + abteilung: vorlage.abteilung, + aktiv: vorlage.abteilung !== "Passive", + }, + }); + + for (const schluessel of vorlage.rollen) { + const rolleId = rollenKarte.get(schluessel); + if (!rolleId) continue; + await prisma.personRolle.upsert({ + where: { personId_rolleId: { personId: person.id, rolleId } }, + update: {}, + create: { organisationId, personId: person.id, rolleId }, + }); + } + + // Terminal-Ausweise: PIN und, wo vorhanden, RFID-Karte. + if (vorlage.pin) { + const vorhanden = await prisma.ausweisToken.findFirst({ + where: { organisationId, personId: person.id, typ: AusweisTokenTyp.PIN }, + }); + if (!vorhanden) { + await prisma.ausweisToken.create({ + data: { + organisationId, + personId: person.id, + typ: AusweisTokenTyp.PIN, + tokenHash: await bcrypt.hash(vorlage.pin, RUNDEN), + bezeichnung: "Terminal-PIN", + }, + }); + } + } + + if (vorlage.rfid) { + const vorhanden = await prisma.ausweisToken.findFirst({ + where: { organisationId, personId: person.id, typ: AusweisTokenTyp.RFID }, + }); + if (!vorhanden) { + await prisma.ausweisToken.create({ + data: { + organisationId, + personId: person.id, + typ: AusweisTokenTyp.RFID, + tokenHash: await bcrypt.hash(vorlage.rfid, RUNDEN), + kennzeichen: vorlage.rfid.slice(-4), + bezeichnung: "Ausweiskarte", + }, + }); + } + } + + nummer += 1; + } + + log(`Personen: ${PERSONEN.length} Mitglieder samt Rollen und Terminal-Ausweisen`); +} + +/** + * Der erste Administrator. Anders als die Mitglieder hat er ein Passwort für + * den lokalen Login — damit kommt man beim Erststart überhaupt hinein, bevor + * authentik eingerichtet ist. + */ +async function adminAnlegen(organisationId: string, rollenKarte: Map) { + const email = (process.env.SEED_ADMIN_EMAIL ?? "admin@beispielwehr.de").toLowerCase(); + const passwort = process.env.SEED_ADMIN_PASSWORT ?? "Feuerwehr2026"; + + const admin = await prisma.person.upsert({ + where: { organisationId_email: { organisationId, email } }, + update: {}, + create: { + organisationId, + mitgliedsnummer: "0000", + vorname: "System", + nachname: "Administrator", + email, + dienstgrad: Dienstgrad.KEIN, + passwortHash: await bcrypt.hash(passwort, RUNDEN), + passwortGeaendertAm: new Date(), + abteilung: "Verwaltung", + }, + }); + + const rolleId = rollenKarte.get(RolleSchluessel.SUPERADMIN); + if (rolleId) { + await prisma.personRolle.upsert({ + where: { personId_rolleId: { personId: admin.id, rolleId } }, + update: {}, + create: { organisationId, personId: admin.id, rolleId }, + }); + } + + log(`Administrator: ${email} (Passwort aus SEED_ADMIN_PASSWORT)`); + return admin; +} + +// ----------------------------------------------------------------------------- +// Fahrzeuge und Geräte +// ----------------------------------------------------------------------------- + +const FAHRZEUGE = [ + { + funkrufname: "Florian Musterhausen 40/1", + bezeichnung: "LF 20 (Löschgruppenfahrzeug)", + art: FahrzeugArt.LOESCHFAHRZEUG, + kennzeichen: "FS-MU 401", + hersteller: "MAN / Rosenbauer", + baujahr: 2018, + tuevBis: datum(2027, 5, 31), + }, + { + funkrufname: "Florian Musterhausen 42/1", + bezeichnung: "TSF-W (Tragkraftspritzenfahrzeug Wasser)", + art: FahrzeugArt.LOESCHFAHRZEUG, + kennzeichen: "FS-MU 421", + hersteller: "Mercedes-Benz / Ziegler", + baujahr: 2011, + tuevBis: datum(2026, 11, 30), + }, + { + funkrufname: "Florian Musterhausen 11/1", + bezeichnung: "MTW (Mannschaftstransportwagen)", + art: FahrzeugArt.MANNSCHAFTSTRANSPORT, + kennzeichen: "FS-MU 111", + hersteller: "Volkswagen", + baujahr: 2021, + tuevBis: datum(2027, 3, 31), + }, +]; + +/** + * 40 Geräte mit realistischen Prüfintervallen. Die Fristen sind bewusst + * gemischt: einige liegen in der Vergangenheit, damit der Status + * "Prüfung fällig" in der Oberfläche auch sichtbar ist. + */ +const GERAETE_VORLAGEN: Array<{ + bezeichnung: string; + hersteller: string; + anzahl: number; + intervallMonate: number; + lebensdauerJahre?: number; + aufFahrzeug?: number; + lagerort?: string; +}> = [ + { bezeichnung: "Pressluftatmer PA 94", hersteller: "Dräger", anzahl: 4, intervallMonate: 12, lebensdauerJahre: 20, aufFahrzeug: 0 }, + { bezeichnung: "Atemschutzmaske FPS 7000", hersteller: "Dräger", anzahl: 6, intervallMonate: 6, lebensdauerJahre: 10, lagerort: "Atemschutzwerkstatt" }, + { bezeichnung: "Atemluftflasche 6,8 l / 300 bar", hersteller: "Luxfer", anzahl: 8, intervallMonate: 60, lebensdauerJahre: 20, aufFahrzeug: 0 }, + { bezeichnung: "Feuerwehrhelm F220", hersteller: "Schuberth", anzahl: 6, intervallMonate: 12, lebensdauerJahre: 15, lagerort: "Umkleide" }, + { bezeichnung: "Feuerwehrleine 30 m", hersteller: "Gustav Bauer", anzahl: 3, intervallMonate: 12, lebensdauerJahre: 8, aufFahrzeug: 0 }, + { bezeichnung: "Steckleiterteil", hersteller: "Iturri", anzahl: 4, intervallMonate: 12, lebensdauerJahre: 25, aufFahrzeug: 0 }, + { bezeichnung: "Tragkraftspritze PFPN 10-1000", hersteller: "Rosenbauer", anzahl: 1, intervallMonate: 12, aufFahrzeug: 1 }, + { bezeichnung: "Hydraulischer Rettungssatz", hersteller: "Weber Rescue", anzahl: 1, intervallMonate: 12, aufFahrzeug: 0 }, + { bezeichnung: "Motorkettensäge MS 261", hersteller: "Stihl", anzahl: 2, intervallMonate: 12, aufFahrzeug: 0 }, + { bezeichnung: "Stromerzeuger 8 kVA", hersteller: "Endress", anzahl: 1, intervallMonate: 12, aufFahrzeug: 0 }, + { bezeichnung: "Wärmebildkamera", hersteller: "Dräger", anzahl: 1, intervallMonate: 12, aufFahrzeug: 0 }, + { bezeichnung: "Handsprechfunkgerät TPH900", hersteller: "Airbus", anzahl: 3, intervallMonate: 24, lagerort: "Funkraum" }, +]; + +async function fahrzeugeUndGeraeteAnlegen(organisationId: string): Promise { + const fahrzeugIds: string[] = []; + + for (const vorlage of FAHRZEUGE) { + const fahrzeug = await prisma.fahrzeug.upsert({ + where: { + organisationId_funkrufname: { organisationId, funkrufname: vorlage.funkrufname }, + }, + update: {}, + create: { organisationId, ...vorlage }, + }); + fahrzeugIds.push(fahrzeug.id); + } + + log(`Fahrzeuge: ${fahrzeugIds.length}`); + + const heute = new Date(); + let laufend = 1; + let angelegt = 0; + + for (const vorlage of GERAETE_VORLAGEN) { + for (let index = 0; index < vorlage.anzahl; index += 1) { + const inventarnummer = `INV-${String(laufend).padStart(4, "0")}`; + + // Ein Drittel der Geräte bekommt eine bereits überschrittene Frist, + // damit die Statusanzeige nicht nur grün ist. + const ueberfaellig = laufend % 3 === 0; + const indienststellung = datum(2015 + (laufend % 9), ((laufend % 12) + 1), 15); + const naechstePruefung = ueberfaellig + ? plusMonate(heute, -(1 + (laufend % 5))) + : plusMonate(heute, 1 + (laufend % vorlage.intervallMonate || 1)); + + const status = ueberfaellig + ? GeraetStatus.PRUEFUNG_FAELLIG + : laufend % 17 === 0 + ? GeraetStatus.GESPERRT + : GeraetStatus.EINSATZBEREIT; + + await prisma.geraet.upsert({ + where: { organisationId_inventarnummer: { organisationId, inventarnummer } }, + update: {}, + create: { + organisationId, + inventarnummer, + bezeichnung: vorlage.bezeichnung, + hersteller: vorlage.hersteller, + seriennummer: `SN-${vorlage.hersteller.slice(0, 3).toUpperCase()}-${100000 + laufend}`, + herstellungsdatum: datum(indienststellung.getUTCFullYear() - 1, 6, 1), + indienststellung, + status, + pruefintervallMonate: vorlage.intervallMonate, + naechstePruefung, + lebensdauerJahre: vorlage.lebensdauerJahre ?? null, + fahrzeugId: + vorlage.aufFahrzeug !== undefined ? (fahrzeugIds[vorlage.aufFahrzeug] ?? null) : null, + lagerort: vorlage.lagerort ?? null, + }, + }); + + laufend += 1; + angelegt += 1; + } + } + + log(`Geräte: ${angelegt}`); +} + +// ----------------------------------------------------------------------------- +// Ablauf +// ----------------------------------------------------------------------------- + +async function main(): Promise { + log("Starte ..."); + + const katalog = await katalogAbgleichen(); + const organisation = await organisationAnlegen(); + const rollen = await rollenAnlegen(organisation.id, katalog); + await adminAnlegen(organisation.id, rollen); + await personenAnlegen(organisation.id, rollen); + await fahrzeugeUndGeraeteAnlegen(organisation.id); + + log("Fertig."); + log(""); + log("Anmeldung für den Erststart:"); + log(` E-Mail: ${process.env.SEED_ADMIN_EMAIL ?? "admin@beispielwehr.de"}`); + log(` Passwort: ${process.env.SEED_ADMIN_PASSWORT ?? "Feuerwehr2026"}`); + log(""); + log("Terminal-PIN zum Ausprobieren (Mitgliedsnummer 0001, Andreas Huber): 471203"); +} + +main() + .catch((fehler) => { + console.error("[seed] Abbruch:", fehler); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + }); diff --git a/scripts/screenshots.ts b/scripts/screenshots.ts new file mode 100644 index 0000000..2d0aa61 --- /dev/null +++ b/scripts/screenshots.ts @@ -0,0 +1,100 @@ +/** + * Erzeugt die Bilder für docs/ANLEITUNG.md. + * + * Läuft gegen die laufende Anwendung, meldet sich mit dem Seed-Administrator an + * und legt die Bilder unter docs/screenshots ab — jeweils in heller und dunkler + * Darstellung, damit die Anleitung zu beiden Einstellungen passt. + * + * docker compose up -d + * npm run dev + * npm run screenshots + * + * Die Bilder werden mit eingecheckt: die Anleitung soll auch dann lesbar sein, + * wenn gerade kein Stack läuft. + */ +import "dotenv/config"; +import { mkdir } from "node:fs/promises"; +import path from "node:path"; +import { chromium, type Page } from "@playwright/test"; + +const BASIS = process.env.E2E_BASIS_URL ?? "http://localhost:3100"; +const ZIEL = path.join(process.cwd(), "docs", "screenshots"); + +const ADMIN_EMAIL = process.env.SEED_ADMIN_EMAIL ?? "admin@beispielwehr.de"; +const ADMIN_PASSWORT = process.env.SEED_ADMIN_PASSWORT ?? "Feuerwehr2026"; + +interface Aufnahme { + name: string; + pfad: string; + beschreibung: string; + /** true, wenn vorher angemeldet werden muss. */ + angemeldet: boolean; +} + +const AUFNAHMEN: Aufnahme[] = [ + { + name: "01-anmeldung", + pfad: "/anmelden", + beschreibung: "Anmeldeseite mit lokalem Login", + angemeldet: false, + }, + { + name: "02-uebersicht", + pfad: "/", + beschreibung: "Übersicht nach der Anmeldung", + angemeldet: true, + }, +]; + +async function anmelden(seite: Page): Promise { + await seite.goto(`${BASIS}/anmelden`, { waitUntil: "networkidle" }); + await seite.getByLabel("E-Mail-Adresse").fill(ADMIN_EMAIL); + await seite.getByLabel("Passwort").fill(ADMIN_PASSWORT); + await seite.getByRole("button", { name: "Anmelden" }).click(); + await seite.waitForURL(`${BASIS}/`, { timeout: 20_000 }); + await seite.waitForLoadState("networkidle"); +} + +async function main(): Promise { + await mkdir(ZIEL, { recursive: true }); + + const browser = await chromium.launch(); + + for (const darstellung of ["light", "dark"] as const) { + const kontext = await browser.newContext({ + viewport: { width: 1440, height: 900 }, + deviceScaleFactor: 2, + locale: "de-DE", + timezoneId: "Europe/Berlin", + colorScheme: darstellung, + }); + + const seite = await kontext.newPage(); + let istAngemeldet = false; + + for (const aufnahme of AUFNAHMEN) { + if (aufnahme.angemeldet && !istAngemeldet) { + await anmelden(seite); + istAngemeldet = true; + } + + await seite.goto(`${BASIS}${aufnahme.pfad}`, { waitUntil: "networkidle" }); + // Kurz warten, damit Übergänge und Symbole fertig gezeichnet sind. + await seite.waitForTimeout(400); + + const datei = path.join(ZIEL, `${aufnahme.name}-${darstellung}.png`); + await seite.screenshot({ path: datei, fullPage: true }); + console.info(`[screenshots] ${aufnahme.beschreibung} (${darstellung}) -> ${datei}`); + } + + await kontext.close(); + } + + await browser.close(); + console.info("[screenshots] Fertig."); +} + +main().catch((fehler) => { + console.error("[screenshots] Abbruch:", fehler); + process.exit(1); +}); diff --git a/src/app/(app)/layout.tsx b/src/app/(app)/layout.tsx new file mode 100644 index 0000000..f70bc37 --- /dev/null +++ b/src/app/(app)/layout.tsx @@ -0,0 +1,42 @@ +import { redirect } from "next/navigation"; +import { aktuelleSitzung } from "@/lib/auth"; +import { fuerOrganisation } from "@/lib/db"; +import { rechteVon } from "@/lib/authz"; +import { Seitenleiste } from "@/components/layout/seitenleiste"; +import { ThemeToggle } from "@/components/layout/theme-toggle"; + +/** + * Layout für alles, was eine Anmeldung verlangt. Wer nicht angemeldet ist, + * landet auf der Anmeldeseite — geprüft wird serverseitig, nicht im Browser. + */ +export default async function AngemeldetLayout({ + children, +}: Readonly<{ children: React.ReactNode }>) { + const sitzung = await aktuelleSitzung(); + if (!sitzung) redirect("/anmelden"); + + const organisation = await fuerOrganisation(sitzung.organisationId, (db) => + db.organisation.findUnique({ + where: { id: sitzung.organisationId }, + select: { name: true, kurzname: true }, + }), + ); + + return ( +
+ + +
+
+ +
+
{children}
+
+
+ ); +} diff --git a/src/app/(app)/page.tsx b/src/app/(app)/page.tsx new file mode 100644 index 0000000..bb3767a --- /dev/null +++ b/src/app/(app)/page.tsx @@ -0,0 +1,121 @@ +import { AlertTriangle, Truck, Users, Wrench } from "lucide-react"; +import { aktuelleSitzung } from "@/lib/auth"; +import { fuerOrganisation } from "@/lib/db"; +import { ROLLEN_BEZEICHNUNG } from "@/lib/authz"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { alsDatumZeit } from "@/lib/utils"; + +export const metadata = { title: "Übersicht" }; + +/** Startseite nach der Anmeldung. */ +export default async function UebersichtSeite() { + const sitzung = await aktuelleSitzung(); + if (!sitzung) return null; + + const daten = await fuerOrganisation(sitzung.organisationId, async (db) => { + const [organisation, mitglieder, fahrzeuge, geraete, pruefungFaellig, gesperrt] = + await Promise.all([ + db.organisation.findUnique({ where: { id: sitzung.organisationId } }), + db.person.count({ where: { aktiv: true, geloeschtAm: null } }), + db.fahrzeug.count({ where: { aktiv: true, geloeschtAm: null } }), + db.geraet.count({ where: { geloeschtAm: null } }), + db.geraet.count({ where: { status: "PRUEFUNG_FAELLIG", geloeschtAm: null } }), + db.geraet.count({ where: { status: "GESPERRT", geloeschtAm: null } }), + ]); + return { organisation, mitglieder, fahrzeuge, geraete, pruefungFaellig, gesperrt }; + }); + + const kacheln = [ + { titel: "Aktive Mitglieder", wert: daten.mitglieder, symbol: Users }, + { titel: "Fahrzeuge", wert: daten.fahrzeuge, symbol: Truck }, + { titel: "Geräte", wert: daten.geraete, symbol: Wrench }, + { titel: "Prüfung fällig", wert: daten.pruefungFaellig, symbol: AlertTriangle }, + ]; + + return ( +
+
+

+ {daten.organisation?.name ?? "Feuerwehr"} +

+

+ Angemeldet als {sitzung.name} —{" "} + {sitzung.rollen.map((rolle) => ROLLEN_BEZEICHNUNG[rolle]).join(", ")} +

+
+ + {/* + Sicherheitshinweis nach den Projektleitplanken. Er bleibt stehen, bis + die Wehr die Anwendung ausdrücklich freigegeben hat. + */} +
+ +
+

Diese Installation ist noch nicht freigegeben.

+

+ Atemschutzüberwachung und Alarmmonitor sind sicherheitsrelevant. Solange die + Wehr das System nicht ausdrücklich freigegeben hat, bleibt die analoge + Überwachung führend. FLORIAN ersetzt sie nicht. +

+
+
+ +
+ {kacheln.map((kachel) => { + const Symbol = kachel.symbol; + return ( + + + + {kachel.titel} + + + + +

{kachel.wert}

+
+
+ ); + })} +
+ + + + Stand des Aufbaus + + Phase 0 ist abgeschlossen: Fundament, Mandantentrennung, Anmeldung, + Dateiablage und Protokollierung stehen. Die Fachmodule kommen Phase für + Phase dazu. + + + +
+ Phase 0 — Fundament + Phase 1 — Stammdaten + Phase 2 — Wartung und Prüfung + Phase 3 — Einsätze + Phase 4 — Terminal + Phase 5 — Atemschutz +
+
+
+
Gesperrte Geräte
+
{daten.gesperrt}
+
+
+
Zeitpunkt
+
{alsDatumZeit(new Date())}
+
+
+
+
+
+ ); +} diff --git a/src/app/anmelden/formular.tsx b/src/app/anmelden/formular.tsx new file mode 100644 index 0000000..fa4ffd2 --- /dev/null +++ b/src/app/anmelden/formular.tsx @@ -0,0 +1,151 @@ +"use client"; + +import * as React from "react"; +import { signIn } from "next-auth/react"; +import { useRouter } from "next/navigation"; +import { KeyRound, LogIn } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; + +export interface AnmeldeFormularProps { + oidcVerfuegbar: boolean; + oidcName: string; + lokalVerfuegbar: boolean; + fehler?: string; + weiterZu?: string; +} + +/** Wandelt die Fehlerkennungen von Auth.js in verständliches Deutsch. */ +function fehlertext(kennung?: string): string | null { + if (!kennung) return null; + switch (kennung) { + case "CredentialsSignin": + return "E-Mail-Adresse oder Passwort stimmt nicht. Nach mehreren Fehlversuchen wird das Konto vorübergehend gesperrt."; + case "AccessDenied": + return "Für dieses Konto gibt es in FLORIAN keinen Zugang. Bitte beim Kommandanten melden."; + case "OAuthAccountNotLinked": + return "Dieses Konto ist bereits mit einem anderen Anmeldeweg verknüpft."; + case "Configuration": + return "Die Anmeldung ist nicht vollständig eingerichtet. Bitte in der Serverkonfiguration prüfen."; + default: + return "Die Anmeldung ist fehlgeschlagen. Bitte erneut versuchen."; + } +} + +export function AnmeldeFormular({ + oidcVerfuegbar, + oidcName, + lokalVerfuegbar, + fehler, + weiterZu, +}: AnmeldeFormularProps) { + const router = useRouter(); + const [laeuft, setLaeuft] = React.useState(false); + const [meldung, setMeldung] = React.useState(fehlertext(fehler)); + + async function lokalAnmelden(ereignis: React.FormEvent) { + ereignis.preventDefault(); + setMeldung(null); + setLaeuft(true); + + const daten = new FormData(ereignis.currentTarget); + const ergebnis = await signIn("passwort", { + email: String(daten.get("email") ?? ""), + passwort: String(daten.get("passwort") ?? ""), + redirect: false, + }); + + setLaeuft(false); + + if (ergebnis?.error) { + setMeldung(fehlertext("CredentialsSignin")); + return; + } + router.push(weiterZu ?? "/"); + router.refresh(); + } + + return ( +
+ {meldung && ( +

+ {meldung} +

+ )} + + {oidcVerfuegbar && ( + + )} + + {oidcVerfuegbar && lokalVerfuegbar && ( +
+ + + oder + + +
+ )} + + {lokalVerfuegbar && ( +
+
+ + +
+ +
+ + +
+ + +
+ )} + + {!oidcVerfuegbar && !lokalVerfuegbar && ( +

+ Es ist kein Anmeldeweg eingerichtet. Bitte in der Serverkonfiguration + AUTH_LOCAL_ENABLED oder AUTH_OIDC_ENABLED setzen. +

+ )} + +

+ Am Terminal im Gerätehaus wird stattdessen mit PIN oder Ausweiskarte + angemeldet. +

+
+ ); +} diff --git a/src/app/anmelden/page.tsx b/src/app/anmelden/page.tsx new file mode 100644 index 0000000..8669b90 --- /dev/null +++ b/src/app/anmelden/page.tsx @@ -0,0 +1,53 @@ +import { redirect } from "next/navigation"; +import { Flame } from "lucide-react"; +import { aktuelleSitzung } from "@/lib/auth"; +import { env, oidcVerfuegbar } from "@/lib/env"; +import { Card, CardContent, CardDescription, CardHeader } from "@/components/ui/card"; +import { AnmeldeFormular } from "@/app/anmelden/formular"; + +export const metadata = { title: "Anmelden" }; + +/** + * Anmeldeseite. Zeigt genau die Wege, die in dieser Installation eingerichtet + * sind: OIDC gegen authentik, lokaler Login, oder beides. + */ +export default async function AnmeldenSeite({ + searchParams, +}: { + searchParams: Promise<{ error?: string; callbackUrl?: string }>; +}) { + const sitzung = await aktuelleSitzung(); + if (sitzung) redirect("/"); + + const { error, callbackUrl } = await searchParams; + + return ( +
+ + +
+ +
+ {/* Echte h1: die Seite braucht eine Ueberschrift, die + Hilfsmittel und Tests ansteuern koennen. */} +

+ FLORIAN +

+ Feuerwehrverwaltung +
+
+
+ + + + +
+
+ ); +} diff --git a/src/app/api/auth/[...nextauth]/route.ts b/src/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 0000000..6fd0f23 --- /dev/null +++ b/src/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,8 @@ +import { handlers } from "@/lib/auth"; + +/** + * Auth.js-Endpunkte. Die Rückleitung von authentik landet hier unter + * /api/auth/callback/authentik — genau diese Adresse muss im authentik-Provider + * als Redirect-URI eingetragen sein. + */ +export const { GET, POST } = handlers; diff --git a/src/app/api/health/route.ts b/src/app/api/health/route.ts new file mode 100644 index 0000000..338a592 --- /dev/null +++ b/src/app/api/health/route.ts @@ -0,0 +1,37 @@ +import { NextResponse } from "next/server"; +import { datenbankErreichbar } from "@/lib/db"; +import { speicherErreichbar } from "@/lib/storage"; +import { warteschlangeErreichbar } from "@/lib/warteschlange"; + +/** + * Erreichbarkeitsprüfung für Docker, Traefik und die Überwachung. + * 200, wenn Datenbank, Objektspeicher und Warteschlange antworten — sonst 503. + * Bewusst ohne personenbezogene Angaben, damit der Endpunkt offen sein kann. + */ +export const dynamic = "force-dynamic"; +export const revalidate = 0; + +export async function GET() { + const beginn = Date.now(); + + const [datenbank, speicher, warteschlange] = await Promise.all([ + datenbankErreichbar(), + speicherErreichbar(), + warteschlangeErreichbar(), + ]); + + const gesund = datenbank && speicher && warteschlange; + + return NextResponse.json( + { + status: gesund ? "ok" : "gestoert", + dienste: { datenbank, speicher, warteschlange }, + dauerMs: Date.now() - beginn, + zeitpunkt: new Date().toISOString(), + }, + { + status: gesund ? 200 : 503, + headers: { "Cache-Control": "no-store" }, + }, + ); +} diff --git a/src/app/api/trpc/[trpc]/route.ts b/src/app/api/trpc/[trpc]/route.ts new file mode 100644 index 0000000..79add83 --- /dev/null +++ b/src/app/api/trpc/[trpc]/route.ts @@ -0,0 +1,22 @@ +import { fetchRequestHandler } from "@trpc/server/adapters/fetch"; +import { appRouter } from "@/server/routers/_app"; +import { kontextErzeugen } from "@/server/context"; +import { env } from "@/lib/env"; + +/** Einstiegspunkt für alle internen tRPC-Aufrufe. */ +function verarbeiten(anfrage: Request) { + return fetchRequestHandler({ + endpoint: "/api/trpc", + req: anfrage, + router: appRouter, + createContext: () => kontextErzeugen({ req: anfrage }), + onError: + env.NODE_ENV === "development" + ? ({ path, error }) => { + console.error(`[trpc] Fehler in ${path ?? ""}:`, error.message); + } + : undefined, + }); +} + +export { verarbeiten as GET, verarbeiten as POST }; diff --git a/src/app/globals.css b/src/app/globals.css new file mode 100644 index 0000000..c143f41 --- /dev/null +++ b/src/app/globals.css @@ -0,0 +1,170 @@ +@import "tailwindcss"; +@import "tw-animate-css"; + +@custom-variant dark (&:is(.dark *)); + +/* + Farbwelt FLORIAN. + Grundton ist das Rot der Feuerwehr, bewusst entsättigt — eine Oberfläche, in + der jemand acht Stunden Prüfungen dokumentiert, darf nicht schreien. Signalrot + bleibt für das reserviert, was wirklich Alarm ist: gesperrte Geräte, + überzogene Fristen, laufende Atemschutzüberwachung. + + Keine Google Fonts, keine externen CDNs — es werden ausschließlich Schriften + benutzt, die auf dem Gerät vorhanden sind. +*/ + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + + /* Statusfarben der Gerätezustände — überall dieselben. */ + --color-einsatzbereit: var(--einsatzbereit); + --color-pruefung-faellig: var(--pruefung-faellig); + --color-gesperrt: var(--gesperrt); + --color-ausgesondert: var(--ausgesondert); + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + + --font-sans: "Inter", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", sans-serif; + --font-mono: "JetBrains Mono", "Cascadia Code", Consolas, "Courier New", monospace; +} + +:root { + --radius: 0.625rem; + + --background: oklch(0.99 0.002 60); + --foreground: oklch(0.21 0.01 40); + --card: oklch(1 0 0); + --card-foreground: oklch(0.21 0.01 40); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.21 0.01 40); + + /* Feuerwehrrot, für Flächen nutzbar gedämpft. */ + --primary: oklch(0.52 0.19 27); + --primary-foreground: oklch(0.99 0.005 60); + + --secondary: oklch(0.96 0.005 60); + --secondary-foreground: oklch(0.28 0.015 40); + --muted: oklch(0.96 0.005 60); + --muted-foreground: oklch(0.53 0.012 45); + --accent: oklch(0.95 0.012 40); + --accent-foreground: oklch(0.28 0.015 40); + + --destructive: oklch(0.58 0.23 27); + --destructive-foreground: oklch(0.99 0.005 60); + + --border: oklch(0.91 0.005 60); + --input: oklch(0.91 0.005 60); + --ring: oklch(0.52 0.19 27); + + --einsatzbereit: oklch(0.62 0.15 150); + --pruefung-faellig: oklch(0.72 0.16 75); + --gesperrt: oklch(0.58 0.23 27); + --ausgesondert: oklch(0.6 0.01 60); +} + +/* + Dunkle Darstellung. Nicht bloß invertiert: im Gerätehaus wird abends und + nachts gearbeitet, deshalb ein warmes, kontrastarmes Grau statt Schwarz — + das blendet nicht, wenn man vom Fahrzeug hereinkommt. +*/ +.dark { + --background: oklch(0.17 0.008 40); + --foreground: oklch(0.94 0.004 60); + --card: oklch(0.21 0.009 40); + --card-foreground: oklch(0.94 0.004 60); + --popover: oklch(0.21 0.009 40); + --popover-foreground: oklch(0.94 0.004 60); + + --primary: oklch(0.63 0.19 27); + --primary-foreground: oklch(0.98 0.005 60); + + --secondary: oklch(0.26 0.01 40); + --secondary-foreground: oklch(0.94 0.004 60); + --muted: oklch(0.26 0.01 40); + --muted-foreground: oklch(0.68 0.01 50); + --accent: oklch(0.29 0.012 40); + --accent-foreground: oklch(0.94 0.004 60); + + --destructive: oklch(0.65 0.21 27); + --destructive-foreground: oklch(0.98 0.005 60); + + --border: oklch(0.29 0.01 40); + --input: oklch(0.29 0.01 40); + --ring: oklch(0.63 0.19 27); + + --einsatzbereit: oklch(0.68 0.15 150); + --pruefung-faellig: oklch(0.78 0.15 75); + --gesperrt: oklch(0.65 0.21 27); + --ausgesondert: oklch(0.55 0.01 60); +} + +@layer base { + * { + border-color: var(--border); + } + + body { + background-color: var(--background); + color: var(--foreground); + font-family: var(--font-sans); + -webkit-font-smoothing: antialiased; + text-rendering: optimizeLegibility; + } + + /* Tastaturbedienung muss sichtbar sein — am Terminal wird auch getabbt. */ + :focus-visible { + outline: 2px solid var(--ring); + outline-offset: 2px; + } + + /* Zahlen in Tabellen sollen untereinander stehen. */ + table, + .tabellenzahlen { + font-variant-numeric: tabular-nums; + } +} + +/* Am Kiosk-Terminal wird mit dem Finger bedient: größere Ziele, keine Auswahl. */ +.kiosk { + --radius: 0.875rem; + user-select: none; +} + +.kiosk button, +.kiosk [role="button"] { + min-height: 3.5rem; + font-size: 1.125rem; +} + +@media print { + .nicht-drucken { + display: none !important; + } + + body { + background: white; + color: black; + } +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 0000000..5537171 --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,45 @@ +import type { Metadata, Viewport } from "next"; +import "./globals.css"; +import { ThemeProvider } from "@/components/layout/theme-provider"; + +export const metadata: Metadata = { + title: { + default: "FLORIAN — Feuerwehrverwaltung", + template: "%s · FLORIAN", + }, + description: + "Verwaltungsplattform für Freiwillige Feuerwehren: Mitglieder, Fahrzeuge, Geräte, Prüfungen, Atemschutz, Einsätze.", + applicationName: "FLORIAN", + // Keine Suchmaschinen: die Installation steht im Gerätehaus, nicht im Netz. + robots: { index: false, follow: false }, +}; + +export const viewport: Viewport = { + width: "device-width", + initialScale: 1, + // Am Terminal soll nicht versehentlich gezoomt werden. + maximumScale: 5, + themeColor: [ + { media: "(prefers-color-scheme: light)", color: "#ffffff" }, + { media: "(prefers-color-scheme: dark)", color: "#1c1a19" }, + ], +}; + +export default function RootLayout({ + children, +}: Readonly<{ children: React.ReactNode }>) { + return ( + + + + {children} + + + + ); +} diff --git a/src/components/layout/seitenleiste.tsx b/src/components/layout/seitenleiste.tsx new file mode 100644 index 0000000..c622fa8 --- /dev/null +++ b/src/components/layout/seitenleiste.tsx @@ -0,0 +1,216 @@ +"use client"; + +import * as React from "react"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { + Album, + BookOpenCheck, + CalendarDays, + ClipboardCheck, + Flame, + Gauge, + Map, + Menu, + MonitorSmartphone, + Siren, + Truck, + Users, + Wrench, + X, +} from "lucide-react"; +import { cn } from "@/lib/utils"; +import { Button } from "@/components/ui/button"; +import type { Recht, Rolle } from "@/lib/authz"; + +/** + * Hauptnavigation. + * + * Welche Einträge jemand sieht, entscheidet NICHT diese Komponente. Sie bekommt + * die Rechte der Sitzung übergeben und blendet danach aus — die Prüfung selbst + * bleibt in lib/authz.ts, wie es die Projektregeln verlangen. Ein ausgeblendeter + * Eintrag ist Bequemlichkeit, kein Schutz; der Schutz sitzt in der tRPC-Schicht. + * + * Die Phasenangaben entsprechen den Dateien Phase-0.md bis Phase-8.md. + */ + +interface Eintrag { + titel: string; + pfad: string; + symbol: React.ComponentType<{ className?: string }>; + /** Sichtbar, wenn eines dieser Rechte vorliegt. Leer = für alle. */ + rechte?: Recht[]; + /** Ab welcher Phase es die Ansicht wirklich gibt. */ + abPhase?: number; +} + +const EINTRAEGE: Eintrag[] = [ + { titel: "Übersicht", pfad: "/", symbol: Gauge }, + { titel: "Mitglieder", pfad: "/mitglieder", symbol: Users, rechte: ["person.lesen"], abPhase: 1 }, + { titel: "Fahrzeuge", pfad: "/fahrzeuge", symbol: Truck, rechte: ["fahrzeug.lesen"], abPhase: 1 }, + { titel: "Geräte", pfad: "/geraete", symbol: Wrench, rechte: ["geraet.lesen"], abPhase: 1 }, + { + titel: "Qualifikationen", + pfad: "/qualifikationen", + symbol: BookOpenCheck, + rechte: ["qualifikation.lesen"], + abPhase: 1, + }, + { + titel: "Prüfungen", + pfad: "/pruefungen", + symbol: ClipboardCheck, + rechte: ["pruefung.lesen"], + abPhase: 2, + }, + { titel: "Einsätze", pfad: "/einsaetze", symbol: Flame, rechte: ["einsatz.lesen"], abPhase: 3 }, + { + titel: "Terminal", + pfad: "/kiosk", + symbol: MonitorSmartphone, + rechte: ["kiosk.erfassen"], + abPhase: 4, + }, + { + titel: "Atemschutz", + pfad: "/atemschutz", + symbol: Siren, + rechte: ["atemschutz.lesen"], + abPhase: 5, + }, + { titel: "Karte", pfad: "/karte", symbol: Map, rechte: ["einsatz.lesen"], abPhase: 6 }, + { + titel: "Termine", + pfad: "/termine", + symbol: CalendarDays, + rechte: ["ausbildung.lesen"], + abPhase: 7, + }, + { + titel: "Protokoll", + pfad: "/protokoll", + symbol: Album, + rechte: ["system.auditlog"], + abPhase: 1, + }, +]; + +/** In Phase 0 gibt es nur die Übersicht — alles andere ist noch Ausblick. */ +const AKTUELLE_PHASE = 0; + +export interface SeitenleisteProps { + rechte: Recht[]; + rollen: Rolle[]; + organisationName: string; + personName: string; +} + +export function Seitenleiste({ rechte, organisationName, personName }: SeitenleisteProps) { + const pfad = usePathname(); + const [offen, setOffen] = React.useState(false); + const rechteMenge = React.useMemo(() => new Set(rechte), [rechte]); + + const sichtbar = EINTRAEGE.filter( + (eintrag) => !eintrag.rechte || eintrag.rechte.some((recht) => rechteMenge.has(recht)), + ); + + return ( + <> + {/* Auf schmalen Geräten aufklappbar. */} + + + + + {offen && ( + + ); +} diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx new file mode 100644 index 0000000..7839898 --- /dev/null +++ b/src/components/ui/badge.tsx @@ -0,0 +1,41 @@ +import * as React from "react"; +import { cva, type VariantProps } from "class-variance-authority"; +import { cn } from "@/lib/utils"; + +/** + * Statusabzeichen. Die Gerätezustände haben feste Farben, die überall gleich + * bedeuten — grün einsatzbereit, gelb Prüfung fällig, rot gesperrt, grau + * ausgesondert. Wer die Farbe kennt, muss den Text nicht lesen. + */ +const badgeVarianten = cva( + "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-medium transition-colors", + { + variants: { + variant: { + default: + "border-transparent bg-[var(--primary)] text-[var(--primary-foreground)]", + secondary: + "border-transparent bg-[var(--secondary)] text-[var(--secondary-foreground)]", + outline: "text-[var(--foreground)] border-[var(--border)]", + einsatzbereit: + "border-transparent bg-[var(--einsatzbereit)]/15 text-[var(--einsatzbereit)]", + pruefungFaellig: + "border-transparent bg-[var(--pruefung-faellig)]/20 text-[var(--pruefung-faellig)]", + gesperrt: "border-transparent bg-[var(--gesperrt)]/15 text-[var(--gesperrt)]", + ausgesondert: + "border-transparent bg-[var(--ausgesondert)]/15 text-[var(--ausgesondert)]", + }, + }, + defaultVariants: { variant: "default" }, + }, +); + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return
; +} + +export { Badge, badgeVarianten }; diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx new file mode 100644 index 0000000..b6ccdb0 --- /dev/null +++ b/src/components/ui/button.tsx @@ -0,0 +1,55 @@ +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; +import { cn } from "@/lib/utils"; + +const buttonVarianten = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--background)] disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", + { + variants: { + variant: { + default: + "bg-[var(--primary)] text-[var(--primary-foreground)] hover:opacity-90 shadow-sm", + destructive: + "bg-[var(--destructive)] text-[var(--destructive-foreground)] hover:opacity-90 shadow-sm", + outline: + "border border-[var(--border)] bg-[var(--background)] hover:bg-[var(--accent)] hover:text-[var(--accent-foreground)]", + secondary: + "bg-[var(--secondary)] text-[var(--secondary-foreground)] hover:opacity-80", + ghost: "hover:bg-[var(--accent)] hover:text-[var(--accent-foreground)]", + link: "text-[var(--primary)] underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2", + sm: "h-8 rounded-md px-3 text-xs", + lg: "h-11 rounded-md px-8", + /** Für das Terminal im Gerätehaus — mit Handschuhen bedienbar. */ + kiosk: "h-16 rounded-xl px-8 text-lg", + icon: "h-9 w-9", + }, + }, + defaultVariants: { variant: "default", size: "default" }, + }, +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Komponente = asChild ? Slot : "button"; + return ( + + ); + }, +); +Button.displayName = "Button"; + +export { Button, buttonVarianten }; diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx new file mode 100644 index 0000000..96efaf7 --- /dev/null +++ b/src/components/ui/card.tsx @@ -0,0 +1,61 @@ +import * as React from "react"; +import { cn } from "@/lib/utils"; + +const Card = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +); +Card.displayName = "Card"; + +const CardHeader = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +); +CardHeader.displayName = "CardHeader"; + +const CardTitle = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +); +CardTitle.displayName = "CardTitle"; + +const CardDescription = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +); +CardDescription.displayName = "CardDescription"; + +const CardContent = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +); +CardContent.displayName = "CardContent"; + +const CardFooter = React.forwardRef>( + ({ className, ...props }, ref) => ( +
+ ), +); +CardFooter.displayName = "CardFooter"; + +export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }; diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx new file mode 100644 index 0000000..8e96cac --- /dev/null +++ b/src/components/ui/input.tsx @@ -0,0 +1,23 @@ +import * as React from "react"; +import { cn } from "@/lib/utils"; + +const Input = React.forwardRef>( + ({ className, type, ...props }, ref) => ( + + ), +); +Input.displayName = "Input"; + +export { Input }; diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx new file mode 100644 index 0000000..7e93689 --- /dev/null +++ b/src/components/ui/label.tsx @@ -0,0 +1,22 @@ +"use client"; + +import * as React from "react"; +import * as LabelPrimitive from "@radix-ui/react-label"; +import { cn } from "@/lib/utils"; + +const Label = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +Label.displayName = LabelPrimitive.Root.displayName; + +export { Label }; diff --git a/src/lib/audit.ts b/src/lib/audit.ts new file mode 100644 index 0000000..c79189c --- /dev/null +++ b/src/lib/audit.ts @@ -0,0 +1,238 @@ +import "server-only"; +import { AkteurTyp, AnmeldeArt, AuditAktion, Prisma } from "@prisma/client"; +import { fuerOrganisation, prismaSystem, type MandantenClient } from "@/lib/db"; +import type { Sitzung } from "@/lib/authz"; + +/** + * Audit-Log. + * + * Regel aus CLAUDE.md: Jede schreibende Operation erzeugt einen Eintrag mit + * Akteur, Zeitpunkt, Entität und Vorher/Nachher als JSONB. Die Tabelle ist + * append-only — auf Datenbankebene per Trigger abgesichert, nicht nur hier. + * + * Zwei Aufrufarten: + * protokolliere(...) — eigene Transaktion, für Einzelaktionen + * protokolliereIn(db, ...) — innerhalb einer bestehenden Transaktion, damit + * Änderung und Protokoll gemeinsam gültig werden + * oder gemeinsam zurückgerollt. + * + * Der zweite Weg ist der richtige, sobald es um Daten geht, die wirklich + * zusammengehören. + */ + +export { AuditAktion, AkteurTyp, AnmeldeArt }; + +/** Felder, die niemals ins Protokoll wandern. */ +const GEHEIME_FELDER = new Set([ + "passwortHash", + "passwort", + "tokenHash", + "pin", + "secret", + "clientSecret", + "accessToken", + "refreshToken", +]); + +/** + * Entfernt Geheimnisse und wandelt Werte, die JSONB nicht kennt (Date, BigInt), + * in etwas Speicherbares um. + */ +export function fuerProtokoll(wert: unknown, tiefe = 0): Prisma.InputJsonValue | null { + if (wert === null || wert === undefined) return null; + if (tiefe > 6) return "[zu tief verschachtelt]"; + + if (wert instanceof Date) return wert.toISOString(); + if (typeof wert === "bigint") return wert.toString(); + if (typeof wert === "string" || typeof wert === "number" || typeof wert === "boolean") { + return wert; + } + + if (Array.isArray(wert)) { + return wert.map((eintrag) => fuerProtokoll(eintrag, tiefe + 1) ?? null); + } + + if (typeof wert === "object") { + const ergebnis: Record = {}; + for (const [schluessel, inhalt] of Object.entries(wert as Record)) { + ergebnis[schluessel] = GEHEIME_FELDER.has(schluessel) + ? "[entfernt]" + : fuerProtokoll(inhalt, tiefe + 1); + } + return ergebnis as Prisma.InputJsonValue; + } + + return String(wert); +} + +/** + * Nur die tatsächlich geänderten Felder — sonst bläht jeder Speichervorgang das + * Protokoll mit unveränderten Werten auf. + */ +export function unterschied( + vorher: Record | null | undefined, + nachher: Record | null | undefined, +): { vorher: Prisma.InputJsonValue | null; nachher: Prisma.InputJsonValue | null; felder: string[] } { + if (!vorher) return { vorher: null, nachher: fuerProtokoll(nachher), felder: Object.keys(nachher ?? {}) }; + if (!nachher) return { vorher: fuerProtokoll(vorher), nachher: null, felder: Object.keys(vorher) }; + + const felder: string[] = []; + const vorherGefiltert: Record = {}; + const nachherGefiltert: Record = {}; + + for (const schluessel of new Set([...Object.keys(vorher), ...Object.keys(nachher)])) { + const alt = vorher[schluessel]; + const neu = nachher[schluessel]; + if (JSON.stringify(alt) === JSON.stringify(neu)) continue; + felder.push(schluessel); + vorherGefiltert[schluessel] = alt; + nachherGefiltert[schluessel] = neu; + } + + return { + vorher: fuerProtokoll(vorherGefiltert), + nachher: fuerProtokoll(nachherGefiltert), + felder, + }; +} + +export interface ProtokollEingabe { + organisationId: string; + aktion: AuditAktion; + /** Modellname, z. B. "Geraet". */ + entitaet: string; + entitaetId?: string | null; + beschreibung?: string; + + /** Wer war es. Entweder eine Sitzung übergeben oder Akteur einzeln setzen. */ + sitzung?: Sitzung | null; + akteurTyp?: AkteurTyp; + akteurPersonId?: string | null; + akteurName?: string | null; + anmeldeArt?: AnmeldeArt | null; + + vorher?: Record | null; + nachher?: Record | null; + kontext?: Record | null; + + ipAdresse?: string | null; + userAgent?: string | null; +} + +function datensatzBauen(eingabe: ProtokollEingabe): Prisma.AuditLogUncheckedCreateInput { + const abweichung = unterschied(eingabe.vorher, eingabe.nachher); + + const kontext = { + ...(eingabe.kontext ?? {}), + ...(abweichung.felder.length > 0 ? { geaenderteFelder: abweichung.felder } : {}), + }; + + return { + organisationId: eingabe.organisationId, + aktion: eingabe.aktion, + entitaet: eingabe.entitaet, + entitaetId: eingabe.entitaetId ?? null, + beschreibung: eingabe.beschreibung ?? null, + akteurTyp: eingabe.akteurTyp ?? (eingabe.sitzung ? AkteurTyp.PERSON : AkteurTyp.SYSTEM), + akteurPersonId: eingabe.akteurPersonId ?? eingabe.sitzung?.personId ?? null, + akteurName: eingabe.akteurName ?? eingabe.sitzung?.name ?? null, + anmeldeArt: + eingabe.anmeldeArt ?? + (eingabe.sitzung ? (eingabe.sitzung.anmeldeArt as AnmeldeArt) : null), + vorher: abweichung.vorher ?? Prisma.JsonNull, + nachher: abweichung.nachher ?? Prisma.JsonNull, + kontext: Object.keys(kontext).length > 0 ? (fuerProtokoll(kontext) as Prisma.InputJsonValue) : Prisma.JsonNull, + ipAdresse: eingabe.ipAdresse ?? null, + userAgent: eingabe.userAgent?.slice(0, 400) ?? null, + }; +} + +/** + * Schreibt den Eintrag in eine BESTEHENDE Transaktion. So werden Änderung und + * Protokoll gemeinsam wirksam. + * + * @example + * await fuerOrganisation(orgId, async (db) => { + * const vorher = await db.geraet.findUniqueOrThrow({ where: { id } }); + * const nachher = await db.geraet.update({ where: { id }, data }); + * await protokolliereIn(db, { + * organisationId: orgId, aktion: AuditAktion.GEAENDERT, + * entitaet: "Geraet", entitaetId: id, sitzung, vorher, nachher, + * }); + * }); + */ +export async function protokolliereIn( + db: MandantenClient, + eingabe: ProtokollEingabe, +): Promise { + await db.auditLog.create({ data: datensatzBauen(eingabe) }); +} + +/** + * Schreibt den Eintrag in einer eigenen Transaktion. Für Vorgänge ohne + * begleitende Datenänderung — Anmeldungen, Exporte, Dateiabrufe. + */ +export async function protokolliere(eingabe: ProtokollEingabe): Promise { + await fuerOrganisation(eingabe.organisationId, async (db) => { + await protokolliereIn(db, eingabe); + }); +} + +/** + * Anmeldevorgänge. Die laufen, bevor ein Mandantenkontext feststeht, deshalb + * schreibt diese Funktion ausnahmsweise über den Eigentümer-Zugang. + * Fehlschläge dürfen den Anmeldeweg nicht blockieren — deshalb nur geloggt. + */ +export async function protokolliereAnmeldung(eingabe: { + organisationId: string; + personId?: string | null; + name?: string | null; + anmeldeArt: AnmeldeArt; + erfolgreich: boolean; + grund?: string; + ipAdresse?: string | null; + userAgent?: string | null; +}): Promise { + try { + await prismaSystem.auditLog.create({ + data: datensatzBauen({ + organisationId: eingabe.organisationId, + aktion: eingabe.erfolgreich + ? AuditAktion.ANGEMELDET + : AuditAktion.ANMELDUNG_FEHLGESCHLAGEN, + entitaet: "Person", + entitaetId: eingabe.personId ?? null, + akteurTyp: eingabe.anmeldeArt === AnmeldeArt.SYSTEM ? AkteurTyp.SYSTEM : AkteurTyp.PERSON, + akteurPersonId: eingabe.personId ?? null, + akteurName: eingabe.name ?? null, + anmeldeArt: eingabe.anmeldeArt, + beschreibung: eingabe.erfolgreich + ? `Anmeldung über ${eingabe.anmeldeArt}` + : `Anmeldung fehlgeschlagen: ${eingabe.grund ?? "unbekannt"}`, + ipAdresse: eingabe.ipAdresse, + userAgent: eingabe.userAgent, + }), + }); + } catch (fehler) { + console.error("[audit] Anmeldung konnte nicht protokolliert werden:", fehler); + } +} + +/** Die letzten Einträge zu einem Datensatz — für die Verlaufsansicht. */ +export async function verlauf( + organisationId: string, + entitaet: string, + entitaetId: string, + anzahl = 50, +) { + return fuerOrganisation(organisationId, (db) => + db.auditLog.findMany({ + where: { entitaet, entitaetId }, + orderBy: { zeitpunkt: "desc" }, + take: anzahl, + include: { + akteur: { select: { id: true, vorname: true, nachname: true } }, + }, + }), + ); +} diff --git a/src/lib/auth.ts b/src/lib/auth.ts new file mode 100644 index 0000000..6f7f332 --- /dev/null +++ b/src/lib/auth.ts @@ -0,0 +1,613 @@ +import NextAuth, { type NextAuthConfig } from "next-auth"; +import Credentials from "next-auth/providers/credentials"; +import type { Provider } from "next-auth/providers"; +import { AnmeldeArt, type RolleSchluessel } from "@prisma/client"; +import { z } from "zod"; +import { env, oidcGruppenZuordnung, oidcVerfuegbar } from "@/lib/env"; +import { prismaSystem, fuerOrganisation } from "@/lib/db"; +import { pruefen } from "@/lib/passwort"; +import { rfidNormalisieren } from "@/lib/passwort"; +import { protokolliereAnmeldung } from "@/lib/audit"; +import { alsRolle, type Rolle, type Sitzung } from "@/lib/authz"; + +/** + * Anmeldung — drei Wege, ein Sitzungsbegriff. + * + * 1. OIDC gegen authentik — der Regelweg im laufenden Betrieb. + * 2. E-Mail und Passwort — Rückfalloption für den Erststart und für den + * Fall, dass authentik gerade nicht erreichbar ist. + * 3. Terminal (PIN/RFID) — für das Kiosk-Gerät im Gerätehaus. + * + * Alle drei führen zu derselben Sitzung (siehe lib/authz.ts). Die Sitzung liegt + * als signiertes JWT im Cookie — keine Sitzungstabelle, damit ein Neustart der + * Anwendung niemanden abmeldet. + * + * Die Rollen stehen im Token. Damit eine Rollenänderung nicht erst beim + * nächsten Login greift, werden sie bei jedem Zugriff nachgeladen, wenn das + * Token älter als fünf Minuten ist. + */ + +const ROLLEN_NACHLADEN_NACH_MS = 5 * 60 * 1000; + +// ----------------------------------------------------------------------------- +// Hilfsfunktionen +// ----------------------------------------------------------------------------- + +/** Lädt Person samt Rollen. Läuft vor der Mandantenauflösung, daher ohne RLS. */ +async function personMitRollen(personId: string) { + return prismaSystem.person.findFirst({ + where: { id: personId, aktiv: true, geloeschtAm: null }, + include: { + rollen: { + where: { OR: [{ gueltigBis: null }, { gueltigBis: { gt: new Date() } }] }, + include: { rolle: true }, + }, + organisation: { select: { id: true, slug: true, name: true, aktiv: true } }, + }, + }); +} + +function rollenAus( + eintraege: Array<{ rolle: { schluessel: RolleSchluessel } }>, +): Rolle[] { + const rollen = eintraege + .map((eintrag) => alsRolle(eintrag.rolle.schluessel)) + .filter((rolle): rolle is Rolle => rolle !== null); + // Wer gar keine Rolle hat, ist trotzdem Mitglied — sonst sieht er nicht + // einmal die eigenen Daten. + return rollen.length > 0 ? rollen : ["MITGLIED"]; +} + +/** Ist das Konto gerade wegen zu vieler Fehlversuche gesperrt? */ +function istGesperrt(person: { gesperrtBis: Date | null }): boolean { + return person.gesperrtBis !== null && person.gesperrtBis > new Date(); +} + +/** Fehlversuch zählen und ab der Grenze sperren. */ +async function fehlversuchVermerken(personId: string): Promise { + const person = await prismaSystem.person.findUnique({ + where: { id: personId }, + select: { fehlversuche: true }, + }); + const neu = (person?.fehlversuche ?? 0) + 1; + await prismaSystem.person.update({ + where: { id: personId }, + data: { + fehlversuche: neu, + gesperrtBis: + neu >= env.KIOSK_MAX_FEHLVERSUCHE + ? new Date(Date.now() + env.KIOSK_SPERRDAUER * 1000) + : null, + }, + }); +} + +async function anmeldungVermerken(personId: string): Promise { + await prismaSystem.person.update({ + where: { id: personId }, + data: { letzteAnmeldung: new Date(), fehlversuche: 0, gesperrtBis: null }, + }); +} + +// ----------------------------------------------------------------------------- +// Anbieter +// ----------------------------------------------------------------------------- + +const lokaleAnmeldung = z.object({ + email: z.string().email("Bitte eine gültige E-Mail-Adresse eingeben."), + passwort: z.string().min(1, "Bitte das Passwort eingeben."), +}); + +const terminalAnmeldung = z.object({ + geraeteSchluessel: z.string().min(1), + organisationSlug: z.string().min(1), + art: z.enum(["PIN", "RFID"]), + /** Bei PIN zusätzlich die Mitgliedsnummer, bei RFID nur die Kennung. */ + kennung: z.string().min(1), + geheimnis: z.string().min(1), +}); + +/** E-Mail und Passwort — der Rückfallweg. */ +const passwortAnbieter = Credentials({ + id: "passwort", + name: "E-Mail und Passwort", + credentials: { + email: { label: "E-Mail", type: "email" }, + passwort: { label: "Passwort", type: "password" }, + }, + async authorize(eingabe) { + const geprueft = lokaleAnmeldung.safeParse(eingabe); + if (!geprueft.success) return null; + + const { email, passwort } = geprueft.data; + + // Die E-Mail-Suche geht bewusst über alle Wehren — zu diesem Zeitpunkt ist + // der Mandant noch nicht bekannt. + const person = await prismaSystem.person.findFirst({ + where: { email: email.toLowerCase(), aktiv: true, geloeschtAm: null }, + include: { + rollen: { include: { rolle: true } }, + organisation: { select: { id: true, aktiv: true } }, + }, + }); + + if (!person || !person.organisation.aktiv) { + // Trotzdem hashen, damit "Konto gibt es nicht" zeitlich nicht auffällt. + await pruefen(passwort, null); + return null; + } + + if (istGesperrt(person)) { + await protokolliereAnmeldung({ + organisationId: person.organisationId, + personId: person.id, + name: `${person.vorname} ${person.nachname}`, + anmeldeArt: AnmeldeArt.LOKAL, + erfolgreich: false, + grund: "Konto vorübergehend gesperrt", + }); + return null; + } + + const stimmt = await pruefen(passwort, person.passwortHash); + if (!stimmt) { + await fehlversuchVermerken(person.id); + await protokolliereAnmeldung({ + organisationId: person.organisationId, + personId: person.id, + name: `${person.vorname} ${person.nachname}`, + anmeldeArt: AnmeldeArt.LOKAL, + erfolgreich: false, + grund: "Falsches Passwort", + }); + return null; + } + + await anmeldungVermerken(person.id); + await protokolliereAnmeldung({ + organisationId: person.organisationId, + personId: person.id, + name: `${person.vorname} ${person.nachname}`, + anmeldeArt: AnmeldeArt.LOKAL, + erfolgreich: true, + }); + + return { + id: person.id, + name: `${person.vorname} ${person.nachname}`, + email: person.email ?? undefined, + organisationId: person.organisationId, + rollen: rollenAus(person.rollen), + anmeldeArt: "LOKAL" as const, + }; + }, +}); + +/** + * Terminal im Gerätehaus. Zwei Schranken: erst weist sich das GERÄT mit + * KIOSK_DEVICE_SECRET aus, dann die PERSON mit PIN oder RFID-Karte. Ohne die + * erste Schranke nützt eine erratene PIN nichts. + */ +const terminalAnbieter = Credentials({ + id: "terminal", + name: "Terminal im Gerätehaus", + credentials: { + geraeteSchluessel: { label: "Gerät", type: "text" }, + organisationSlug: { label: "Wehr", type: "text" }, + art: { label: "Art", type: "text" }, + kennung: { label: "Kennung", type: "text" }, + geheimnis: { label: "PIN oder Kartenkennung", type: "password" }, + }, + async authorize(eingabe) { + if (!env.KIOSK_ENABLED) return null; + + const geprueft = terminalAnmeldung.safeParse(eingabe); + if (!geprueft.success) return null; + + const { geraeteSchluessel, organisationSlug, art, kennung, geheimnis } = geprueft.data; + + // Erste Schranke: Ist das ein Terminal, das wir kennen? + if (geraeteSchluessel !== env.KIOSK_DEVICE_SECRET) return null; + + const organisation = await prismaSystem.organisation.findFirst({ + where: { slug: organisationSlug, aktiv: true, geloeschtAm: null }, + select: { id: true }, + }); + if (!organisation) return null; + + // Zweite Schranke: die Person. Bei PIN identifiziert die Mitgliedsnummer, + // bei RFID die Kartenkennung. + const tokens = await fuerOrganisation(organisation.id, (db) => + db.ausweisToken.findMany({ + where: { + typ: art, + aktiv: true, + geloeschtAm: null, + gueltigAb: { lte: new Date() }, + OR: [{ gueltigBis: null }, { gueltigBis: { gt: new Date() } }], + ...(art === "PIN" + ? { person: { mitgliedsnummer: kennung, aktiv: true, geloeschtAm: null } } + : { kennzeichen: rfidNormalisieren(kennung).slice(-4) }), + }, + include: { + person: { + include: { rollen: { include: { rolle: true } } }, + }, + }, + }), + ); + + const vergleichswert = art === "RFID" ? rfidNormalisieren(geheimnis) : geheimnis; + + for (const token of tokens) { + if (token.gesperrtBis && token.gesperrtBis > new Date()) continue; + if (!(await pruefen(vergleichswert, token.tokenHash))) continue; + + await fuerOrganisation(organisation.id, (db) => + db.ausweisToken.update({ + where: { id: token.id }, + data: { letzteNutzung: new Date(), fehlversuche: 0, gesperrtBis: null }, + }), + ); + + const anmeldeArt = art === "PIN" ? AnmeldeArt.KIOSK_PIN : AnmeldeArt.KIOSK_RFID; + await protokolliereAnmeldung({ + organisationId: organisation.id, + personId: token.person.id, + name: `${token.person.vorname} ${token.person.nachname}`, + anmeldeArt, + erfolgreich: true, + }); + + return { + id: token.person.id, + name: `${token.person.vorname} ${token.person.nachname}`, + email: token.person.email ?? undefined, + organisationId: organisation.id, + rollen: rollenAus(token.person.rollen), + anmeldeArt: art === "PIN" ? ("KIOSK_PIN" as const) : ("KIOSK_RFID" as const), + }; + } + + // Fehlversuche werden auf allen in Frage kommenden Tokens gezählt. + await fuerOrganisation(organisation.id, async (db) => { + for (const token of tokens) { + const neu = token.fehlversuche + 1; + await db.ausweisToken.update({ + where: { id: token.id }, + data: { + fehlversuche: neu, + gesperrtBis: + neu >= env.KIOSK_MAX_FEHLVERSUCHE + ? new Date(Date.now() + env.KIOSK_SPERRDAUER * 1000) + : null, + }, + }); + } + }); + + await protokolliereAnmeldung({ + organisationId: organisation.id, + anmeldeArt: art === "PIN" ? AnmeldeArt.KIOSK_PIN : AnmeldeArt.KIOSK_RFID, + erfolgreich: false, + grund: `Terminal-Anmeldung fehlgeschlagen (${art})`, + }); + + return null; + }, +}); + +/** + * OIDC gegen authentik. Wird nur eingehängt, wenn Issuer, Client-ID und Secret + * gesetzt sind — sonst bliebe ein Knopf stehen, der ins Leere führt. + */ +function authentikAnbieter(): Provider { + return { + id: "authentik", + name: env.AUTH_OIDC_NAME, + type: "oidc", + issuer: env.AUTH_OIDC_ISSUER, + clientId: env.AUTH_OIDC_CLIENT_ID, + clientSecret: env.AUTH_OIDC_CLIENT_SECRET, + authorization: { params: { scope: "openid email profile" } }, + checks: ["pkce", "state"], + }; +} + +// ----------------------------------------------------------------------------- +// OIDC: Person finden oder anlegen, Rollen aus Gruppen vorbelegen +// ----------------------------------------------------------------------------- + +interface OidcProfil { + sub: string; + email?: string; + name?: string; + given_name?: string; + family_name?: string; + [claim: string]: unknown; +} + +/** + * Verknüpft ein authentik-Konto mit einer Person. Reihenfolge: + * 1. bereits verknüpft (oidcSubject) + * 2. E-Mail-Adresse stimmt überein -> verknüpfen + * 3. AUTH_OIDC_AUTO_PROVISION -> neue Person anlegen + * Sonst: Anmeldung abgelehnt. + */ +async function personAusOidc(profil: OidcProfil): Promise<{ + personId: string; + organisationId: string; + name: string; + rollen: Rolle[]; +} | null> { + const email = profil.email?.toLowerCase(); + + const vorhanden = await prismaSystem.person.findFirst({ + where: { + OR: [{ oidcSubject: profil.sub }, ...(email ? [{ email }] : [])], + geloeschtAm: null, + }, + include: { + rollen: { include: { rolle: true } }, + organisation: { select: { id: true, aktiv: true } }, + }, + }); + + const gruppen = gruppenAusProfil(profil); + + if (vorhanden) { + if (!vorhanden.aktiv || !vorhanden.organisation.aktiv) return null; + + await prismaSystem.person.update({ + where: { id: vorhanden.id }, + data: { + oidcSubject: profil.sub, + oidcGruppen: gruppen, + letzteAnmeldung: new Date(), + fehlversuche: 0, + gesperrtBis: null, + }, + }); + + // Gruppen wirken nur als Vorbelegung: Rollen, die jemand in FLORIAN von + // Hand vergeben hat, bleiben unangetastet. + await gruppenRollenVorbelegen(vorhanden.organisationId, vorhanden.id, gruppen); + + const aktualisiert = await personMitRollen(vorhanden.id); + if (!aktualisiert) return null; + + return { + personId: aktualisiert.id, + organisationId: aktualisiert.organisationId, + name: `${aktualisiert.vorname} ${aktualisiert.nachname}`, + rollen: rollenAus(aktualisiert.rollen), + }; + } + + if (!env.AUTH_OIDC_AUTO_PROVISION) return null; + + const slug = env.AUTH_OIDC_STANDARD_ORGANISATION; + if (!slug) { + console.error( + "[auth] AUTH_OIDC_AUTO_PROVISION=true, aber AUTH_OIDC_STANDARD_ORGANISATION fehlt.", + ); + return null; + } + + const organisation = await prismaSystem.organisation.findFirst({ + where: { slug, aktiv: true, geloeschtAm: null }, + select: { id: true }, + }); + if (!organisation) return null; + + const vorname = profil.given_name ?? profil.name?.split(" ")[0] ?? "Unbekannt"; + const nachname = + profil.family_name ?? profil.name?.split(" ").slice(1).join(" ") ?? "Unbekannt"; + + const neu = await fuerOrganisation(organisation.id, (db) => + db.person.create({ + data: { + organisationId: organisation.id, + vorname, + nachname, + email: email ?? null, + oidcSubject: profil.sub, + oidcGruppen: gruppen, + letzteAnmeldung: new Date(), + }, + }), + ); + + await gruppenRollenVorbelegen(organisation.id, neu.id, gruppen); + + const mitRollen = await personMitRollen(neu.id); + return { + personId: neu.id, + organisationId: organisation.id, + name: `${vorname} ${nachname}`, + rollen: mitRollen ? rollenAus(mitRollen.rollen) : ["MITGLIED"], + }; +} + +function gruppenAusProfil(profil: OidcProfil): string[] { + const claim = env.AUTH_OIDC_GROUP_CLAIM; + if (!claim) return []; + const wert = profil[claim]; + if (Array.isArray(wert)) return wert.map(String); + if (typeof wert === "string") return wert.split(",").map((teil) => teil.trim()); + return []; +} + +/** + * Belegt Rollen aus authentik-Gruppen vor. Bestehende Zuweisungen werden nicht + * angefasst: Was in FLORIAN von Hand gesetzt wurde, ist führend. + */ +async function gruppenRollenVorbelegen( + organisationId: string, + personId: string, + gruppen: string[], +): Promise { + if (gruppen.length === 0) return; + + const zuordnung = oidcGruppenZuordnung(); + if (zuordnung.size === 0) return; + + const gewuenscht = new Set(); + for (const gruppe of gruppen) { + const rolle = zuordnung.get(gruppe.toLowerCase()); + if (rolle) gewuenscht.add(rolle); + } + if (gewuenscht.size === 0) return; + + await fuerOrganisation(organisationId, async (db) => { + const bestehend = await db.personRolle.findMany({ + where: { personId }, + select: { rolleId: true }, + }); + // Nur beim allerersten Mal vorbelegen — danach entscheidet die Wehr. + if (bestehend.length > 0) return; + + const rollen = await db.rolle.findMany({ + where: { + organisationId, + schluessel: { in: [...gewuenscht] as RolleSchluessel[] }, + }, + select: { id: true }, + }); + + if (rollen.length === 0) return; + + await db.personRolle.createMany({ + data: rollen.map((rolle) => ({ + organisationId, + personId, + rolleId: rolle.id, + ausOidcGruppe: true, + })), + skipDuplicates: true, + }); + }); +} + +// ----------------------------------------------------------------------------- +// Auth.js-Konfiguration +// ----------------------------------------------------------------------------- + +function anbieterListe(): Provider[] { + const liste: Provider[] = []; + if (oidcVerfuegbar) liste.push(authentikAnbieter()); + if (env.AUTH_LOCAL_ENABLED) liste.push(passwortAnbieter); + if (env.KIOSK_ENABLED) liste.push(terminalAnbieter); + return liste; +} + +export const authConfig: NextAuthConfig = { + trustHost: env.AUTH_TRUST_HOST, + secret: env.AUTH_SECRET, + session: { strategy: "jwt", maxAge: env.AUTH_SESSION_MAX_AGE }, + pages: { signIn: "/anmelden", error: "/anmelden" }, + providers: anbieterListe(), + + callbacks: { + async signIn({ account, profile }) { + // Die Credentials-Anbieter haben in authorize() bereits alles geprüft. + if (account?.provider !== "authentik") return true; + if (!profile?.sub) return false; + + const ergebnis = await personAusOidc(profile as unknown as OidcProfil); + if (!ergebnis) return false; + + await protokolliereAnmeldung({ + organisationId: ergebnis.organisationId, + personId: ergebnis.personId, + name: ergebnis.name, + anmeldeArt: AnmeldeArt.OIDC, + erfolgreich: true, + }); + return true; + }, + + async jwt({ token, user, account, trigger }) { + // Erste Anmeldung über einen Credentials-Anbieter. + if (user && "organisationId" in user) { + const angemeldet = user as unknown as { + id: string; + name: string; + organisationId: string; + rollen: Rolle[]; + anmeldeArt: Sitzung["anmeldeArt"]; + }; + token.personId = angemeldet.id; + token.organisationId = angemeldet.organisationId; + token.rollen = angemeldet.rollen; + token.anmeldeArt = angemeldet.anmeldeArt; + token.geladenAm = Date.now(); + return token; + } + + // Erste Anmeldung über OIDC — hier steht das Profil im account/profile. + if (account?.provider === "authentik" && token.sub) { + const person = await prismaSystem.person.findFirst({ + where: { oidcSubject: token.sub, geloeschtAm: null }, + include: { rollen: { include: { rolle: true } } }, + }); + if (person) { + token.personId = person.id; + token.organisationId = person.organisationId; + token.rollen = rollenAus(person.rollen); + token.anmeldeArt = "OIDC"; + token.geladenAm = Date.now(); + } + return token; + } + + // Rollen nachladen, damit eine Änderung nicht bis zum nächsten Login wartet. + const veraltet = + typeof token.geladenAm !== "number" || + Date.now() - token.geladenAm > ROLLEN_NACHLADEN_NACH_MS; + + if (trigger === "update" || (veraltet && typeof token.personId === "string")) { + const person = await personMitRollen(token.personId as string); + if (!person) { + // Person deaktiviert oder gelöscht: Sitzung entwerten. + return { ...token, personId: undefined, organisationId: undefined, rollen: [] }; + } + token.rollen = rollenAus(person.rollen); + token.organisationId = person.organisationId; + token.geladenAm = Date.now(); + } + + return token; + }, + + async session({ session, token }) { + session.user = { + ...session.user, + id: (token.personId as string) ?? "", + organisationId: (token.organisationId as string) ?? "", + rollen: (token.rollen as Rolle[]) ?? [], + anmeldeArt: (token.anmeldeArt as Sitzung["anmeldeArt"]) ?? "LOKAL", + }; + return session; + }, + }, +}; + +export const { handlers, auth, signIn, signOut } = NextAuth(authConfig); + +/** + * Die Sitzung, wie der Rest der Anwendung sie erwartet. Gibt null zurück, + * wenn niemand angemeldet ist oder die Sitzung entwertet wurde. + */ +export async function aktuelleSitzung(): Promise { + const sitzung = await auth(); + if (!sitzung?.user?.id || !sitzung.user.organisationId) return null; + + return { + personId: sitzung.user.id, + organisationId: sitzung.user.organisationId, + name: sitzung.user.name ?? "Unbekannt", + rollen: sitzung.user.rollen ?? [], + anmeldeArt: sitzung.user.anmeldeArt ?? "LOKAL", + }; +} diff --git a/src/lib/authz.ts b/src/lib/authz.ts new file mode 100644 index 0000000..7eb99a1 --- /dev/null +++ b/src/lib/authz.ts @@ -0,0 +1,338 @@ +/** + * Zentrale Berechtigungslogik. + * + * Regel aus CLAUDE.md: Keine Rollenprüfung in Komponenten. Alles, was mit + * "darf jemand das?" zu tun hat, steht hier — und nur hier. + * + * Aufbau: + * Recht = ein einzelnes Können, z. B. "geraet.bearbeiten" + * Rolle = ein Bündel von Rechten, z. B. GERAETEWART + * Sitzung = wer gerade angemeldet ist, mit welchen Rollen, in welcher Wehr + * + * Die Rechte einer Rolle sind hier als Vorgabe hinterlegt. Beim Seed werden sie + * in die Tabellen berechtigung/rolle_berechtigung gespiegelt, damit eine Wehr + * sie nachträglich anpassen kann, ohne dass jemand Code ändern muss. + */ + +/** Rollen exakt wie im Prisma-Schema (enum RolleSchluessel). */ +export const ROLLEN = [ + "SUPERADMIN", + "KOMMANDANT", + "GERAETEWART", + "ATEMSCHUTZGERAETEWART", + "KASSIER", + "SCHRIFTFUEHRER", + "AUSBILDER", + "MITGLIED", + "KIOSK", +] as const; + +export type Rolle = (typeof ROLLEN)[number]; + +/** Klartext für die Oberfläche. */ +export const ROLLEN_BEZEICHNUNG: Record = { + SUPERADMIN: "Administrator", + KOMMANDANT: "Kommandant", + GERAETEWART: "Gerätewart", + ATEMSCHUTZGERAETEWART: "Atemschutzgerätewart", + KASSIER: "Kassier", + SCHRIFTFUEHRER: "Schriftführer", + AUSBILDER: "Ausbilder", + MITGLIED: "Mitglied", + KIOSK: "Terminal im Gerätehaus", +}; + +export const ROLLEN_BESCHREIBUNG: Record = { + SUPERADMIN: + "Verwaltet die Installation selbst: Wehren anlegen, Rollen zuschneiden, Systemeinstellungen.", + KOMMANDANT: + "Führt die Wehr. Sieht und bearbeitet alles innerhalb der eigenen Organisation.", + GERAETEWART: + "Verantwortet Fahrzeuge, Geräte und deren Prüfungen nach DGUV.", + ATEMSCHUTZGERAETEWART: + "Verantwortet Atemschutzgeräte, Masken und die Überwachung nach FwDV 7.", + KASSIER: "Führt die Kasse: Buchungen, Beiträge, Rechnungen, Auswertungen.", + SCHRIFTFUEHRER: + "Führt Protokolle, Berichte und die Dokumentation der Wehr.", + AUSBILDER: "Plant Übungen und Lehrgänge, führt Teilnahmenachweise.", + MITGLIED: "Sieht die eigenen Daten, Termine und Qualifikationen.", + KIOSK: + "Terminal im Gerätehaus. Darf nur anmelden, quittieren und erfassen — nie verwalten.", +}; + +// ----------------------------------------------------------------------------- +// Rechte +// ----------------------------------------------------------------------------- + +/** + * Alle Einzelrechte. Schema: . + * Neue Rechte hier ergänzen, dann `npm run db:seed` — der Seed gleicht den + * Katalog in der Datenbank ab. + */ +export const RECHTE = { + // Organisation und System + "organisation.lesen": { bereich: "Organisation", bezeichnung: "Wehrdaten ansehen" }, + "organisation.bearbeiten": { bereich: "Organisation", bezeichnung: "Wehrdaten bearbeiten" }, + "organisation.verwalten": { bereich: "Organisation", bezeichnung: "Wehren anlegen und löschen" }, + "system.einstellungen": { bereich: "System", bezeichnung: "Systemeinstellungen ändern" }, + "system.auditlog": { bereich: "System", bezeichnung: "Audit-Log einsehen" }, + + // Personen + "person.lesen": { bereich: "Personen", bezeichnung: "Mitgliederliste ansehen" }, + "person.eigene": { bereich: "Personen", bezeichnung: "Eigene Daten ansehen und pflegen" }, + "person.bearbeiten": { bereich: "Personen", bezeichnung: "Mitglieder anlegen und bearbeiten" }, + "person.loeschen": { bereich: "Personen", bezeichnung: "Mitglieder austragen" }, + "person.rollen": { bereich: "Personen", bezeichnung: "Rollen zuweisen" }, + "person.ausweis": { bereich: "Personen", bezeichnung: "PIN und RFID-Ausweise verwalten" }, + "person.export": { bereich: "Personen", bezeichnung: "Personendaten exportieren (DSGVO)" }, + + // Fahrzeuge und Geräte + "fahrzeug.lesen": { bereich: "Fahrzeuge", bezeichnung: "Fahrzeuge ansehen" }, + "fahrzeug.bearbeiten": { bereich: "Fahrzeuge", bezeichnung: "Fahrzeuge anlegen und bearbeiten" }, + "geraet.lesen": { bereich: "Geräte", bezeichnung: "Geräte ansehen" }, + "geraet.bearbeiten": { bereich: "Geräte", bezeichnung: "Geräte anlegen und bearbeiten" }, + "geraet.aussondern": { bereich: "Geräte", bezeichnung: "Geräte aussondern oder sperren" }, + + // Prüfungen und Wartung (Phase 2) + "pruefung.lesen": { bereich: "Prüfungen", bezeichnung: "Prüfungen und Fristen ansehen" }, + "pruefung.durchfuehren": { bereich: "Prüfungen", bezeichnung: "Prüfungen durchführen und dokumentieren" }, + "pruefung.planen": { bereich: "Prüfungen", bezeichnung: "Prüftermine und Vorlagen planen" }, + + // Atemschutz nach FwDV 7 (Phase 5) + "atemschutz.lesen": { bereich: "Atemschutz", bezeichnung: "Atemschutzdaten ansehen" }, + "atemschutz.bearbeiten": { bereich: "Atemschutz", bezeichnung: "Atemschutzgeräte und Träger verwalten" }, + "atemschutz.ueberwachung": { bereich: "Atemschutz", bezeichnung: "Atemschutzüberwachung führen" }, + + // Einsätze und Übungen (Phase 3) + "einsatz.lesen": { bereich: "Einsätze", bezeichnung: "Einsätze ansehen" }, + "einsatz.bearbeiten": { bereich: "Einsätze", bezeichnung: "Einsätze erfassen und bearbeiten" }, + "einsatz.bericht": { bereich: "Einsätze", bezeichnung: "Einsatzberichte erstellen" }, + + // Ausbildung und Qualifikationen (Phase 1 und 3) + "ausbildung.lesen": { bereich: "Ausbildung", bezeichnung: "Übungen und Lehrgänge ansehen" }, + "ausbildung.bearbeiten": { bereich: "Ausbildung", bezeichnung: "Übungen planen und Teilnahme erfassen" }, + "qualifikation.lesen": { bereich: "Ausbildung", bezeichnung: "Qualifikationen ansehen" }, + "qualifikation.bearbeiten": { bereich: "Ausbildung", bezeichnung: "Qualifikationen pflegen" }, + + // Kasse — die Rolle KASSIER ist in Phase 0 gefordert, ein Kassenmodul ist + // in Phase-0.md bis Phase-8.md aber nicht vorgesehen. Die Rechte stehen hier + // bereits bereit, damit die Rolle nicht leer bleibt. Siehe docs/ROADMAP.md. + "kasse.lesen": { bereich: "Kasse", bezeichnung: "Buchungen und Auswertungen ansehen" }, + "kasse.bearbeiten": { bereich: "Kasse", bezeichnung: "Buchungen erfassen" }, + "kasse.abschluss": { bereich: "Kasse", bezeichnung: "Kassenabschluss durchführen" }, + + // Dateien + "datei.lesen": { bereich: "Dateien", bezeichnung: "Dateien herunterladen" }, + "datei.hochladen": { bereich: "Dateien", bezeichnung: "Dateien hochladen" }, + "datei.loeschen": { bereich: "Dateien", bezeichnung: "Dateien löschen" }, + + // Terminal im Gerätehaus (Phase 4) + "kiosk.anmelden": { bereich: "Terminal", bezeichnung: "Am Terminal anmelden" }, + "kiosk.erfassen": { bereich: "Terminal", bezeichnung: "Am Terminal Erfassungen vornehmen" }, +} as const; + +export type Recht = keyof typeof RECHTE; + +export const ALLE_RECHTE = Object.keys(RECHTE) as Recht[]; + +// ----------------------------------------------------------------------------- +// Rolle zu Recht +// ----------------------------------------------------------------------------- + +/** Rechte, die jede angemeldete Person hat. */ +const GRUNDRECHTE: Recht[] = [ + "person.eigene", + "organisation.lesen", + "person.lesen", + "fahrzeug.lesen", + "geraet.lesen", + "pruefung.lesen", + "einsatz.lesen", + "ausbildung.lesen", + "qualifikation.lesen", + "datei.lesen", +]; + +/** + * Vorgabe je Rolle. SUPERADMIN steht bewusst nicht drin — der bekommt in + * hatRecht() pauschal alles, damit ein neu ergänztes Recht nicht versehentlich + * am Administrator vorbeigeht. + */ +export const ROLLEN_RECHTE: Record, Recht[]> = { + KOMMANDANT: [ + ...GRUNDRECHTE, + "organisation.bearbeiten", + "system.auditlog", + "person.bearbeiten", + "person.loeschen", + "person.rollen", + "person.ausweis", + "person.export", + "fahrzeug.bearbeiten", + "geraet.bearbeiten", + "geraet.aussondern", + "pruefung.planen", + "pruefung.durchfuehren", + "atemschutz.lesen", + "atemschutz.bearbeiten", + "atemschutz.ueberwachung", + "einsatz.bearbeiten", + "einsatz.bericht", + "ausbildung.bearbeiten", + "qualifikation.bearbeiten", + "kasse.lesen", + "datei.hochladen", + "datei.loeschen", + ], + + GERAETEWART: [ + ...GRUNDRECHTE, + "fahrzeug.bearbeiten", + "geraet.bearbeiten", + "geraet.aussondern", + "pruefung.planen", + "pruefung.durchfuehren", + "datei.hochladen", + ], + + ATEMSCHUTZGERAETEWART: [ + ...GRUNDRECHTE, + "geraet.bearbeiten", + "pruefung.planen", + "pruefung.durchfuehren", + "atemschutz.lesen", + "atemschutz.bearbeiten", + "atemschutz.ueberwachung", + "datei.hochladen", + ], + + KASSIER: [...GRUNDRECHTE, "kasse.lesen", "kasse.bearbeiten", "kasse.abschluss", "datei.hochladen"], + + SCHRIFTFUEHRER: [ + ...GRUNDRECHTE, + "person.bearbeiten", + "einsatz.bearbeiten", + "einsatz.bericht", + "ausbildung.bearbeiten", + "datei.hochladen", + ], + + AUSBILDER: [ + ...GRUNDRECHTE, + "ausbildung.bearbeiten", + "qualifikation.bearbeiten", + "datei.hochladen", + ], + + MITGLIED: [...GRUNDRECHTE], + + // Das Terminal darf bewusst fast nichts. Wer daran arbeitet, meldet sich + // zusätzlich mit PIN oder Ausweis an und handelt dann unter seiner Person. + KIOSK: ["kiosk.anmelden", "kiosk.erfassen", "organisation.lesen"], +}; + +// ----------------------------------------------------------------------------- +// Sitzung und Prüffunktionen +// ----------------------------------------------------------------------------- + +/** Was die Anwendung über den gerade Angemeldeten weiß. */ +export interface Sitzung { + personId: string; + organisationId: string; + name: string; + rollen: Rolle[]; + /** Wie die Anmeldung zustande kam — relevant fürs Audit und für den Kiosk. */ + anmeldeArt: "LOKAL" | "OIDC" | "KIOSK_PIN" | "KIOSK_RFID" | "SYSTEM"; +} + +/** Fehler, den die tRPC-Schicht in ein FORBIDDEN übersetzt. */ +export class KeinZugriffFehler extends Error { + constructor( + public readonly benoetigtesRecht: Recht | Recht[], + nachricht?: string, + ) { + const rechte = Array.isArray(benoetigtesRecht) + ? benoetigtesRecht.join(", ") + : benoetigtesRecht; + super(nachricht ?? `Fehlende Berechtigung: ${rechte}`); + this.name = "KeinZugriffFehler"; + } +} + +/** Alle Rechte, die sich aus den Rollen einer Sitzung ergeben. */ +export function rechteVon(rollen: readonly Rolle[]): Set { + if (rollen.includes("SUPERADMIN")) return new Set(ALLE_RECHTE); + + const gesammelt = new Set(); + for (const rolle of rollen) { + if (rolle === "SUPERADMIN") continue; + for (const recht of ROLLEN_RECHTE[rolle] ?? []) gesammelt.add(recht); + } + return gesammelt; +} + +/** Hat die Sitzung das Recht? Der Administrator hat immer alles. */ +export function hatRecht(sitzung: Sitzung | null, recht: Recht): boolean { + if (!sitzung) return false; + if (sitzung.rollen.includes("SUPERADMIN")) return true; + return rechteVon(sitzung.rollen).has(recht); +} + +/** Mindestens eines der Rechte. */ +export function hatEinesDerRechte(sitzung: Sitzung | null, rechte: Recht[]): boolean { + return rechte.some((recht) => hatRecht(sitzung, recht)); +} + +/** Alle genannten Rechte. */ +export function hatAlleRechte(sitzung: Sitzung | null, rechte: Recht[]): boolean { + return rechte.every((recht) => hatRecht(sitzung, recht)); +} + +/** Wirft, wenn das Recht fehlt. Für Server-Actions und tRPC-Prozeduren. */ +export function verlangeRecht(sitzung: Sitzung | null, recht: Recht): asserts sitzung is Sitzung { + if (!hatRecht(sitzung, recht)) throw new KeinZugriffFehler(recht); +} + +/** Wirft, wenn keines der Rechte vorliegt. */ +export function verlangeEinesDerRechte( + sitzung: Sitzung | null, + rechte: Recht[], +): asserts sitzung is Sitzung { + if (!hatEinesDerRechte(sitzung, rechte)) throw new KeinZugriffFehler(rechte); +} + +/** Hat die Sitzung eine dieser Rollen? Nur für Anzeigezwecke gedacht. */ +export function hatRolle(sitzung: Sitzung | null, ...rollen: Rolle[]): boolean { + if (!sitzung) return false; + return rollen.some((rolle) => sitzung.rollen.includes(rolle)); +} + +/** + * Darf die Sitzung auf diesen Datensatz zugreifen? Ergänzt die Mandanten- + * trennung der Datenbank um eine Prüfung im Code — doppelt hält besser, und + * die Fehlermeldung ist verständlicher als ein leeres Ergebnis. + */ +export function gehoertZurOrganisation( + sitzung: Sitzung | null, + datensatz: { organisationId: string } | null | undefined, +): boolean { + if (!sitzung || !datensatz) return false; + return sitzung.organisationId === datensatz.organisationId; +} + +/** + * Die eigenen Daten darf jeder sehen, auch ohne person.lesen. Andere nur mit + * dem entsprechenden Recht. + */ +export function darfPersonSehen(sitzung: Sitzung | null, personId: string): boolean { + if (!sitzung) return false; + if (sitzung.personId === personId) return true; + return hatRecht(sitzung, "person.lesen"); +} + +/** Wandelt einen beliebigen Text in eine gültige Rolle um, sonst null. */ +export function alsRolle(wert: string | null | undefined): Rolle | null { + if (!wert) return null; + const gross = wert.trim().toUpperCase(); + return (ROLLEN as readonly string[]).includes(gross) ? (gross as Rolle) : null; +} diff --git a/src/lib/db.ts b/src/lib/db.ts new file mode 100644 index 0000000..c52fd6d --- /dev/null +++ b/src/lib/db.ts @@ -0,0 +1,146 @@ +import { PrismaClient } from "@prisma/client"; +import { env } from "@/lib/env"; + +/** + * Datenbankzugriff mit Mandantenkontext. + * + * Es gibt ZWEI Verbindungen, und der Unterschied ist der Kern der + * Mandantentrennung: + * + * prismaApp — verbindet sich als "florian_app". Für diese Rolle ist Row + * Level Security aktiv. Ohne gesetztes + * app.current_organisation liefert jede Abfrage null Zeilen. + * Das ist die Verbindung, über die die Anwendung arbeitet. + * + * prismaSystem — verbindet sich als Eigentümer (DIRECT_DATABASE_URL) und + * umgeht RLS. Das ist nötig für genau die Vorgänge, die VOR + * der Mandantenauflösung stattfinden: die Anmeldung muss eine + * E-Mail-Adresse über alle Wehren hinweg suchen können, weil + * zu dem Zeitpunkt noch niemand weiß, zu welcher Wehr sie + * gehört. Ebenso Wartungsjobs des Workers. + * + * Zugriff auf Fachdaten läuft ausschließlich über fuerOrganisation(). Wer + * prismaSystem in einem Router benutzt, umgeht die Trennung — deshalb heißt die + * Hilfsfunktion dafür ohneMandantenfilter() und verlangt eine Begründung. + */ + +const globalerCache = globalThis as unknown as { + prismaApp?: PrismaClient; + prismaSystem?: PrismaClient; +}; + +function protokollStufen() { + return env.NODE_ENV === "development" + ? ([ + { emit: "stdout", level: "warn" }, + { emit: "stdout", level: "error" }, + ] as const) + : ([{ emit: "stdout", level: "error" }] as const); +} + +function clientAnlegen(url: string): PrismaClient { + return new PrismaClient({ + datasources: { db: { url } }, + log: [...protokollStufen()], + errorFormat: env.NODE_ENV === "development" ? "pretty" : "minimal", + }); +} + +/** + * Anwendungsverbindung mit aktiver Mandantentrennung. + * Nicht direkt verwenden — immer über fuerOrganisation(). + */ +export const prismaApp: PrismaClient = + globalerCache.prismaApp ?? clientAnlegen(env.DATABASE_URL); + +/** + * Eigentümerverbindung ohne Mandantentrennung. + * Nur für Anmeldung, Health-Checks und Wartungsjobs. + */ +export const prismaSystem: PrismaClient = + globalerCache.prismaSystem ?? + clientAnlegen(env.DIRECT_DATABASE_URL ?? env.DATABASE_URL); + +// In der Entwicklung zwischenlagern, damit Hot Reload keine neue +// Verbindungsschar aufmacht. +if (env.NODE_ENV !== "production") { + globalerCache.prismaApp = prismaApp; + globalerCache.prismaSystem = prismaSystem; +} + +/** Der auf eine Wehr eingeschränkte Client, den fuerOrganisation übergibt. */ +export type MandantenClient = Omit< + PrismaClient, + "$connect" | "$disconnect" | "$on" | "$transaction" | "$use" | "$extends" +>; + +/** Prüft grob, ob es überhaupt ein UUID sein kann, bevor es ins SQL geht. */ +const UUID_MUSTER = + /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; + +/** + * Führt fn innerhalb einer Transaktion aus, in der app.current_organisation + * gesetzt ist. SET LOCAL gilt nur bis zum Ende der Transaktion — danach ist der + * Kontext wieder leer, auch wenn die Verbindung in den Pool zurückgeht. + * + * @example + * const personen = await fuerOrganisation(orgId, (db) => + * db.person.findMany({ where: { aktiv: true } }), + * ); + */ +export async function fuerOrganisation( + organisationId: string, + fn: (db: MandantenClient) => Promise, + optionen: { timeoutMs?: number; maxWaitMs?: number } = {}, +): Promise { + if (!UUID_MUSTER.test(organisationId)) { + throw new Error( + `Ungültige Organisations-Kennung: ${organisationId}. Erwartet wird ein UUID.`, + ); + } + + return prismaApp.$transaction( + async (tx) => { + // Parametrisiert, damit kein Wert ungeprüft ins SQL wandert. + // set_config mit is_local=true entspricht SET LOCAL. + await tx.$executeRaw`SELECT set_config('app.current_organisation', ${organisationId}, true)`; + return fn(tx as unknown as MandantenClient); + }, + { + maxWait: optionen.maxWaitMs ?? 5_000, + timeout: optionen.timeoutMs ?? 15_000, + }, + ); +} + +/** + * Zugriff ohne Mandantenfilter — ausdrücklich benannt, damit man beim Lesen + * sofort sieht, dass hier die Trennung umgangen wird. + * + * Zulässig nur für: Anmeldung (E-Mail-Suche über alle Wehren), Health-Checks, + * Wartungsjobs des Workers. Jede Verwendung gehört begründet. + */ +export async function ohneMandantenfilter( + grund: string, + fn: (db: PrismaClient) => Promise, +): Promise { + if (env.NODE_ENV === "development") { + console.warn(`[db] Zugriff ohne Mandantenfilter: ${grund}`); + } + return fn(prismaSystem); +} + +/** Einfache Erreichbarkeitsprüfung für /api/health. */ +export async function datenbankErreichbar(): Promise { + try { + await prismaApp.$queryRaw`SELECT 1`; + return true; + } catch { + return false; + } +} + +/** Verbindungen schließen — für den Worker-Abbruch und für Tests. */ +export async function verbindungenTrennen(): Promise { + await Promise.allSettled([prismaApp.$disconnect(), prismaSystem.$disconnect()]); +} diff --git a/src/lib/env.ts b/src/lib/env.ts new file mode 100644 index 0000000..c80c922 --- /dev/null +++ b/src/lib/env.ts @@ -0,0 +1,173 @@ +import { z } from "zod"; + +/** + * Zentrale Umgebungsvalidierung. + * + * Fehlt oder taugt eine Variable nicht, bricht der Start hier ab — mit einer + * Meldung, die sagt welche. Das ist deutlich angenehmer als ein Dienst, der + * anläuft und beim ersten Datei-Upload merkt, dass MinIO nie konfiguriert war. + * + * Beim Container-Build ist keine echte Umgebung vorhanden; dort setzt das + * Dockerfile SKIP_ENV_VALIDATION=1. + */ + +/** + * Akzeptiert "true"/"1"/"ja" als wahr — Compose liefert alles als Text. + * Der Standardwert wird innerhalb der Umwandlung gesetzt, nicht per .default(): + * .default() erwartet den Eingabetyp (string), nicht das Ergebnis (boolean). + */ +const boolAusText = (standard: boolean) => + z + .string() + .optional() + .transform((wert) => { + if (wert === undefined || wert.trim() === "") return standard; + return ["true", "1", "ja", "yes", "on"].includes(wert.trim().toLowerCase()); + }); + +const zahl = (standard: number) => + z + .string() + .optional() + .transform((wert) => (wert === undefined || wert === "" ? standard : Number(wert))) + .pipe(z.number().int().positive()); + +const schema = z.object({ + NODE_ENV: z.enum(["development", "production", "test"]).default("development"), + TZ: z.string().default("Europe/Berlin"), + APP_URL: z.string().url().default("http://localhost:3000"), + + // Datenbank + DATABASE_URL: z.string().url(), + DIRECT_DATABASE_URL: z.string().url().optional(), + + // Redis / Warteschlangen + REDIS_URL: z.string().url(), + WORKER_CONCURRENCY: zahl(5), + + // Objektspeicher + S3_ENDPOINT: z.string().min(1), + S3_PORT: zahl(9000), + S3_USE_SSL: boolAusText(false), + S3_ACCESS_KEY: z.string().min(1), + S3_SECRET_KEY: z.string().min(1), + S3_REGION: z.string().default("eu-central-1"), + S3_BUCKET: z.string().min(1).default("florian"), + S3_PUBLIC_URL: z.string().url().optional(), + S3_URL_TTL: zahl(900), + + // PDF-Erzeugung + GOTENBERG_URL: z.string().url().optional(), + GOTENBERG_TIMEOUT: zahl(30), + + // Sitzungen + AUTH_SECRET: z.string().min(32, "AUTH_SECRET muss mindestens 32 Zeichen haben"), + AUTH_TRUST_HOST: boolAusText(true), + AUTH_SESSION_MAX_AGE: zahl(28_800), + + // OIDC gegen authentik + AUTH_OIDC_ENABLED: boolAusText(false), + AUTH_OIDC_ISSUER: z.string().optional(), + AUTH_OIDC_CLIENT_ID: z.string().optional(), + AUTH_OIDC_CLIENT_SECRET: z.string().optional(), + AUTH_OIDC_NAME: z.string().default("authentik"), + AUTH_OIDC_GROUP_CLAIM: z.string().default("groups"), + AUTH_OIDC_GROUP_MAPPING: z.string().default(""), + AUTH_OIDC_AUTO_PROVISION: boolAusText(true), + AUTH_OIDC_STANDARD_ORGANISATION: z.string().optional(), + + // Lokaler Login + AUTH_LOCAL_ENABLED: boolAusText(true), + AUTH_BCRYPT_ROUNDS: zahl(12), + + // Kiosk + KIOSK_ENABLED: boolAusText(true), + KIOSK_DEVICE_SECRET: z.string().min(8), + KIOSK_PIN_LAENGE: zahl(6), + KIOSK_MAX_FEHLVERSUCHE: zahl(5), + KIOSK_SPERRDAUER: zahl(300), + KIOSK_IDLE_TIMEOUT: zahl(120), + + // Integrationen (alle optional und abschaltbar) + MAIL_ENABLED: boolAusText(false), + MAIL_HOST: z.string().optional(), + MAIL_PORT: zahl(587), + MAIL_SECURE: boolAusText(false), + MAIL_USER: z.string().optional(), + MAIL_PASSWORD: z.string().optional(), + MAIL_FROM: z.string().optional(), + + TELEGRAM_ENABLED: boolAusText(false), + TELEGRAM_BOT_TOKEN: z.string().optional(), + TELEGRAM_CHAT_ID: z.string().optional(), + + LLM_ENABLED: boolAusText(false), + LLM_BASE_URL: z.string().optional(), + LLM_API_KEY: z.string().optional(), + LLM_MODELL: z.string().optional(), + + // Aufbewahrung + AUFBEWAHRUNG_JAHRE: zahl(10), + AUDIT_AUFBEWAHRUNG_TAGE: zahl(3650), + PERSON_ANONYMISIERUNG_TAGE: zahl(365), +}); + +export type Umgebung = z.infer; + +/** Ersatzwerte für Build und Tests — nie für den echten Betrieb. */ +const platzhalter: Record = { + DATABASE_URL: "postgresql://build:build@localhost:5432/build?schema=public", + REDIS_URL: "redis://localhost:6379", + S3_ENDPOINT: "localhost", + S3_ACCESS_KEY: "build", + S3_SECRET_KEY: "build", + AUTH_SECRET: "build_time_platzhalter_mindestens_32_zeichen", + KIOSK_DEVICE_SECRET: "build_time_platzhalter", +}; + +function lesen(): Umgebung { + const roh = + process.env.SKIP_ENV_VALIDATION === "1" || process.env.NODE_ENV === "test" + ? { ...platzhalter, ...process.env } + : process.env; + + const ergebnis = schema.safeParse(roh); + + if (!ergebnis.success) { + const zeilen = ergebnis.error.issues.map( + (problem) => ` - ${problem.path.join(".")}: ${problem.message}`, + ); + throw new Error( + [ + "Die Umgebungskonfiguration ist unvollständig oder fehlerhaft:", + ...zeilen, + "", + "Vorlage mit Erklärungen zu jeder Variable: .env.example", + ].join("\n"), + ); + } + + return ergebnis.data; +} + +export const env: Umgebung = lesen(); + +/** true, wenn die Anmeldung über authentik vollständig konfiguriert ist. */ +export const oidcVerfuegbar = + env.AUTH_OIDC_ENABLED && + Boolean(env.AUTH_OIDC_ISSUER) && + Boolean(env.AUTH_OIDC_CLIENT_ID) && + Boolean(env.AUTH_OIDC_CLIENT_SECRET); + +/** + * Zuordnung authentik-Gruppe zu FLORIAN-Rolle aus AUTH_OIDC_GROUP_MAPPING. + * Format: "gruppe:ROLLE,gruppe2:ROLLE2". + */ +export function oidcGruppenZuordnung(): Map { + const karte = new Map(); + for (const eintrag of env.AUTH_OIDC_GROUP_MAPPING.split(",")) { + const [gruppe, rolle] = eintrag.split(":").map((teil) => teil?.trim()); + if (gruppe && rolle) karte.set(gruppe.toLowerCase(), rolle.toUpperCase()); + } + return karte; +} diff --git a/src/lib/integrations/llm.ts b/src/lib/integrations/llm.ts new file mode 100644 index 0000000..0f26d94 --- /dev/null +++ b/src/lib/integrations/llm.ts @@ -0,0 +1,106 @@ +import "server-only"; +import { env } from "@/lib/env"; + +/** + * Sprachmodell-Anbindung hinter einer Schnittstelle. + * + * Vorgesehen für: Einsatzberichte aus Stichpunkten formulieren, Prüfmängel + * zusammenfassen, Protokolle vorstrukturieren. Immer als Vorschlag, nie als + * Entscheidung — und immer mit einem Menschen, der gegenzeichnet. + * + * Es wird ausschließlich gegen einen OpenAI-kompatiblen Endpunkt gesprochen, + * der selbst betrieben werden kann (Ollama, vLLM, llama.cpp). Kein Cloud-Zwang, + * keine Daten, die das Gerätehaus verlassen müssen. + */ + +export interface LlmAnfrage { + systemAnweisung: string; + benutzerEingabe: string; + maxToken?: number; + temperatur?: number; +} + +export interface LlmAntwort { + erfolgreich: boolean; + text?: string; + fehler?: string; +} + +export interface LlmDienst { + vervollstaendigen(anfrage: LlmAnfrage): Promise; + verfuegbar(): boolean; +} + +/** Standard, solange LLM_ENABLED=false: liefert eine klare Absage. */ +class AbgeschaltetesLlm implements LlmDienst { + verfuegbar(): boolean { + return false; + } + + async vervollstaendigen(): Promise { + return { + erfolgreich: false, + fehler: "Die Textunterstützung ist in dieser Installation abgeschaltet (LLM_ENABLED=false).", + }; + } +} + +class OpenAiKompatiblesLlm implements LlmDienst { + verfuegbar(): boolean { + return Boolean(env.LLM_BASE_URL && env.LLM_MODELL); + } + + async vervollstaendigen(anfrage: LlmAnfrage): Promise { + if (!this.verfuegbar()) { + return { erfolgreich: false, fehler: "LLM ist nicht vollständig konfiguriert." }; + } + + try { + const antwort = await fetch(`${env.LLM_BASE_URL}/chat/completions`, { + method: "POST", + headers: { + "Content-Type": "application/json", + ...(env.LLM_API_KEY ? { Authorization: `Bearer ${env.LLM_API_KEY}` } : {}), + }, + body: JSON.stringify({ + model: env.LLM_MODELL, + messages: [ + { role: "system", content: anfrage.systemAnweisung }, + { role: "user", content: anfrage.benutzerEingabe }, + ], + max_tokens: anfrage.maxToken ?? 1024, + temperature: anfrage.temperatur ?? 0.3, + }), + }); + + if (!antwort.ok) { + return { erfolgreich: false, fehler: `Endpunkt antwortete mit ${antwort.status}` }; + } + + const daten = (await antwort.json()) as { + choices?: Array<{ message?: { content?: string } }>; + }; + const text = daten.choices?.[0]?.message?.content; + + return text + ? { erfolgreich: true, text } + : { erfolgreich: false, fehler: "Leere Antwort vom Endpunkt." }; + } catch (fehler) { + return { erfolgreich: false, fehler: String(fehler) }; + } + } +} + +let cache: LlmDienst | null = null; + +export function llm(): LlmDienst { + if (!cache) { + cache = env.LLM_ENABLED ? new OpenAiKompatiblesLlm() : new AbgeschaltetesLlm(); + } + return cache; +} + +/** Nur für Tests. */ +export function _llmErsetzen(ersatz: LlmDienst | null): void { + cache = ersatz; +} diff --git a/src/lib/integrations/mail.ts b/src/lib/integrations/mail.ts new file mode 100644 index 0000000..0afdd1e --- /dev/null +++ b/src/lib/integrations/mail.ts @@ -0,0 +1,75 @@ +import "server-only"; +import { env } from "@/lib/env"; + +/** + * E-Mail-Versand hinter einer Schnittstelle. + * + * Regel aus CLAUDE.md: externe Dienste hinter Interfaces in lib/integrations/, + * damit sie im Test mockbar und im Betrieb abschaltbar sind. Ist MAIL_ENABLED + * nicht gesetzt, landet die Nachricht im Log statt beim Empfänger — für + * Entwicklung und Testbetrieb genau richtig. + * + * Die tatsächliche SMTP-Anbindung kommt in Phase 7 (Benachrichtigungen). Bis + * dahin steht hier bewusst nur der Vertrag. + */ + +export interface Nachricht { + an: string | string[]; + betreff: string; + text: string; + html?: string; + antwortAn?: string; + anhaenge?: Array<{ dateiname: string; inhalt: Buffer; mimeTyp: string }>; +} + +export interface MailVersand { + senden(nachricht: Nachricht): Promise<{ erfolgreich: boolean; kennung?: string; fehler?: string }>; + verfuegbar(): boolean; +} + +/** Schreibt statt zu senden. Standard, solange MAIL_ENABLED=false. */ +class ProtokollVersand implements MailVersand { + verfuegbar(): boolean { + return false; + } + + async senden(nachricht: Nachricht) { + const empfaenger = Array.isArray(nachricht.an) ? nachricht.an.join(", ") : nachricht.an; + console.info( + `[mail] (nicht versendet, MAIL_ENABLED=false) an=${empfaenger} betreff="${nachricht.betreff}"`, + ); + return { erfolgreich: true, kennung: "protokoll" }; + } +} + +/** Platzhalter für den echten SMTP-Versand aus Phase 7. */ +class SmtpVersand implements MailVersand { + verfuegbar(): boolean { + return Boolean(env.MAIL_HOST && env.MAIL_FROM); + } + + async senden(nachricht: Nachricht) { + const empfaenger = Array.isArray(nachricht.an) ? nachricht.an.join(", ") : nachricht.an; + console.warn( + `[mail] SMTP-Versand ist erst ab Phase 7 umgesetzt. an=${empfaenger} betreff="${nachricht.betreff}"`, + ); + return { + erfolgreich: false, + fehler: "SMTP-Versand noch nicht umgesetzt (Phase 7).", + }; + } +} + +let versandCache: MailVersand | null = null; + +export function mail(): MailVersand { + if (!versandCache) { + versandCache = env.MAIL_ENABLED ? new SmtpVersand() : new ProtokollVersand(); + } + return versandCache; +} + +/** Nur für Tests: eigene Umsetzung unterschieben. */ +export function _mailErsetzen(ersatz: MailVersand | null): void { + versandCache = ersatz; +} diff --git a/src/lib/integrations/telegram.ts b/src/lib/integrations/telegram.ts new file mode 100644 index 0000000..8079e75 --- /dev/null +++ b/src/lib/integrations/telegram.ts @@ -0,0 +1,77 @@ +import "server-only"; +import { env } from "@/lib/env"; + +/** + * Telegram-Benachrichtigungen hinter einer Schnittstelle — abschaltbar und + * mockbar. Gedacht für Erinnerungen an fällige Prüfungen und, ab Phase 4, + * optionale Alarmhinweise. + * + * ACHTUNG: Telegram ist ein externer Dienst. Es dürfen ausschließlich + * Hinweise ohne personenbezogene Daten hinausgehen ("3 Prüfungen fällig"), + * niemals Namen, Adressen oder Einsatzdetails. + */ + +export interface TelegramVersand { + senden(text: string, chatId?: string): Promise<{ erfolgreich: boolean; fehler?: string }>; + verfuegbar(): boolean; +} + +class ProtokollTelegram implements TelegramVersand { + verfuegbar(): boolean { + return false; + } + + async senden(text: string) { + console.info(`[telegram] (nicht versendet, TELEGRAM_ENABLED=false) ${text}`); + return { erfolgreich: true }; + } +} + +class EchtesTelegram implements TelegramVersand { + verfuegbar(): boolean { + return Boolean(env.TELEGRAM_BOT_TOKEN && env.TELEGRAM_CHAT_ID); + } + + async senden(text: string, chatId?: string) { + if (!this.verfuegbar()) { + return { erfolgreich: false, fehler: "Telegram ist nicht vollständig konfiguriert." }; + } + + try { + const antwort = await fetch( + `https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/sendMessage`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + chat_id: chatId ?? env.TELEGRAM_CHAT_ID, + text, + parse_mode: "HTML", + disable_web_page_preview: true, + }), + }, + ); + + if (!antwort.ok) { + return { erfolgreich: false, fehler: `Telegram antwortete mit ${antwort.status}` }; + } + return { erfolgreich: true }; + } catch (fehler) { + return { erfolgreich: false, fehler: String(fehler) }; + } + } +} + +let cache: TelegramVersand | null = null; + +export function telegram(): TelegramVersand { + if (!cache) { + cache = env.TELEGRAM_ENABLED ? new EchtesTelegram() : new ProtokollTelegram(); + } + return cache; +} + +/** Nur für Tests. */ +export function _telegramErsetzen(ersatz: TelegramVersand | null): void { + cache = ersatz; +} diff --git a/src/lib/passwort.ts b/src/lib/passwort.ts new file mode 100644 index 0000000..031a554 --- /dev/null +++ b/src/lib/passwort.ts @@ -0,0 +1,66 @@ +import "server-only"; +import bcrypt from "bcryptjs"; +import { env } from "@/lib/env"; + +/** + * Alles, was gehasht wird: Passwörter, Terminal-PINs, RFID-Kennungen. + * Immer bcrypt, immer mit dem Kostenfaktor aus AUTH_BCRYPT_ROUNDS, und + * Vergleiche immer über bcrypt.compare — nie per Zeichenkettenvergleich. + */ + +export async function hashen(klartext: string): Promise { + return bcrypt.hash(klartext, env.AUTH_BCRYPT_ROUNDS); +} + +export async function pruefen(klartext: string, hash: string | null | undefined): Promise { + if (!hash) { + // Trotzdem einen Vergleich durchführen, damit ein fehlender Hash zeitlich + // nicht von einem falschen Passwort zu unterscheiden ist. + await bcrypt.compare(klartext, "$2a$12$ungueltigerhashfuerzeitgleichheit000000000000000000000"); + return false; + } + return bcrypt.compare(klartext, hash); +} + +/** + * Mindestanforderungen an ein Passwort. Bewusst schlicht gehalten: Länge + * schlägt Sonderzeichen, und eine Wehr soll sich nicht an Regeln abarbeiten. + */ +export function passwortPruefen(passwort: string): { gueltig: boolean; meldung?: string } { + if (passwort.length < 10) { + return { gueltig: false, meldung: "Das Passwort muss mindestens 10 Zeichen lang sein." }; + } + if (passwort.length > 200) { + return { gueltig: false, meldung: "Das Passwort darf höchstens 200 Zeichen lang sein." }; + } + if (/^\d+$/.test(passwort)) { + return { gueltig: false, meldung: "Das Passwort darf nicht nur aus Ziffern bestehen." }; + } + return { gueltig: true }; +} + +/** Prüft eine Terminal-PIN gegen die konfigurierte Länge. */ +export function pinPruefen(pin: string): { gueltig: boolean; meldung?: string } { + const laenge = env.KIOSK_PIN_LAENGE; + if (!new RegExp(`^\\d{${laenge}}$`).test(pin)) { + return { gueltig: false, meldung: `Die PIN muss aus genau ${laenge} Ziffern bestehen.` }; + } + if (/^(\d)\1+$/.test(pin)) { + return { gueltig: false, meldung: "Die PIN darf nicht aus lauter gleichen Ziffern bestehen." }; + } + const ziffern = pin.split("").map(Number); + const aufsteigend = ziffern.every((z, i) => i === 0 || z === (ziffern[i - 1] ?? 0) + 1); + const absteigend = ziffern.every((z, i) => i === 0 || z === (ziffern[i - 1] ?? 0) - 1); + if (aufsteigend || absteigend) { + return { gueltig: false, meldung: "Die PIN darf keine fortlaufende Ziffernfolge sein." }; + } + return { gueltig: true }; +} + +/** + * Normalisiert eine RFID-/NFC-Kennung, bevor sie gehasht wird. Lesegeräte + * liefern die UID mal mit Doppelpunkten, mal klein-, mal großgeschrieben. + */ +export function rfidNormalisieren(uid: string): string { + return uid.replace(/[\s:-]/g, "").toUpperCase(); +} diff --git a/src/lib/storage.ts b/src/lib/storage.ts new file mode 100644 index 0000000..a44e918 --- /dev/null +++ b/src/lib/storage.ts @@ -0,0 +1,209 @@ +import "server-only"; +import { createHash, randomUUID } from "node:crypto"; +import { Readable } from "node:stream"; +import { Client as MinioClient } from "minio"; +import { env } from "@/lib/env"; + +/** + * Der einzige Weg zu Dateien. + * + * Regel aus CLAUDE.md: Dateizugriff ausschließlich über lib/storage.ts. Kein + * fs.readFile, kein Pfad im Dateisystem, keine Blobs in der Datenbank. Alles + * liegt in MinIO — dadurch überlebt eine Datei den Neuaufbau des Containers, + * lässt sich versionieren und getrennt sichern. + * + * Objektschlüssel folgen immer demselben Muster: + * ///- + * Der Slug vorn macht Sicherungen und Löschungen je Wehr einfach. + */ + +let clientCache: MinioClient | null = null; + +function client(): MinioClient { + if (!clientCache) { + clientCache = new MinioClient({ + endPoint: env.S3_ENDPOINT, + port: env.S3_PORT, + useSSL: env.S3_USE_SSL, + accessKey: env.S3_ACCESS_KEY, + secretKey: env.S3_SECRET_KEY, + region: env.S3_REGION, + }); + } + return clientCache; +} + +export const BUCKET = env.S3_BUCKET; + +/** Zeichen, die in Objektschlüsseln Ärger machen, werden ersetzt. */ +function dateinameBereinigen(name: string): string { + return name + .normalize("NFKD") + .replace(/[̀-ͯ]/g, "") + .replace(/ä/gi, "ae") + .replace(/ö/gi, "oe") + .replace(/ü/gi, "ue") + .replace(/ß/g, "ss") + .replace(/[^a-zA-Z0-9._-]/g, "_") + .replace(/_{2,}/g, "_") + .slice(0, 180); +} + +export interface SchluesselTeile { + organisationSlug: string; + /** Modellname, z. B. "Geraet". Wird kleingeschrieben. */ + entitaet: string; + entitaetId?: string; + dateiname: string; +} + +/** Baut einen eindeutigen Objektschlüssel nach dem oben beschriebenen Muster. */ +export function objektSchluessel({ + organisationSlug, + entitaet, + entitaetId, + dateiname, +}: SchluesselTeile): string { + const teile = [ + organisationSlug, + entitaet.toLowerCase(), + entitaetId ?? "allgemein", + `${randomUUID()}-${dateinameBereinigen(dateiname)}`, + ]; + return teile.join("/"); +} + +export interface HochladenErgebnis { + bucket: string; + objektSchluessel: string; + groesseBytes: number; + /** SHA-256 des Inhalts — erkennt Dubletten, belegt Unverändertheit. */ + pruefsumme: string; + etag: string; +} + +/** + * Legt einen Puffer in MinIO ab und gibt zurück, was in die Datei-Tabelle + * gehört. Der Aufrufer schreibt den Datensatz und den Audit-Eintrag. + */ +export async function hochladen( + schluessel: string, + inhalt: Buffer, + mimeTyp: string, + metadaten: Record = {}, +): Promise { + await bucketSicherstellen(); + + const pruefsumme = createHash("sha256").update(inhalt).digest("hex"); + + const ergebnis = await client().putObject(BUCKET, schluessel, inhalt, inhalt.length, { + "Content-Type": mimeTyp, + // MinIO nimmt eigene Angaben nur mit diesem Präfix an. + "x-amz-meta-pruefsumme": pruefsumme, + ...Object.fromEntries( + Object.entries(metadaten).map(([schlssel, wert]) => [`x-amz-meta-${schlssel}`, wert]), + ), + }); + + return { + bucket: BUCKET, + objektSchluessel: schluessel, + groesseBytes: inhalt.length, + pruefsumme, + etag: ergebnis.etag, + }; +} + +/** Lädt ein Objekt vollständig in den Speicher. Für große Dateien streamen. */ +export async function herunterladen(schluessel: string): Promise { + const strom = await client().getObject(BUCKET, schluessel); + const stuecke: Buffer[] = []; + for await (const stueck of strom) { + stuecke.push(Buffer.isBuffer(stueck) ? stueck : Buffer.from(stueck)); + } + return Buffer.concat(stuecke); +} + +/** Objekt als Strom — für Downloads, die durch die Anwendung durchgereicht werden. */ +export async function alsStrom(schluessel: string): Promise { + return client().getObject(BUCKET, schluessel); +} + +/** + * Zeitlich begrenzter Download-Link. Damit muss die Datei nicht durch die + * Anwendung laufen — der Browser holt sie direkt bei MinIO ab. + * Der Link gilt S3_URL_TTL Sekunden und ist danach wertlos. + */ +export async function downloadLink( + schluessel: string, + dateiname?: string, + gueltigkeitSekunden = env.S3_URL_TTL, +): Promise { + const kopfzeilen: Record = {}; + if (dateiname) { + kopfzeilen["response-content-disposition"] = + `attachment; filename="${dateinameBereinigen(dateiname)}"`; + } + return client().presignedGetObject(BUCKET, schluessel, gueltigkeitSekunden, kopfzeilen); +} + +/** + * Zeitlich begrenzter Upload-Link. Der Browser lädt direkt zu MinIO hoch, + * ohne dass große Dateien durch die Anwendung müssen. + */ +export async function uploadLink( + schluessel: string, + gueltigkeitSekunden = env.S3_URL_TTL, +): Promise { + await bucketSicherstellen(); + return client().presignedPutObject(BUCKET, schluessel, gueltigkeitSekunden); +} + +/** Löscht ein Objekt endgültig. Nur für den Aufbewahrungs-Job des Workers. */ +export async function endgueltigLoeschen(schluessel: string): Promise { + await client().removeObject(BUCKET, schluessel); +} + +/** Gibt es das Objekt und wie groß ist es? */ +export async function objektInfo(schluessel: string) { + try { + const status = await client().statObject(BUCKET, schluessel); + return { + vorhanden: true as const, + groesseBytes: status.size, + geaendertAm: status.lastModified, + mimeTyp: status.metaData?.["content-type"] ?? "application/octet-stream", + etag: status.etag, + }; + } catch { + return { vorhanden: false as const }; + } +} + +/** Kopiert ein Objekt, z. B. beim Duplizieren einer Prüfvorlage. */ +export async function kopieren(vonSchluessel: string, nachSchluessel: string): Promise { + await client().copyObject(BUCKET, nachSchluessel, `/${BUCKET}/${vonSchluessel}`); +} + +/** Legt den Bucket an, falls er fehlt. Idempotent, deshalb überall aufrufbar. */ +export async function bucketSicherstellen(): Promise { + const vorhanden = await client().bucketExists(BUCKET).catch(() => false); + if (!vorhanden) { + await client().makeBucket(BUCKET, env.S3_REGION); + } +} + +/** Erreichbarkeitsprüfung für /api/health. */ +export async function speicherErreichbar(): Promise { + try { + await client().bucketExists(BUCKET); + return true; + } catch { + return false; + } +} + +/** Nur für Tests: setzt den zwischengespeicherten Client zurück. */ +export function _clientZuruecksetzen(): void { + clientCache = null; +} diff --git a/src/lib/utils.ts b/src/lib/utils.ts new file mode 100644 index 0000000..1f6a554 --- /dev/null +++ b/src/lib/utils.ts @@ -0,0 +1,104 @@ +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; +import { formatDistanceToNow } from "date-fns"; +import { de } from "date-fns/locale"; +import { formatInTimeZone } from "date-fns-tz"; + +/** Tailwind-Klassen zusammenführen, spätere gewinnen. */ +export function cn(...eingaben: ClassValue[]) { + return twMerge(clsx(eingaben)); +} + +/** + * Alle Anzeigen in Europe/Berlin. Gespeichert wird in UTC — die Umrechnung + * passiert ausschließlich hier, damit sie nirgends vergessen wird. + */ +export const ZEITZONE = "Europe/Berlin"; + +function alsDate(wert: Date | string | number): Date { + return wert instanceof Date ? wert : new Date(wert); +} + +/** 02.09.2026 */ +export function alsDatum(wert: Date | string | number | null | undefined): string { + if (!wert) return "—"; + return formatInTimeZone(alsDate(wert), ZEITZONE, "dd.MM.yyyy", { locale: de }); +} + +/** 02.09.2026, 14:35 */ +export function alsDatumZeit(wert: Date | string | number | null | undefined): string { + if (!wert) return "—"; + return formatInTimeZone(alsDate(wert), ZEITZONE, "dd.MM.yyyy, HH:mm", { locale: de }); +} + +/** 14:35 */ +export function alsUhrzeit(wert: Date | string | number | null | undefined): string { + if (!wert) return "—"; + return formatInTimeZone(alsDate(wert), ZEITZONE, "HH:mm", { locale: de }); +} + +/** "vor 3 Tagen" */ +export function alsRelativeZeit(wert: Date | string | number | null | undefined): string { + if (!wert) return "—"; + return formatDistanceToNow(alsDate(wert), { addSuffix: true, locale: de }); +} + +/** Nachname, Vorname — so stehen Mitglieder in jeder Liste. */ +export function personName( + person: { vorname: string; nachname: string } | null | undefined, +): string { + if (!person) return "—"; + return `${person.nachname}, ${person.vorname}`; +} + +/** Vorname Nachname — für Anreden und Überschriften. */ +export function personAnrede( + person: { vorname: string; nachname: string } | null | undefined, +): string { + if (!person) return "—"; + return `${person.vorname} ${person.nachname}`; +} + +/** Initialen für den Platzhalter im Profilbild. */ +export function initialen( + person: { vorname: string; nachname: string } | null | undefined, +): string { + if (!person) return "??"; + return `${person.vorname.charAt(0)}${person.nachname.charAt(0)}`.toUpperCase(); +} + +/** 1,4 MB — deutsche Schreibweise mit Komma. */ +export function alsDateigroesse(bytes: number | bigint | null | undefined): string { + if (bytes === null || bytes === undefined) return "—"; + const zahl = typeof bytes === "bigint" ? Number(bytes) : bytes; + if (zahl < 1024) return `${zahl} B`; + const einheiten = ["kB", "MB", "GB", "TB"]; + let wert = zahl / 1024; + let index = 0; + while (wert >= 1024 && index < einheiten.length - 1) { + wert /= 1024; + index += 1; + } + return `${wert.toLocaleString("de-DE", { maximumFractionDigits: 1 })} ${einheiten[index]}`; +} + +/** 1.234,50 € */ +export function alsEuro(betrag: number | null | undefined): string { + if (betrag === null || betrag === undefined) return "—"; + return betrag.toLocaleString("de-DE", { style: "currency", currency: "EUR" }); +} + +/** Erzeugt aus einem Namen einen URL-tauglichen Slug. */ +export function alsSlug(text: string): string { + return text + .toLowerCase() + .replace(/ä/g, "ae") + .replace(/ö/g, "oe") + .replace(/ü/g, "ue") + .replace(/ß/g, "ss") + .normalize("NFKD") + .replace(/[̀-ͯ]/g, "") + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-+|-+$/g, "") + .slice(0, 64); +} diff --git a/src/lib/warteschlange.ts b/src/lib/warteschlange.ts new file mode 100644 index 0000000..40124b8 --- /dev/null +++ b/src/lib/warteschlange.ts @@ -0,0 +1,109 @@ +import "server-only"; +import { Queue, type JobsOptions } from "bullmq"; +import IORedis from "ioredis"; +import { env } from "@/lib/env"; + +/** + * Warteschlangen für Hintergrundarbeit. + * + * Was hier hineingehört: alles, was länger dauert als eine Anfrage dauern darf + * oder nach Plan laufen soll — Fristenberechnung, Erinnerungen, PDF-Erzeugung, + * Exporte, Aufräumjobs. Der Worker in src/worker/index.ts arbeitet sie ab. + * + * Die Anwendung schreibt nur hinein, der Worker liest. So kann man den Worker + * neu starten, ohne dass die Oberfläche stehen bleibt. + */ + +export const WARTESCHLANGE = "florian"; + +/** Bekannte Auftragsarten. Ab Phase 2 kommen die fachlichen Jobs dazu. */ +export const AUFTRAG = { + /** Prüft alle Geräte auf fällige Fristen und setzt den Status. */ + FRISTEN_PRUEFEN: "fristen.pruefen", + /** Verschickt Erinnerungen an fällige Prüfungen. */ + ERINNERUNG_SENDEN: "erinnerung.senden", + /** Erzeugt ein PDF über Gotenberg. */ + PDF_ERZEUGEN: "pdf.erzeugen", + /** Räumt abgelaufene Daten nach den Aufbewahrungsfristen ab. */ + AUFBEWAHRUNG_PRUEFEN: "aufbewahrung.pruefen", +} as const; + +export type AuftragsArt = (typeof AUFTRAG)[keyof typeof AUFTRAG]; + +/** Jeder Auftrag trägt die Wehr mit, für die er läuft. */ +export interface AuftragsDaten { + organisationId: string; + [feld: string]: unknown; +} + +let verbindungCache: IORedis | null = null; +let warteschlangeCache: Queue | null = null; + +export function redis(): IORedis { + if (!verbindungCache) { + verbindungCache = new IORedis(env.REDIS_URL, { + // BullMQ verlangt null, sonst bricht es Aufträge bei Netzhängern ab. + maxRetriesPerRequest: null, + enableReadyCheck: false, + }); + verbindungCache.on("error", (fehler) => { + console.error("[redis] Verbindungsfehler:", fehler.message); + }); + } + return verbindungCache; +} + +export function warteschlange(): Queue { + if (!warteschlangeCache) { + warteschlangeCache = new Queue(WARTESCHLANGE, { + connection: redis(), + defaultJobOptions: { + attempts: 3, + backoff: { type: "exponential", delay: 5_000 }, + removeOnComplete: { age: 3600, count: 500 }, + removeOnFail: { age: 7 * 24 * 3600 }, + }, + }); + } + return warteschlangeCache; +} + +/** Stellt einen Auftrag ein. */ +export async function beauftrage( + art: AuftragsArt, + daten: AuftragsDaten, + optionen: JobsOptions = {}, +): Promise { + const auftrag = await warteschlange().add(art, daten, optionen); + return auftrag.id; +} + +/** Wiederkehrender Auftrag nach Zeitplan (cron-Syntax, Europe/Berlin). */ +export async function beauftrageWiederkehrend( + art: AuftragsArt, + daten: AuftragsDaten, + zeitplan: string, +): Promise { + await warteschlange().add(art, daten, { + repeat: { pattern: zeitplan, tz: "Europe/Berlin" }, + jobId: `${art}:${daten.organisationId}`, + }); +} + +/** Erreichbarkeitsprüfung für /api/health. */ +export async function warteschlangeErreichbar(): Promise { + try { + const antwort = await redis().ping(); + return antwort === "PONG"; + } catch { + return false; + } +} + +/** Verbindungen sauber schließen — für Tests und den Worker-Abbruch. */ +export async function verbindungenSchliessen(): Promise { + await warteschlangeCache?.close(); + verbindungCache?.disconnect(); + warteschlangeCache = null; + verbindungCache = null; +} diff --git a/src/server/context.ts b/src/server/context.ts new file mode 100644 index 0000000..e6b3d77 --- /dev/null +++ b/src/server/context.ts @@ -0,0 +1,32 @@ +import { aktuelleSitzung } from "@/lib/auth"; +import type { Sitzung } from "@/lib/authz"; + +/** + * Kontext jeder tRPC-Anfrage. Enthält bewusst nur, was für JEDE Prozedur + * sinnvoll ist — die Sitzung und die Angaben zur Anfrage fürs Audit. + * Der mandantengebundene Datenbankzugriff kommt in der angemeldet-Middleware + * dazu, damit er ohne Sitzung gar nicht erst existiert. + */ +export interface Kontext { + sitzung: Sitzung | null; + ipAdresse: string | null; + userAgent: string | null; +} + +/** Ermittelt die IP hinter Traefik aus den Weiterleitungs-Kopfzeilen. */ +function ipErmitteln(kopfzeilen: Headers): string | null { + const weitergeleitet = kopfzeilen.get("x-forwarded-for"); + if (weitergeleitet) { + const erste = weitergeleitet.split(",")[0]?.trim(); + if (erste) return erste; + } + return kopfzeilen.get("x-real-ip"); +} + +export async function kontextErzeugen({ req }: { req: Request }): Promise { + return { + sitzung: await aktuelleSitzung(), + ipAdresse: ipErmitteln(req.headers), + userAgent: req.headers.get("user-agent"), + }; +} diff --git a/src/server/routers/_app.ts b/src/server/routers/_app.ts new file mode 100644 index 0000000..e586783 --- /dev/null +++ b/src/server/routers/_app.ts @@ -0,0 +1,106 @@ +import { z } from "zod"; +import { router, oeffentlich, angemeldet, mitRecht } from "@/server/trpc"; +import { ROLLEN_BEZEICHNUNG, rechteVon } from "@/lib/authz"; +import { datenbankErreichbar } from "@/lib/db"; +import { speicherErreichbar } from "@/lib/storage"; + +/** + * Wurzel-Router. Ab Phase 1 kommen hier die Fachbereiche als Unter-Router + * dazu (stammdaten, wartung, atemschutz, einsatz, ausbildung, kasse). + * Phase 0 liefert nur, was zum Prüfen des Fundaments nötig ist. + */ + +const systemRouter = router({ + /** Erreichbarkeit der Randsysteme. Ohne Anmeldung abrufbar. */ + status: oeffentlich.query(async () => { + const [datenbank, speicher] = await Promise.all([ + datenbankErreichbar(), + speicherErreichbar(), + ]); + return { + datenbank, + speicher, + zeitpunkt: new Date(), + zeitzone: "Europe/Berlin", + }; + }), +}); + +const sitzungRouter = router({ + /** Wer bin ich, welcher Wehr gehöre ich an, was darf ich? */ + ich: angemeldet.query(async ({ ctx }) => { + const person = await ctx.db((db) => + db.person.findUnique({ + where: { id: ctx.sitzung.personId }, + select: { + id: true, + vorname: true, + nachname: true, + email: true, + dienstgrad: true, + mitgliedsnummer: true, + letzteAnmeldung: true, + organisation: { select: { id: true, name: true, kurzname: true, slug: true } }, + }, + }), + ); + + return { + person, + rollen: ctx.sitzung.rollen, + rollenBezeichnung: ctx.sitzung.rollen.map((rolle) => ROLLEN_BEZEICHNUNG[rolle]), + rechte: [...rechteVon(ctx.sitzung.rollen)], + anmeldeArt: ctx.sitzung.anmeldeArt, + }; + }), +}); + +const organisationRouter = router({ + /** Stammdaten der eigenen Wehr. */ + eigene: angemeldet.query(async ({ ctx }) => + ctx.db((db) => + db.organisation.findUnique({ + where: { id: ctx.sitzung.organisationId }, + }), + ), + ), + + /** Kennzahlen für die Startseite. */ + kennzahlen: angemeldet.query(async ({ ctx }) => + ctx.db(async (db) => { + const [mitglieder, fahrzeuge, geraete, pruefungFaellig] = await Promise.all([ + db.person.count({ where: { aktiv: true, geloeschtAm: null } }), + db.fahrzeug.count({ where: { aktiv: true, geloeschtAm: null } }), + db.geraet.count({ where: { geloeschtAm: null } }), + db.geraet.count({ where: { status: "PRUEFUNG_FAELLIG", geloeschtAm: null } }), + ]); + return { mitglieder, fahrzeuge, geraete, pruefungFaellig }; + }), + ), +}); + +const auditRouter = router({ + /** Die letzten Einträge im Protokoll — nur mit dem entsprechenden Recht. */ + letzte: mitRecht("system.auditlog") + .input(z.object({ anzahl: z.number().int().min(1).max(200).default(50) }).optional()) + .query(async ({ ctx, input }) => + ctx.db((db) => + db.auditLog.findMany({ + orderBy: { zeitpunkt: "desc" }, + take: input?.anzahl ?? 50, + include: { + akteur: { select: { id: true, vorname: true, nachname: true } }, + }, + }), + ), + ), +}); + +export const appRouter = router({ + system: systemRouter, + sitzung: sitzungRouter, + organisation: organisationRouter, + audit: auditRouter, +}); + +export type AppRouter = typeof appRouter; diff --git a/src/server/trpc.ts b/src/server/trpc.ts new file mode 100644 index 0000000..5c4ff06 --- /dev/null +++ b/src/server/trpc.ts @@ -0,0 +1,99 @@ +import { initTRPC, TRPCError } from "@trpc/server"; +import superjson from "superjson"; +import { ZodError } from "zod"; +import type { Kontext } from "@/server/context"; +import { KeinZugriffFehler, hatRecht, type Recht } from "@/lib/authz"; +import { fuerOrganisation } from "@/lib/db"; + +/** + * tRPC-Grundgerüst. + * + * Drei Arten von Prozeduren: + * oeffentlich — ohne Anmeldung erreichbar (Health, Anmeldestatus) + * angemeldet — verlangt eine Sitzung; stellt db bereit, der bereits auf + * die Wehr des Angemeldeten eingeschränkt ist + * mitRecht(...) — zusätzlich ein bestimmtes Recht aus lib/authz.ts + * + * Wichtig: In den Prozeduren steht ctx.db für den mandantengebundenen Zugriff. + * Wer prismaSystem direkt benutzt, umgeht die Trennung — das soll auffallen. + */ + +const t = initTRPC.context().create({ + // superjson erhaelt Date, BigInt und Decimal auf dem Weg zum Browser. + transformer: superjson, + errorFormatter({ shape, error }) { + return { + ...shape, + data: { + ...shape.data, + // Feldfehler aus Zod direkt an die Oberfläche durchreichen. + zodFehler: + error.cause instanceof ZodError ? error.cause.flatten().fieldErrors : null, + }, + }; + }, +}); + +export const router = t.router; +export const middleware = t.middleware; +export const createCallerFactory = t.createCallerFactory; + +/** Ohne Anmeldung erreichbar. */ +export const oeffentlich = t.procedure; + +/** Übersetzt KeinZugriffFehler in ein sauberes tRPC-FORBIDDEN. */ +const fehlerUebersetzen = middleware(async ({ next }) => { + try { + return await next(); + } catch (fehler) { + if (fehler instanceof KeinZugriffFehler) { + throw new TRPCError({ code: "FORBIDDEN", message: fehler.message, cause: fehler }); + } + throw fehler; + } +}); + +/** Verlangt eine Sitzung und stellt den mandantengebundenen Zugriff bereit. */ +const angemeldetMiddleware = middleware(async ({ ctx, next }) => { + if (!ctx.sitzung) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "Bitte zuerst anmelden.", + }); + } + + const sitzung = ctx.sitzung; + + return next({ + ctx: { + ...ctx, + sitzung, + /** + * Führt eine Abfrage im Mandantenkontext der Sitzung aus. + * @example const personen = await ctx.db((db) => db.person.findMany()); + */ + db: (fn: Parameters>[1]) => + fuerOrganisation(sitzung.organisationId, fn), + }, + }); +}); + +export const angemeldet = t.procedure.use(fehlerUebersetzen).use(angemeldetMiddleware); + +/** + * Prozedur, die ein bestimmtes Recht verlangt. + * @example export const geraeteAendern = mitRecht("geraet.bearbeiten").mutation(...) + */ +export function mitRecht(recht: Recht) { + return angemeldet.use( + middleware(async ({ ctx, next }) => { + if (!hatRecht(ctx.sitzung, recht)) { + throw new TRPCError({ + code: "FORBIDDEN", + message: `Für diesen Vorgang fehlt die Berechtigung "${recht}".`, + }); + } + return next({ ctx }); + }), + ); +} diff --git a/src/types/next-auth.d.ts b/src/types/next-auth.d.ts new file mode 100644 index 0000000..19da6d3 --- /dev/null +++ b/src/types/next-auth.d.ts @@ -0,0 +1,38 @@ +import type { Rolle, Sitzung } from "@/lib/authz"; + +/** + * Erweitert die Typen von Auth.js um das, was FLORIAN zusätzlich in der + * Sitzung führt: Person, Wehr, Rollen und die Art der Anmeldung. + */ +declare module "next-auth" { + interface Session { + user: { + id: string; + name?: string | null; + email?: string | null; + image?: string | null; + organisationId: string; + rollen: Rolle[]; + anmeldeArt: Sitzung["anmeldeArt"]; + }; + } + + interface User { + organisationId?: string; + rollen?: Rolle[]; + anmeldeArt?: Sitzung["anmeldeArt"]; + } +} + +declare module "next-auth/jwt" { + interface JWT { + personId?: string; + organisationId?: string; + rollen?: Rolle[]; + anmeldeArt?: Sitzung["anmeldeArt"]; + /** Zeitpunkt, zu dem die Rollen zuletzt aus der Datenbank kamen. */ + geladenAm?: number; + } +} + +export {}; diff --git a/src/worker/index.ts b/src/worker/index.ts new file mode 100644 index 0000000..f3a1258 --- /dev/null +++ b/src/worker/index.ts @@ -0,0 +1,94 @@ +import { Worker, type Job } from "bullmq"; +import { env } from "@/lib/env"; +import { AUFTRAG, WARTESCHLANGE, redis, type AuftragsDaten } from "@/lib/warteschlange"; +import { verbindungenTrennen } from "@/lib/db"; + +/** + * Hintergrundprozess. + * + * Läuft als eigener Container neben der Anwendung und arbeitet die + * Warteschlange ab. In Phase 0 sind die Auftragsarten angelegt, aber fachlich + * noch leer — die Fristenberechnung kommt in Phase 2, die Erinnerungen in + * Phase 7. Was hier schon steht, ist das Gerüst: Aufnahme, Protokollierung, + * Fehlerbehandlung und ein sauberes Herunterfahren. + */ + +function protokoll(nachricht: string, ...weiteres: unknown[]): void { + console.info(`[worker] ${nachricht}`, ...weiteres); +} + +async function fristenPruefen(auftrag: Job): Promise<{ geprueft: number }> { + protokoll(`Fristen prüfen für Organisation ${auftrag.data.organisationId}`); + // Ab Phase 2: Geräte mit überschrittener Frist auf PRUEFUNG_FAELLIG setzen. + return { geprueft: 0 }; +} + +async function erinnerungSenden(auftrag: Job): Promise<{ versendet: number }> { + protokoll(`Erinnerungen für Organisation ${auftrag.data.organisationId}`); + // Ab Phase 7: Mail und Telegram über lib/integrations. + return { versendet: 0 }; +} + +async function pdfErzeugen(auftrag: Job): Promise<{ erzeugt: boolean }> { + protokoll(`PDF erzeugen für Organisation ${auftrag.data.organisationId}`); + // Ab Phase 2: HTML an Gotenberg, Ergebnis nach MinIO über lib/storage. + return { erzeugt: false }; +} + +async function aufbewahrungPruefen(auftrag: Job): Promise<{ entfernt: number }> { + protokoll(`Aufbewahrungsfristen prüfen für Organisation ${auftrag.data.organisationId}`); + // Ab Phase 8: Audit-Log exportieren und danach abräumen, + // ausgetretene Mitglieder nach PERSON_ANONYMISIERUNG_TAGE anonymisieren. + return { entfernt: 0 }; +} + +const bearbeiter: Record) => Promise> = { + [AUFTRAG.FRISTEN_PRUEFEN]: fristenPruefen, + [AUFTRAG.ERINNERUNG_SENDEN]: erinnerungSenden, + [AUFTRAG.PDF_ERZEUGEN]: pdfErzeugen, + [AUFTRAG.AUFBEWAHRUNG_PRUEFEN]: aufbewahrungPruefen, +}; + +const worker = new Worker( + WARTESCHLANGE, + async (auftrag) => { + const bearbeiten = bearbeiter[auftrag.name]; + if (!bearbeiten) { + throw new Error(`Unbekannte Auftragsart: ${auftrag.name}`); + } + const beginn = Date.now(); + const ergebnis = await bearbeiten(auftrag); + protokoll(`${auftrag.name} erledigt in ${Date.now() - beginn} ms`); + return ergebnis; + }, + { + connection: redis(), + concurrency: env.WORKER_CONCURRENCY, + }, +); + +worker.on("failed", (auftrag, fehler) => { + console.error( + `[worker] Auftrag ${auftrag?.name ?? "?"} (${auftrag?.id ?? "?"}) fehlgeschlagen:`, + fehler.message, + ); +}); + +worker.on("ready", () => { + protokoll( + `bereit — Warteschlange "${WARTESCHLANGE}", ${env.WORKER_CONCURRENCY} parallele Aufträge`, + ); +}); + +/** Laufende Aufträge zu Ende bringen, bevor der Prozess endet. */ +async function herunterfahren(signal: string): Promise { + protokoll(`${signal} empfangen — fahre herunter ...`); + await worker.close(); + await verbindungenTrennen(); + redis().disconnect(); + protokoll("beendet."); + process.exit(0); +} + +process.on("SIGTERM", () => void herunterfahren("SIGTERM")); +process.on("SIGINT", () => void herunterfahren("SIGINT")); diff --git a/tests/e2e/anmeldung.spec.ts b/tests/e2e/anmeldung.spec.ts new file mode 100644 index 0000000..9c401c8 --- /dev/null +++ b/tests/e2e/anmeldung.spec.ts @@ -0,0 +1,96 @@ +import { expect, test } from "@playwright/test"; + +/** + * Beispiel-E2E-Test für Phase 0. + * + * Geprüft wird der Weg, ohne den nichts anderes funktioniert: jemand ruft die + * Anwendung auf, wird zur Anmeldung geschickt, meldet sich an und landet auf + * der Übersicht seiner Wehr. + * + * Voraussetzung: Stack läuft, Datenbank ist migriert und geseedet. + * docker compose up -d && npm run db:migrate && npm run db:seed + */ + +const ADMIN_EMAIL = process.env.SEED_ADMIN_EMAIL ?? "admin@beispielwehr.de"; +const ADMIN_PASSWORT = process.env.SEED_ADMIN_PASSWORT ?? "Feuerwehr2026"; + +test.describe("Anmeldung", () => { + test("schickt nicht angemeldete Aufrufe zur Anmeldeseite", async ({ page }) => { + await page.goto("/"); + await expect(page).toHaveURL(/\/anmelden/); + await expect(page.getByRole("heading", { name: "FLORIAN" })).toBeVisible(); + }); + + test("weist eine falsche Kombination ab und nennt den Grund auf Deutsch", async ({ page }) => { + await page.goto("/anmelden"); + + await page.getByLabel("E-Mail-Adresse").fill(ADMIN_EMAIL); + await page.getByLabel("Passwort").fill("definitiv-das-falsche-passwort"); + await page.getByRole("button", { name: "Anmelden" }).click(); + + // Nicht ueber die Rolle "alert" suchen: Next.js haengt einen eigenen + // Routen-Ansager mit derselben Rolle in die Seite. + const meldung = page.getByTestId("anmeldefehler"); + await expect(meldung).toBeVisible(); + await expect(meldung).toContainText("stimmt nicht"); + // Und niemand ist hineingekommen. + await expect(page).toHaveURL(/\/anmelden/); + }); + + test("lässt den Administrator hinein und zeigt die Übersicht seiner Wehr", async ({ page }) => { + await page.goto("/anmelden"); + + await page.getByLabel("E-Mail-Adresse").fill(ADMIN_EMAIL); + await page.getByLabel("Passwort").fill(ADMIN_PASSWORT); + await page.getByRole("button", { name: "Anmelden" }).click(); + + await page.waitForURL("/"); + + // Die Wehr aus dem Seed. + await expect( + page.getByRole("heading", { name: "Freiwillige Feuerwehr Musterhausen" }), + ).toBeVisible(); + + // Die Kennzahlen aus dem Seed: 15 Mitglieder, 3 Fahrzeuge, 40 Geräte. + await expect(page.getByText("Aktive Mitglieder")).toBeVisible(); + await expect(page.getByText("Fahrzeuge", { exact: true }).first()).toBeVisible(); + await expect(page.getByText("Geräte", { exact: true }).first()).toBeVisible(); + + // Der Sicherheitshinweis muss stehen, solange nichts freigegeben ist. + await expect(page.getByRole("note")).toContainText("analoge Überwachung"); + }); + + test("zeigt die Navigation und markiert künftige Module als noch nicht verfügbar", async ({ + page, + }) => { + await page.goto("/anmelden"); + await page.getByLabel("E-Mail-Adresse").fill(ADMIN_EMAIL); + await page.getByLabel("Passwort").fill(ADMIN_PASSWORT); + await page.getByRole("button", { name: "Anmelden" }).click(); + await page.waitForURL("/"); + + const navigation = page.getByRole("navigation", { name: "Hauptnavigation" }); + await expect(navigation.getByRole("link", { name: "Übersicht" })).toBeVisible(); + + // Atemschutz kommt erst in Phase 3 — der Eintrag ist sichtbar, aber kein Link. + await expect(navigation.getByText("Atemschutz")).toBeVisible(); + await expect(navigation.getByRole("link", { name: "Atemschutz" })).toHaveCount(0); + }); +}); + +test.describe("Erreichbarkeit", () => { + test("meldet den Zustand der Randsysteme unter /api/health", async ({ request }) => { + const antwort = await request.get("/api/health"); + expect(antwort.status()).toBe(200); + + const daten = (await antwort.json()) as { + status: string; + dienste: { datenbank: boolean; speicher: boolean; warteschlange: boolean }; + }; + + expect(daten.status).toBe("ok"); + expect(daten.dienste.datenbank).toBe(true); + expect(daten.dienste.speicher).toBe(true); + expect(daten.dienste.warteschlange).toBe(true); + }); +}); diff --git a/tests/setup.ts b/tests/setup.ts new file mode 100644 index 0000000..b39b2f1 --- /dev/null +++ b/tests/setup.ts @@ -0,0 +1,32 @@ +import "dotenv/config"; + +/** + * Wird vor jedem Testlauf ausgeführt. + * + * Setzt eine vollständige, aber unschädliche Umgebung, damit lib/env.ts + * durchläuft, ohne dass eine echte .env vorhanden sein muss. + * + * Ist eine .env vorhanden, wird sie oben geladen — dann laufen auch die Tests + * mit, die eine echte Datenbank brauchen (Mandantentrennung). Ohne .env greifen + * die Platzhalter, und diese Tests überspringen sich selbst. + */ + +// NODE_ENV ist in den Node-Typen schreibgeschützt, zur Laufzeit aber setzbar. +const umgebung = process.env as Record; + +umgebung.NODE_ENV = "test"; +umgebung.TZ = "Europe/Berlin"; +umgebung.SKIP_ENV_VALIDATION = "1"; + +umgebung.APP_URL ??= "http://localhost:3100"; +umgebung.DATABASE_URL ??= "postgresql://test:test@localhost:5442/test?schema=public"; +umgebung.DIRECT_DATABASE_URL ??= umgebung.DATABASE_URL; +umgebung.REDIS_URL ??= "redis://localhost:6389"; +umgebung.S3_ENDPOINT ??= "localhost"; +umgebung.S3_ACCESS_KEY ??= "test"; +umgebung.S3_SECRET_KEY ??= "test"; +umgebung.S3_BUCKET ??= "test"; +umgebung.AUTH_SECRET ??= "testgeheimnis_mindestens_32_zeichen_lang_ok"; +umgebung.KIOSK_DEVICE_SECRET ??= "testgeraetegeheimnis"; +// bcrypt mit weniger Runden — im Test zählt Tempo, nicht Härte. +umgebung.AUTH_BCRYPT_ROUNDS ??= "4"; diff --git a/tests/unit/authz.test.ts b/tests/unit/authz.test.ts new file mode 100644 index 0000000..d5e1db2 --- /dev/null +++ b/tests/unit/authz.test.ts @@ -0,0 +1,197 @@ +import { describe, expect, it } from "vitest"; +import { + ALLE_RECHTE, + KeinZugriffFehler, + ROLLEN, + ROLLEN_BEZEICHNUNG, + ROLLEN_RECHTE, + alsRolle, + darfPersonSehen, + gehoertZurOrganisation, + hatAlleRechte, + hatEinesDerRechte, + hatRecht, + rechteVon, + verlangeRecht, + type Sitzung, +} from "@/lib/authz"; + +/** + * Beispieltest für Phase 0. + * + * Geprüft wird das, was am teuersten schiefgeht: dass der Gerätewart nicht in + * die Kasse kommt, dass der Administrator wirklich alles darf, und dass das + * Terminal nur das Nötigste kann. + */ + +function sitzung(rollen: Sitzung["rollen"], organisationId = "org-1"): Sitzung { + return { + personId: "person-1", + organisationId, + name: "Testperson", + rollen, + anmeldeArt: "LOKAL", + }; +} + +describe("Rollenkatalog", () => { + it("hat für jede Rolle eine deutsche Bezeichnung", () => { + for (const rolle of ROLLEN) { + expect(ROLLEN_BEZEICHNUNG[rolle]).toBeTruthy(); + } + }); + + it("vergibt in ROLLEN_RECHTE nur Rechte, die es im Katalog gibt", () => { + for (const [rolle, rechte] of Object.entries(ROLLEN_RECHTE)) { + for (const recht of rechte) { + expect(ALLE_RECHTE, `${rolle} verweist auf unbekanntes Recht ${recht}`).toContain( + recht, + ); + } + } + }); +}); + +describe("hatRecht", () => { + it("gibt dem Administrator ausnahmslos alles", () => { + const admin = sitzung(["SUPERADMIN"]); + for (const recht of ALLE_RECHTE) { + expect(hatRecht(admin, recht)).toBe(true); + } + }); + + it("lässt den Gerätewart Geräte bearbeiten", () => { + expect(hatRecht(sitzung(["GERAETEWART"]), "geraet.bearbeiten")).toBe(true); + expect(hatRecht(sitzung(["GERAETEWART"]), "pruefung.durchfuehren")).toBe(true); + }); + + it("lässt den Gerätewart NICHT an die Kasse", () => { + expect(hatRecht(sitzung(["GERAETEWART"]), "kasse.bearbeiten")).toBe(false); + expect(hatRecht(sitzung(["GERAETEWART"]), "kasse.abschluss")).toBe(false); + }); + + it("lässt den Kassier nicht an Geräte und den Atemschutz", () => { + expect(hatRecht(sitzung(["KASSIER"]), "geraet.bearbeiten")).toBe(false); + expect(hatRecht(sitzung(["KASSIER"]), "atemschutz.ueberwachung")).toBe(false); + }); + + it("gibt dem einfachen Mitglied nur Lese- und eigene Rechte", () => { + const mitglied = sitzung(["MITGLIED"]); + expect(hatRecht(mitglied, "person.eigene")).toBe(true); + expect(hatRecht(mitglied, "geraet.lesen")).toBe(true); + expect(hatRecht(mitglied, "person.bearbeiten")).toBe(false); + expect(hatRecht(mitglied, "geraet.bearbeiten")).toBe(false); + }); + + it("beschränkt das Terminal auf Anmelden und Erfassen", () => { + const kiosk = sitzung(["KIOSK"]); + expect(hatRecht(kiosk, "kiosk.anmelden")).toBe(true); + expect(hatRecht(kiosk, "kiosk.erfassen")).toBe(true); + expect(hatRecht(kiosk, "person.bearbeiten")).toBe(false); + expect(hatRecht(kiosk, "system.einstellungen")).toBe(false); + expect(hatRecht(kiosk, "kasse.lesen")).toBe(false); + }); + + it("verweigert ohne Sitzung alles", () => { + for (const recht of ALLE_RECHTE) { + expect(hatRecht(null, recht)).toBe(false); + } + }); + + it("addiert die Rechte mehrerer Rollen", () => { + const doppelrolle = sitzung(["GERAETEWART", "KASSIER"]); + expect(hatRecht(doppelrolle, "geraet.bearbeiten")).toBe(true); + expect(hatRecht(doppelrolle, "kasse.bearbeiten")).toBe(true); + }); +}); + +describe("rechteVon", () => { + it("liefert für den Administrator den vollständigen Katalog", () => { + expect(rechteVon(["SUPERADMIN"]).size).toBe(ALLE_RECHTE.length); + }); + + it("liefert für das Terminal deutlich weniger als für den Kommandanten", () => { + expect(rechteVon(["KIOSK"]).size).toBeLessThan(rechteVon(["KOMMANDANT"]).size); + }); +}); + +describe("hatEinesDerRechte / hatAlleRechte", () => { + const geraetewart = sitzung(["GERAETEWART"]); + + it("genügt ein Treffer bei hatEinesDerRechte", () => { + expect(hatEinesDerRechte(geraetewart, ["kasse.bearbeiten", "geraet.bearbeiten"])).toBe( + true, + ); + }); + + it("verlangt hatAlleRechte jeden einzelnen Treffer", () => { + expect(hatAlleRechte(geraetewart, ["kasse.bearbeiten", "geraet.bearbeiten"])).toBe(false); + expect(hatAlleRechte(geraetewart, ["geraet.lesen", "geraet.bearbeiten"])).toBe(true); + }); +}); + +describe("verlangeRecht", () => { + it("lässt durch, wenn das Recht vorliegt", () => { + expect(() => verlangeRecht(sitzung(["KOMMANDANT"]), "person.bearbeiten")).not.toThrow(); + }); + + it("wirft KeinZugriffFehler, wenn das Recht fehlt", () => { + expect(() => verlangeRecht(sitzung(["MITGLIED"]), "person.bearbeiten")).toThrow( + KeinZugriffFehler, + ); + }); + + it("nennt im Fehler das fehlende Recht", () => { + try { + verlangeRecht(sitzung(["MITGLIED"]), "kasse.abschluss"); + expect.unreachable("hätte werfen müssen"); + } catch (fehler) { + expect(fehler).toBeInstanceOf(KeinZugriffFehler); + expect((fehler as Error).message).toContain("kasse.abschluss"); + } + }); +}); + +describe("Mandantentrennung", () => { + it("erkennt Datensätze der eigenen Wehr", () => { + expect(gehoertZurOrganisation(sitzung(["MITGLIED"], "org-1"), { organisationId: "org-1" })).toBe( + true, + ); + }); + + it("weist Datensätze einer fremden Wehr ab — auch dem Administrator", () => { + expect( + gehoertZurOrganisation(sitzung(["SUPERADMIN"], "org-1"), { organisationId: "org-2" }), + ).toBe(false); + }); + + it("weist null-Datensätze ab", () => { + expect(gehoertZurOrganisation(sitzung(["KOMMANDANT"]), null)).toBe(false); + }); +}); + +describe("darfPersonSehen", () => { + it("lässt jeden die eigenen Daten sehen, auch ohne person.lesen", () => { + const kiosk = sitzung(["KIOSK"]); + expect(hatRecht(kiosk, "person.lesen")).toBe(false); + expect(darfPersonSehen(kiosk, kiosk.personId)).toBe(true); + }); + + it("verlangt für fremde Personen das Recht person.lesen", () => { + expect(darfPersonSehen(sitzung(["KIOSK"]), "andere-person")).toBe(false); + expect(darfPersonSehen(sitzung(["MITGLIED"]), "andere-person")).toBe(true); + }); +}); + +describe("alsRolle", () => { + it("erkennt Rollen unabhängig von Groß- und Kleinschreibung", () => { + expect(alsRolle("kommandant")).toBe("KOMMANDANT"); + expect(alsRolle(" GeraeteWart ")).toBe("GERAETEWART"); + }); + + it("gibt bei Unsinn null zurück", () => { + expect(alsRolle("oberbrandmeister")).toBeNull(); + expect(alsRolle("")).toBeNull(); + expect(alsRolle(null)).toBeNull(); + }); +}); diff --git a/tests/unit/mandantentrennung.test.ts b/tests/unit/mandantentrennung.test.ts new file mode 100644 index 0000000..96ed678 --- /dev/null +++ b/tests/unit/mandantentrennung.test.ts @@ -0,0 +1,226 @@ +import { PrismaClient } from "@prisma/client"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; + +/** + * Prüft die Mandantentrennung dort, wo sie zählt: in der Datenbank. + * + * Der Test legt zwei Wehren an und stellt sicher, dass die Anwendungsrolle + * (florian_app, NOBYPASSRLS) nur die Wehr sieht, die per + * app.current_organisation gesetzt ist — und ohne gesetzten Wert gar nichts. + * + * Er braucht eine laufende Datenbank. Ist keine erreichbar, wird er + * übersprungen statt fehlzuschlagen, damit "npm run test" auch ohne Docker + * durchläuft. + */ + +const APP_URL = process.env.DATABASE_URL ?? ""; +const SYSTEM_URL = process.env.DIRECT_DATABASE_URL ?? APP_URL; + +const app = new PrismaClient({ datasources: { db: { url: APP_URL } } }); +const system = new PrismaClient({ datasources: { db: { url: SYSTEM_URL } } }); + +let erreichbar = false; +let orgA = ""; +let orgB = ""; + +const SLUG_A = "test-rls-wehr-a"; +const SLUG_B = "test-rls-wehr-b"; + +beforeAll(async () => { + try { + await system.$queryRaw`SELECT 1`; + erreichbar = true; + } catch { + return; + } + + const a = await system.organisation.upsert({ + where: { slug: SLUG_A }, + update: {}, + create: { slug: SLUG_A, name: "Testwehr A", kurzname: "TW A" }, + }); + const b = await system.organisation.upsert({ + where: { slug: SLUG_B }, + update: {}, + create: { slug: SLUG_B, name: "Testwehr B", kurzname: "TW B" }, + }); + + orgA = a.id; + orgB = b.id; + + await system.person.upsert({ + where: { organisationId_mitgliedsnummer: { organisationId: orgA, mitgliedsnummer: "T001" } }, + update: {}, + create: { + organisationId: orgA, + mitgliedsnummer: "T001", + vorname: "Anna", + nachname: "AusWehrA", + }, + }); + + await system.person.upsert({ + where: { organisationId_mitgliedsnummer: { organisationId: orgB, mitgliedsnummer: "T001" } }, + update: {}, + create: { + organisationId: orgB, + mitgliedsnummer: "T001", + vorname: "Bertram", + nachname: "AusWehrB", + }, + }); +}); + +afterAll(async () => { + if (erreichbar) { + // Aufräumen über die Systemverbindung. Der Schalter app.audit_bereinigung + // ist nötig, weil die Kaskade auch Audit-Einträge mitnimmt und die sonst + // vom Trigger geschützt sind. + await system.$transaction(async (tx) => { + await tx.$executeRaw`SELECT set_config('app.audit_bereinigung', 'an', true)`; + await tx.organisation.deleteMany({ where: { slug: { in: [SLUG_A, SLUG_B] } } }); + }); + } + await Promise.allSettled([app.$disconnect(), system.$disconnect()]); +}); + +/** Führt eine Abfrage über die Anwendungsrolle im Kontext einer Wehr aus. */ +async function imKontext( + organisationId: string | null, + fn: (db: PrismaClient) => Promise, +): Promise { + return app.$transaction(async (tx) => { + if (organisationId) { + await tx.$executeRaw`SELECT set_config('app.current_organisation', ${organisationId}, true)`; + } + return fn(tx as unknown as PrismaClient); + }); +} + +describe.runIf(process.env.DATABASE_URL)("Row Level Security", () => { + it("liefert ohne gesetzten Mandantenkontext keine Zeilen", async ({ skip }) => { + if (!erreichbar) return skip(); + + const personen = await imKontext(null, (db) => db.person.findMany()); + expect(personen).toHaveLength(0); + + const organisationen = await imKontext(null, (db) => db.organisation.findMany()); + expect(organisationen).toHaveLength(0); + }); + + it("zeigt im Kontext von Wehr A nur Personen aus Wehr A", async ({ skip }) => { + if (!erreichbar) return skip(); + + const personen = await imKontext(orgA, (db) => db.person.findMany()); + expect(personen.length).toBeGreaterThan(0); + expect(personen.every((person) => person.organisationId === orgA)).toBe(true); + expect(personen.some((person) => person.nachname === "AusWehrB")).toBe(false); + }); + + it("zeigt im Kontext von Wehr B nur Personen aus Wehr B", async ({ skip }) => { + if (!erreichbar) return skip(); + + const personen = await imKontext(orgB, (db) => db.person.findMany()); + expect(personen.every((person) => person.organisationId === orgB)).toBe(true); + expect(personen.some((person) => person.nachname === "AusWehrA")).toBe(false); + }); + + it("findet einen Datensatz der fremden Wehr auch bei gezielter Abfrage nicht", async ({ + skip, + }) => { + if (!erreichbar) return skip(); + + const fremde = await system.person.findFirst({ where: { organisationId: orgB } }); + expect(fremde).not.toBeNull(); + + // Dieselbe ID, aber im Kontext der anderen Wehr: RLS blendet sie aus. + const versuch = await imKontext(orgA, (db) => + db.person.findUnique({ where: { id: fremde!.id } }), + ); + expect(versuch).toBeNull(); + }); + + it("lässt keinen Datensatz für eine fremde Wehr anlegen", async ({ skip }) => { + if (!erreichbar) return skip(); + + await expect( + imKontext(orgA, (db) => + db.person.create({ + data: { + organisationId: orgB, + mitgliedsnummer: "T999", + vorname: "Schmuggel", + nachname: "Versuch", + }, + }), + ), + ).rejects.toThrow(); + }); + + it("hält den Audit-Log unveränderlich", async ({ skip }) => { + if (!erreichbar) return skip(); + + const eintrag = await system.auditLog.create({ + data: { + organisationId: orgA, + aktion: "ERSTELLT", + entitaet: "Test", + beschreibung: "Testeintrag für die Unveränderlichkeit", + }, + }); + + // Selbst über die Systemverbindung greift der Trigger. + await expect( + system.auditLog.update({ + where: { id: eintrag.id }, + data: { beschreibung: "nachträglich geändert" }, + }), + ).rejects.toThrow(); + + await expect(system.auditLog.delete({ where: { id: eintrag.id } })).rejects.toThrow(); + }); + + it("erlaubt das Löschen nur im gekennzeichneten Aufbewahrungslauf", async ({ skip }) => { + if (!erreichbar) return skip(); + + const eintrag = await system.auditLog.create({ + data: { + organisationId: orgA, + aktion: "ERSTELLT", + entitaet: "Test", + beschreibung: "Eintrag für den Aufbewahrungslauf", + }, + }); + + await system.$transaction(async (tx) => { + await tx.$executeRaw`SELECT set_config('app.audit_bereinigung', 'an', true)`; + await tx.auditLog.delete({ where: { id: eintrag.id } }); + }); + + const weg = await system.auditLog.findUnique({ where: { id: eintrag.id } }); + expect(weg).toBeNull(); + }); + + it("lässt auch im Aufbewahrungslauf kein UPDATE zu", async ({ skip }) => { + if (!erreichbar) return skip(); + + const eintrag = await system.auditLog.create({ + data: { + organisationId: orgA, + aktion: "ERSTELLT", + entitaet: "Test", + beschreibung: "Eintrag, der nicht verfälscht werden darf", + }, + }); + + await expect( + system.$transaction(async (tx) => { + await tx.$executeRaw`SELECT set_config('app.audit_bereinigung', 'an', true)`; + await tx.auditLog.update({ + where: { id: eintrag.id }, + data: { beschreibung: "verfälscht" }, + }); + }), + ).rejects.toThrow(); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..8903c37 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noUncheckedIndexedAccess": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [{ "name": "next" }], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules", "tests/e2e"] +} diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..be52978 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,25 @@ +import { defineConfig } from "vitest/config"; +import react from "@vitejs/plugin-react"; +import tsconfigPaths from "vite-tsconfig-paths"; + +/** + * Unit- und Integrationstests. + * Die End-to-End-Tests laufen unter tests/e2e mit Playwright und sind hier + * ausgeschlossen — sonst würde Vitest versuchen, sie mitzunehmen. + */ +export default defineConfig({ + plugins: [react(), tsconfigPaths()], + test: { + environment: "node", + globals: true, + setupFiles: ["./tests/setup.ts"], + include: ["tests/**/*.test.{ts,tsx}"], + exclude: ["node_modules", ".next", "tests/e2e/**"], + coverage: { + provider: "v8", + reportsDirectory: "./coverage", + include: ["src/lib/**", "src/server/**"], + exclude: ["**/*.d.ts"], + }, + }, +});

fTO2Zm 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 literal 0 HcmV?d00001 diff --git a/docs/screenshots/02-uebersicht-light.png b/docs/screenshots/02-uebersicht-light.png new file mode 100644 index 0000000000000000000000000000000000000000..b34560850bf46e7d8ed01c7f9502897d1d8bfff8 GIT binary patch literal 193435 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%0DcM!)|1{G~@|pwg6A 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%0A80~N} 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