From 5b3c5ebb2fef080d3c02234be6e4d88a9bc1e75d Mon Sep 17 00:00:00 2001 From: rouggy Date: Sat, 11 Apr 2026 12:12:07 +0200 Subject: [PATCH] up --- .gitignore | 51 + Dockerfile | 24 + Makefile | 133 + README.md | 125 +- build.bat | 10 + cmd/server/main.go | 219 ++ docker-compose.yml | 17 + frontend/package-lock.json | 2522 +++++++++++++++++ frontend/package.json | 28 + frontend/postcss.config.js | 6 + frontend/src/app.css | 8 + frontend/src/app.html | 13 + frontend/src/lib/stores/api.js | 112 + frontend/src/routes/+layout.js | 2 + frontend/src/routes/+layout.svelte | 133 + frontend/src/routes/+page.svelte | 471 +++ frontend/src/routes/calendar/+page.svelte | 275 ++ frontend/src/routes/categories/+page.svelte | 223 ++ frontend/src/routes/documents/+page.svelte | 239 ++ frontend/src/routes/fiscal/+page.svelte | 126 + frontend/src/routes/import/+page.svelte | 896 ++++++ frontend/src/routes/loans/+page.svelte | 350 +++ frontend/src/routes/login/+page.svelte | 36 + frontend/src/routes/profile/+page.svelte | 137 + frontend/src/routes/properties/+page.svelte | 174 ++ frontend/src/routes/transactions/+page.svelte | 704 +++++ frontend/src/routes/users/+page.svelte | 157 + frontend/svelte.config.js | 20 + frontend/tailwind.config.js | 13 + frontend/vite.config.js | 14 + go.mod | 22 + go.sum | 28 + internal/auth/auth.go | 316 +++ internal/calendar/calendar.go | 207 ++ internal/category/category.go | 140 + internal/db/db.go | 150 + internal/document/document.go | 330 +++ internal/fiscal/fiscal.go | 91 + internal/ical/ical.go | 204 ++ internal/importer/qif.go | 307 ++ internal/loan/loan.go | 416 +++ internal/loan/pdf_parser.go | 269 ++ internal/loan/seed.go | 474 ++++ internal/property/property.go | 166 ++ internal/transaction/transaction.go | 483 ++++ web/build/_app/env.js | 1 + .../_app/immutable/assets/0.D9W-GzMQ.css | 1 + web/build/_app/immutable/chunks/2JpTzebs.js | 1 + web/build/_app/immutable/chunks/6cgBTYw-.js | 1 + web/build/_app/immutable/chunks/7IRyKQzz.js | 1 + web/build/_app/immutable/chunks/7MWtQ_HQ.js | 1 + web/build/_app/immutable/chunks/B9ru2HtO.js | 1 + web/build/_app/immutable/chunks/BYJWe3D5.js | 1 + web/build/_app/immutable/chunks/Betcpvy2.js | 1 + web/build/_app/immutable/chunks/BojKV9te.js | 1 + web/build/_app/immutable/chunks/C1tDbyYL.js | 6 + web/build/_app/immutable/chunks/C7zepb3B.js | 1 + web/build/_app/immutable/chunks/CEKnDucw.js | 1 + web/build/_app/immutable/chunks/CImgxE6m.js | 1 + web/build/_app/immutable/chunks/CLS07P5K.js | 1 + web/build/_app/immutable/chunks/CNY58Upt.js | 1 + web/build/_app/immutable/chunks/CUwAs-mp.js | 1 + web/build/_app/immutable/chunks/C_K4PjIw.js | 1 + web/build/_app/immutable/chunks/C_l6I72z.js | 1 + web/build/_app/immutable/chunks/D3KWQg4I.js | 1 + web/build/_app/immutable/chunks/DHedsUi_.js | 1 + web/build/_app/immutable/chunks/DIz1JgvC.js | 1 + web/build/_app/immutable/chunks/Db8cSFEn.js | 1 + web/build/_app/immutable/chunks/DjUyxgK6.js | 1 + web/build/_app/immutable/chunks/G8UKEBBn.js | 1 + web/build/_app/immutable/chunks/eC_GsYA7.js | 1 + web/build/_app/immutable/chunks/oVx0hpG8.js | 1 + web/build/_app/immutable/chunks/zQrtb5Kq.js | 1 + .../_app/immutable/entry/app.CdLU2Zc4.js | 2 + .../_app/immutable/entry/start.C3I7xU3P.js | 1 + web/build/_app/immutable/nodes/0.DFf2-eFR.js | 1 + web/build/_app/immutable/nodes/1.B1rRQsgb.js | 1 + web/build/_app/immutable/nodes/10.B_2_uNhq.js | 1 + web/build/_app/immutable/nodes/11.BwpkXZtE.js | 1 + web/build/_app/immutable/nodes/12.D8a8HBoC.js | 4 + web/build/_app/immutable/nodes/13.D1xscHx8.js | 2 + web/build/_app/immutable/nodes/2.Bm5TUCsw.js | 18 + web/build/_app/immutable/nodes/3.DMsI14gA.js | 1 + web/build/_app/immutable/nodes/4.CoOqOv0Y.js | 7 + web/build/_app/immutable/nodes/5.DZw7u2fg.js | 1 + web/build/_app/immutable/nodes/6.BT2j549n.js | 3 + web/build/_app/immutable/nodes/7.C8SC4dD8.js | 10 + web/build/_app/immutable/nodes/8.By7Q-xW8.js | 5 + web/build/_app/immutable/nodes/9.DcGrZhBA.js | 1 + web/build/_app/version.json | 1 + web/build/index.html | 36 + web/embed.go | 13 + 92 files changed, 10948 insertions(+), 35 deletions(-) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 Makefile create mode 100644 build.bat create mode 100644 cmd/server/main.go create mode 100644 docker-compose.yml create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/postcss.config.js create mode 100644 frontend/src/app.css create mode 100644 frontend/src/app.html create mode 100644 frontend/src/lib/stores/api.js create mode 100644 frontend/src/routes/+layout.js create mode 100644 frontend/src/routes/+layout.svelte create mode 100644 frontend/src/routes/+page.svelte create mode 100644 frontend/src/routes/calendar/+page.svelte create mode 100644 frontend/src/routes/categories/+page.svelte create mode 100644 frontend/src/routes/documents/+page.svelte create mode 100644 frontend/src/routes/fiscal/+page.svelte create mode 100644 frontend/src/routes/import/+page.svelte create mode 100644 frontend/src/routes/loans/+page.svelte create mode 100644 frontend/src/routes/login/+page.svelte create mode 100644 frontend/src/routes/profile/+page.svelte create mode 100644 frontend/src/routes/properties/+page.svelte create mode 100644 frontend/src/routes/transactions/+page.svelte create mode 100644 frontend/src/routes/users/+page.svelte create mode 100644 frontend/svelte.config.js create mode 100644 frontend/tailwind.config.js create mode 100644 frontend/vite.config.js create mode 100644 go.mod create mode 100644 go.sum create mode 100644 internal/auth/auth.go create mode 100644 internal/calendar/calendar.go create mode 100644 internal/category/category.go create mode 100644 internal/db/db.go create mode 100644 internal/document/document.go create mode 100644 internal/fiscal/fiscal.go create mode 100644 internal/ical/ical.go create mode 100644 internal/importer/qif.go create mode 100644 internal/loan/loan.go create mode 100644 internal/loan/pdf_parser.go create mode 100644 internal/loan/seed.go create mode 100644 internal/property/property.go create mode 100644 internal/transaction/transaction.go create mode 100644 web/build/_app/env.js create mode 100644 web/build/_app/immutable/assets/0.D9W-GzMQ.css create mode 100644 web/build/_app/immutable/chunks/2JpTzebs.js create mode 100644 web/build/_app/immutable/chunks/6cgBTYw-.js create mode 100644 web/build/_app/immutable/chunks/7IRyKQzz.js create mode 100644 web/build/_app/immutable/chunks/7MWtQ_HQ.js create mode 100644 web/build/_app/immutable/chunks/B9ru2HtO.js create mode 100644 web/build/_app/immutable/chunks/BYJWe3D5.js create mode 100644 web/build/_app/immutable/chunks/Betcpvy2.js create mode 100644 web/build/_app/immutable/chunks/BojKV9te.js create mode 100644 web/build/_app/immutable/chunks/C1tDbyYL.js create mode 100644 web/build/_app/immutable/chunks/C7zepb3B.js create mode 100644 web/build/_app/immutable/chunks/CEKnDucw.js create mode 100644 web/build/_app/immutable/chunks/CImgxE6m.js create mode 100644 web/build/_app/immutable/chunks/CLS07P5K.js create mode 100644 web/build/_app/immutable/chunks/CNY58Upt.js create mode 100644 web/build/_app/immutable/chunks/CUwAs-mp.js create mode 100644 web/build/_app/immutable/chunks/C_K4PjIw.js create mode 100644 web/build/_app/immutable/chunks/C_l6I72z.js create mode 100644 web/build/_app/immutable/chunks/D3KWQg4I.js create mode 100644 web/build/_app/immutable/chunks/DHedsUi_.js create mode 100644 web/build/_app/immutable/chunks/DIz1JgvC.js create mode 100644 web/build/_app/immutable/chunks/Db8cSFEn.js create mode 100644 web/build/_app/immutable/chunks/DjUyxgK6.js create mode 100644 web/build/_app/immutable/chunks/G8UKEBBn.js create mode 100644 web/build/_app/immutable/chunks/eC_GsYA7.js create mode 100644 web/build/_app/immutable/chunks/oVx0hpG8.js create mode 100644 web/build/_app/immutable/chunks/zQrtb5Kq.js create mode 100644 web/build/_app/immutable/entry/app.CdLU2Zc4.js create mode 100644 web/build/_app/immutable/entry/start.C3I7xU3P.js create mode 100644 web/build/_app/immutable/nodes/0.DFf2-eFR.js create mode 100644 web/build/_app/immutable/nodes/1.B1rRQsgb.js create mode 100644 web/build/_app/immutable/nodes/10.B_2_uNhq.js create mode 100644 web/build/_app/immutable/nodes/11.BwpkXZtE.js create mode 100644 web/build/_app/immutable/nodes/12.D8a8HBoC.js create mode 100644 web/build/_app/immutable/nodes/13.D1xscHx8.js create mode 100644 web/build/_app/immutable/nodes/2.Bm5TUCsw.js create mode 100644 web/build/_app/immutable/nodes/3.DMsI14gA.js create mode 100644 web/build/_app/immutable/nodes/4.CoOqOv0Y.js create mode 100644 web/build/_app/immutable/nodes/5.DZw7u2fg.js create mode 100644 web/build/_app/immutable/nodes/6.BT2j549n.js create mode 100644 web/build/_app/immutable/nodes/7.C8SC4dD8.js create mode 100644 web/build/_app/immutable/nodes/8.By7Q-xW8.js create mode 100644 web/build/_app/immutable/nodes/9.DcGrZhBA.js create mode 100644 web/build/_app/version.json create mode 100644 web/build/index.html create mode 100644 web/embed.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f9c472c --- /dev/null +++ b/.gitignore @@ -0,0 +1,51 @@ +# ── Binaires ───────────────────────────────────────────────────────────────── +*.exe +*.exe~ +*.dll +*.so +*.dylib +RentalManager + +# ── Go ──────────────────────────────────────────────────────────────────────── +/vendor/ +*.test +*.out +/dist/ + +# ── Données applicatives ────────────────────────────────────────────────────── +/data/rental.db +/data/rental.db-wal +/data/rental.db-shm +/data/*.db +/data/documents/ + +# ── Frontend ────────────────────────────────────────────────────────────────── +frontend/node_modules/ +frontend/.svelte-kit/ +frontend/build/ +frontend/.env +frontend/.env.local +frontend/.env.*.local + +# ── Web embed (build artifact) ──────────────────────────────────────────────── +web/dist/ + +# ── Environnement ───────────────────────────────────────────────────────────── +.env +.env.local +.env.production + +# ── OS ──────────────────────────────────────────────────────────────────────── +.DS_Store +Thumbs.db +desktop.ini + +# ── Éditeurs ────────────────────────────────────────────────────────────────── +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# ── Logs ────────────────────────────────────────────────────────────────────── +*.log diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e026494 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,24 @@ +FROM golang:1.22-alpine AS builder +RUN apk add --no-cache gcc musl-dev +WORKDIR /app +COPY go.mod go.sum ./ +RUN go mod download +COPY . . +RUN CGO_ENABLED=1 GOOS=linux go build -o rental-manager ./cmd/server + +FROM node:20-alpine AS frontend-builder +WORKDIR /app/frontend +COPY frontend/package*.json ./ +RUN npm install +COPY frontend/ . +RUN npm run build + +FROM alpine:3.19 +RUN apk add --no-cache ca-certificates tzdata +WORKDIR /app +COPY --from=builder /app/rental-manager . +COPY --from=frontend-builder /app/frontend/build ./frontend/build +RUN mkdir -p /app/data/documents +EXPOSE 8080 +ENV TZ=Europe/Paris +CMD ["./rental-manager"] diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fa9989a --- /dev/null +++ b/Makefile @@ -0,0 +1,133 @@ +# ── Rental Manager — Makefile ───────────────────────────────────────────────── + +APP = RentalManager +GO_CMD = ./cmd/server +DATA_DIR = ./data +FRONTEND_DIR = ./frontend + +ifeq ($(OS),Windows_NT) + BINARY = $(APP).exe +else + BINARY = $(APP) +endif + +.PHONY: help build frontend backend dev-back dev-front \ + clean clean-db clean-all deps tidy test \ + docker-build docker-up docker-down docker-logs docker-restart \ + status version data + +help: ## Affiche cette aide + @echo "" + @echo " Rental Manager" + @echo "" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) \ + | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}' + @echo "" + +# ── Build complet ───────────────────────────────────────────────────────────── + +build: frontend backend ## Build complet (frontend + backend) + +frontend: ## Build le frontend et copie dans web/build + @echo "Building frontend..." + cd $(FRONTEND_DIR) && npm run build + @if exist "web\build" rmdir /s /q "web\build" + mkdir web\build + xcopy /E /I /Y frontend\build web\build + @echo "Frontend built successfully" + +backend: ## Compile le binaire Go + @echo "Building backend..." +ifeq ($(OS),Windows_NT) + go build -ldflags="-s -w -H windowsgui" -o $(BINARY) $(GO_CMD) +else + CGO_ENABLED=0 go build -ldflags="-s -w -H windowsgui" -o $(BINARY) $(GO_CMD) +endif + @echo "Backend built: ./$(BINARY)" + +# ── Développement ───────────────────────────────────────────────────────────── + +dev-back: data ## Lance le backend Go (port 9000) +ifeq ($(OS),Windows_NT) + set PORT=9000& go run $(GO_CMD) +else + PORT=9000 go run $(GO_CMD) +endif + +dev-front: ## Lance le frontend Svelte (port 5173) + cd $(FRONTEND_DIR) && npm run dev + +# ── Dépendances ─────────────────────────────────────────────────────────────── + +deps: ## Installe toutes les dépendances + go mod tidy + cd $(FRONTEND_DIR) && npm install + +tidy: ## go mod tidy + go mod tidy + +# ── Docker ──────────────────────────────────────────────────────────────────── + +docker-build: ## Build l'image Docker + docker compose build + +docker-up: ## Démarre en production (détaché) + docker compose up -d + +docker-down: ## Arrête les conteneurs + docker compose down + +docker-logs: ## Logs en temps réel + docker compose logs -f + +docker-restart: ## Redémarre le conteneur + docker compose restart + +status: ## État des conteneurs + docker compose ps + +# ── Base de données ─────────────────────────────────────────────────────────── + +data: ## Crée les dossiers data/ +ifeq ($(OS),Windows_NT) + @if not exist "$(DATA_DIR)\documents" mkdir "$(DATA_DIR)\documents" +else + @mkdir -p $(DATA_DIR)/documents +endif + +clean-db: ## Supprime la base SQLite (repart de zéro) +ifeq ($(OS),Windows_NT) + @del /f "$(DATA_DIR)\rental.db" 2>nul || echo Fichier absent +else + @rm -f $(DATA_DIR)/rental.db && echo "Base supprimee" +endif + +# ── Nettoyage ───────────────────────────────────────────────────────────────── + +clean: ## Supprime les artefacts de build +ifeq ($(OS),Windows_NT) + @if exist "$(BINARY)" del /f "$(BINARY)" + @if exist "frontend\build" rmdir /s /q "frontend\build" + @if exist "frontend\.svelte-kit" rmdir /s /q "frontend\.svelte-kit" + @if exist "web\build" rmdir /s /q "web\build" +else + @rm -f $(BINARY) + @rm -rf frontend/build frontend/.svelte-kit web/build +endif + +clean-all: clean ## Supprime aussi node_modules +ifeq ($(OS),Windows_NT) + @if exist "frontend\node_modules" rmdir /s /q "frontend\node_modules" +else + @rm -rf frontend/node_modules +endif + +# ── Tests & info ────────────────────────────────────────────────────────────── + +test: ## Lance les tests Go + go test ./... + +version: ## Versions des outils + @go version + @node --version + @npm --version \ No newline at end of file diff --git a/README.md b/README.md index 37de418..6cacfdc 100644 --- a/README.md +++ b/README.md @@ -1,76 +1,131 @@ # Rental Manager -Gestion comptable de biens locatifs — Go + SvelteKit + SQLite. +Application de gestion comptable de biens locatifs — Go + SvelteKit + SQLite. +Interface web embarquée, exécutable unique, sans dépendances externes. + +## Fonctionnalités + +- **Biens immobiliers** — gestion multi-biens (longue durée & Airbnb) +- **Transactions** — revenus & dépenses par bien, catégories fiscales + - Import QIF/QFX depuis votre banque + - Ventilation sur plusieurs biens (split) + - Ventilation mixte revenu/dépense + - Fusion de prélèvements scindés en deux par la banque +- **Prêts immobiliers** — tableaux d'amortissement, split automatique intérêts/capital +- **Calendrier** — grille mensuelle avec sync iCal Airbnb automatique (toutes les heures) +- **Documents** — pièces jointes classées par année/catégorie, export ZIP +- **Export fiscal** — CSV annuel par bien (revenus, charges déductibles) +- **Catégories** — personnalisables, avec indicateur déductibilité fiscale +- **Multi-utilisateurs** — authentification par session ## Stack -- **Backend** : Go 1.22, gorilla/mux, mattn/go-sqlite3 -- **Frontend** : SvelteKit + TailwindCSS + Chart.js -- **BDD** : SQLite (WAL mode) -- **Déploiement** : Docker Compose (ESXi / NAS) +| Composant | Technologie | +|-----------|-------------| +| Backend | Go 1.25, `gorilla/mux`, `modernc.org/sqlite` | +| Frontend | SvelteKit + TailwindCSS + Chart.js + lucide-svelte | +| Base de données | SQLite (mode WAL) | +| Build Windows | `go build -ldflags="-H windowsgui"` | +| Déploiement | Docker Compose | -## Lancement développement +## Développement ```bash -# Backend -cd rental-manager +# Backend (port 9000) go mod tidy go run ./cmd/server -# Frontend (autre terminal) +# Frontend (autre terminal, port 5173) cd frontend npm install npm run dev ``` -## Déploiement production (ESXi) +Le frontend dev proxy les appels `/api/*` vers `:9000`. + +## Build production (Windows) + +```bat +build.bat +``` + +Génère `RentalManager.exe` — interface web embarquée dans le binaire, sans console visible. + +## Déploiement Docker (ESXi / NAS) ```bash docker compose up -d --build ``` -L'application est accessible sur `http://:8080` +Application accessible sur `http://:8080`. ## Structure ``` -cmd/server/main.go Entrypoint Go +cmd/server/main.go Point d'entrée — routes, middlewares, démarrage internal/ - auth/ Authentification, sessions, middleware - property/ Biens immobiliers - transaction/ Revenus & dépenses - calendar/ Événements d'occupation - document/ Pièces jointes + auth/ Authentification, sessions cookie, middleware + property/ Biens immobiliers (CRUD, URL iCal) + transaction/ Revenus & dépenses, split, ventilation mixte + calendar/ Événements d'occupation (CRUD) + ical/ Sync flux iCal Airbnb — goroutine horaire + document/ Upload pièces jointes, export ZIP année/catégorie fiscal/ Export CSV annuel - ical/ Sync flux iCal Airbnb (goroutine) + loan/ Prêts immobiliers, tableaux d'amortissement, split auto + category/ Catégories de transactions + importer/ Import QIF/QFX bancaire db/ SQLite init + migrations -data/ - rental.db Base SQLite (créée au démarrage) - documents/ Fichiers uploadés (property_id/année/) +web/ Frontend SvelteKit embarqué (embed.FS) frontend/src/ - routes/ Pages SvelteKit - lib/stores/api.js Client API + stores Svelte + routes/ + +page.svelte Tableau de bord (graphiques, KPI) + transactions/ Liste & création de transactions + import/ Import bancaire QIF + calendar/ Calendrier mensuel + documents/ Gestion des pièces jointes + loans/ Prêts & amortissement + properties/ Gestion des biens + categories/ Catégories + fiscal/ Export fiscal + profile/ Profil & mot de passe + lib/stores/api.js Client HTTP centralisé +data/ + rental.db Base SQLite (créée au premier démarrage) + documents/ Fichiers uploadés ``` ## Variables d'environnement | Variable | Défaut | Description | |----------|--------|-------------| -| PORT | 9000 | Port d'écoute | -| TZ | Europe/Paris | Timezone | +| `PORT` | `9000` | Port d'écoute | +| `TZ` | `Europe/Paris` | Timezone | -## Premiers pas après démarrage +## Premiers pas -1. Créer le premier utilisateur (endpoint à ajouter ou via SQLite direct) -2. Ajouter les deux biens (apartement longue durée + Airbnb) -3. Renseigner l'URL iCal Airbnb dans la fiche du bien Airbnb -4. La sync démarre automatiquement toutes les heures +1. Lancer l'application — un compte `admin@rental.local` / `admin1234` est créé automatiquement +2. Changer le mot de passe dans **Profil** +3. Créer vos biens dans **Biens** +4. Pour les biens Airbnb : coller l'URL iCal dans la fiche bien + _(Airbnb → Annonce → Paramètres → Calendrier → Exporter le calendrier)_ +5. Configurer vos **Catégories** si besoin +6. Importer vos relevés bancaires via **Import** ## Sync iCal Airbnb -Airbnb expose un flux iCal par annonce : -`Annonce → Paramètres → Calendrier → Exporter le calendrier` +La goroutine `ical.Service` synchronise automatiquement toutes les heures. +Un bouton **Synchroniser** dans le calendrier force la sync immédiate. +Les résultats sont loggués dans la table `ical_sync_log`. -Coller l'URL `.ics` dans la fiche du bien. La goroutine `ical.Service` -synchronise automatiquement toutes les heures et loggue les résultats -dans `ical_sync_log`. +À chaque sync, les anciens événements Airbnb de la propriété sont supprimés et remplacés (ce qui gère aussi les annulations de réservations). + +## Import bancaire + +Formats supportés : **QIF**, **QFX** +_(Espace client → Mes comptes → Télécharger → Format QIF)_ + +Fonctionnalités : +- Détection automatique des lignes déjà importées +- Ventilation sur plusieurs biens +- Ventilation mixte revenu + dépense (ex: loyer reçu + appel de fonds) +- **Fusion** de deux prélèvements scindés par la banque → split intérêts/capital via tableau d'amortissement diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..b1f9714 --- /dev/null +++ b/build.bat @@ -0,0 +1,10 @@ +@echo off +echo Building frontend... +cd frontend +call npm run build +cd .. + +echo Building server... +go build -ldflags="-H windowsgui" -o RentalManager.exe ./cmd/server + +echo Done: RentalManager.exe diff --git a/cmd/server/main.go b/cmd/server/main.go new file mode 100644 index 0000000..53625dc --- /dev/null +++ b/cmd/server/main.go @@ -0,0 +1,219 @@ +package main + +import ( + "encoding/json" + "log" + "net/http" + "os" + "strings" + + "github.com/gorilla/mux" + + "github.com/f4bpo/rental-manager/internal/auth" + "github.com/f4bpo/rental-manager/internal/calendar" + "github.com/f4bpo/rental-manager/internal/category" + "github.com/f4bpo/rental-manager/internal/db" + "github.com/f4bpo/rental-manager/internal/document" + "github.com/f4bpo/rental-manager/internal/fiscal" + "github.com/f4bpo/rental-manager/internal/ical" + "github.com/f4bpo/rental-manager/internal/importer" + "github.com/f4bpo/rental-manager/internal/loan" + "github.com/f4bpo/rental-manager/internal/property" + "github.com/f4bpo/rental-manager/internal/transaction" + "github.com/f4bpo/rental-manager/web" +) + +func corsMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + origin := r.Header.Get("Origin") + if strings.Contains(origin, "localhost") { + w.Header().Set("Access-Control-Allow-Origin", origin) + w.Header().Set("Access-Control-Allow-Credentials", "true") + w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") + } + if r.Method == "OPTIONS" { + w.WriteHeader(http.StatusNoContent) + return + } + next.ServeHTTP(w, r) + }) +} + +func main() { + database, err := db.Init("./data/rental.db") + if err != nil { + log.Fatalf("failed to init database: %v", err) + } + defer database.Close() + + if err := db.Migrate(database); err != nil { + log.Fatalf("failed to run migrations: %v", err) + } + + propertyStore := property.NewStore(database) + transactionStore := transaction.NewStore(database) + documentStore := document.NewStore(database) + documentStore.Migrate() + calendarStore := calendar.NewStore(database) + userStore := auth.NewStore(database) + categoryStore := category.NewStore(database) + + if userStore.Count() == 0 { + u, err := userStore.Create("admin@rental.local", "Administrateur", "admin1234") + if err != nil { + log.Printf("⚠ impossible de créer l'utilisateur par défaut: %v", err) + } else { + log.Printf("✓ Utilisateur par défaut créé — email: %s / mdp: admin1234", u.Email) + } + } + + loanStore := loan.NewStore(database) + loanStore.Migrate() + + // Seed tableaux d'amortissement si pas encore importés + loans, _ := loanStore.ListLoans("") + if len(loans) == 0 { + log.Println("ℹ Tableaux d'amortissement non encore configurés.") + log.Println(" Ajoutez vos prêts dans la page Prêts et uploadez les échéances.") + } + + icalService := ical.NewService(calendarStore, propertyStore) + icalService.StartSync() + + authHandler := auth.NewHandler(userStore) + propertyHandler := property.NewHandler(propertyStore) + transactionHandler := transaction.NewHandler(transactionStore) + documentHandler := document.NewHandler(documentStore, "./data/documents") + calendarHandler := calendar.NewHandler(calendarStore) + fiscalHandler := fiscal.NewHandler(transactionStore, documentStore) + categoryHandler := category.NewHandler(categoryStore) + importHandler := importer.NewHandler(database) + loanHandler := loan.NewHandler(loanStore) + + r := mux.NewRouter() + r.Use(corsMiddleware) + + api := r.PathPrefix("/api").Subrouter() + + api.HandleFunc("/auth/login", authHandler.Login).Methods("POST", "OPTIONS") + api.HandleFunc("/auth/logout", authHandler.Logout).Methods("POST", "OPTIONS") + api.HandleFunc("/auth/register", func(w http.ResponseWriter, r *http.Request) { + if userStore.Count() > 0 { + auth.Middleware(userStore)(http.HandlerFunc(authHandler.Register)).ServeHTTP(w, r) + return + } + authHandler.Register(w, r) + }).Methods("POST", "OPTIONS") + + protected := api.NewRoute().Subrouter() + protected.Use(auth.Middleware(userStore)) + + // Profil & utilisateurs + protected.HandleFunc("/me", authHandler.Me).Methods("GET") + protected.HandleFunc("/me", authHandler.UpdateProfile).Methods("PUT") + protected.HandleFunc("/me/password", authHandler.UpdatePassword).Methods("PUT") + protected.HandleFunc("/users", authHandler.ListUsers).Methods("GET") + protected.HandleFunc("/users/{id}", authHandler.DeleteUser).Methods("DELETE") + + // Catégories + protected.HandleFunc("/categories", categoryHandler.List).Methods("GET") + protected.HandleFunc("/categories", categoryHandler.Create).Methods("POST") + protected.HandleFunc("/categories/{id}", categoryHandler.Update).Methods("PUT") + protected.HandleFunc("/categories/{id}", categoryHandler.Delete).Methods("DELETE") + + // Biens + protected.HandleFunc("/properties", propertyHandler.List).Methods("GET") + protected.HandleFunc("/properties", propertyHandler.Create).Methods("POST") + protected.HandleFunc("/properties/{id}", propertyHandler.Get).Methods("GET") + protected.HandleFunc("/properties/{id}", propertyHandler.Update).Methods("PUT") + protected.HandleFunc("/properties/{id}", propertyHandler.Delete).Methods("DELETE") + + // Transactions + protected.HandleFunc("/transactions", transactionHandler.List).Methods("GET") + protected.HandleFunc("/transactions", transactionHandler.Create).Methods("POST") + protected.HandleFunc("/transactions/summary", transactionHandler.Summary).Methods("GET") + protected.HandleFunc("/transactions/monthly", transactionHandler.Monthly).Methods("GET") + protected.HandleFunc("/transactions/categories", transactionHandler.CategoryBreakdown).Methods("GET") + protected.HandleFunc("/transactions/{id}", transactionHandler.Get).Methods("GET") + protected.HandleFunc("/transactions/{id}", transactionHandler.Update).Methods("PUT") + protected.HandleFunc("/transactions/{id}", transactionHandler.Delete).Methods("DELETE") + protected.HandleFunc("/transactions/{id}/split", transactionHandler.SplitTransaction).Methods("POST") + + // Import QIF + protected.HandleFunc("/import/preview", importHandler.Preview).Methods("POST") + protected.HandleFunc("/import/check", importHandler.Check).Methods("POST") + protected.HandleFunc("/import/qif", importHandler.Import).Methods("POST") + + // Documents + protected.HandleFunc("/documents", documentHandler.List).Methods("GET") + protected.HandleFunc("/documents", documentHandler.Upload).Methods("POST") + protected.HandleFunc("/documents/export", documentHandler.Export).Methods("GET") + protected.HandleFunc("/documents/{id}", documentHandler.Get).Methods("GET") + protected.HandleFunc("/documents/{id}", documentHandler.Delete).Methods("DELETE") + protected.HandleFunc("/documents/{id}/download", documentHandler.Download).Methods("GET") + + // Calendrier + protected.HandleFunc("/calendar", calendarHandler.List).Methods("GET") + protected.HandleFunc("/calendar", calendarHandler.CreateEvent).Methods("POST") + protected.HandleFunc("/calendar/stats", calendarHandler.Stats).Methods("GET") + protected.HandleFunc("/calendar/sync", func(w http.ResponseWriter, r *http.Request) { + results := icalService.SyncAll() + if results == nil { + results = []ical.SyncResult{} + } + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(results) + }).Methods("POST") + protected.HandleFunc("/calendar/{id}", calendarHandler.UpdateEvent).Methods("PUT") + protected.HandleFunc("/calendar/{id}", calendarHandler.DeleteEvent).Methods("DELETE") + + // Prêts immobiliers + protected.HandleFunc("/loans", loanHandler.ListLoans).Methods("GET") + protected.HandleFunc("/loans", loanHandler.CreateLoan).Methods("POST") + // Routes statiques AVANT les routes avec {id} + protected.HandleFunc("/loans/split", loanHandler.GetSplitForAmount).Methods("GET") + protected.HandleFunc("/loans/upload-pdf", loanHandler.UploadPDF).Methods("POST") + protected.HandleFunc("/loans/create", loanHandler.CreateLoanManual).Methods("POST") + // Routes avec paramètre {id} + protected.HandleFunc("/loans/{id}", loanHandler.DeleteLoan).Methods("DELETE") + protected.HandleFunc("/loans/{id}/lines", loanHandler.GetLines).Methods("GET") + protected.HandleFunc("/loans/{id}/lines", loanHandler.UploadLines).Methods("POST") + protected.HandleFunc("/loans/{id}/split", loanHandler.SplitByDate).Methods("GET") + protected.HandleFunc("/loans/{id}/summary", loanHandler.AnnualSummary).Methods("GET") + protected.HandleFunc("/loans/{id}/reload", loanHandler.ReloadLines).Methods("POST") + + // Export fiscal + protected.HandleFunc("/fiscal/export", fiscalHandler.Export).Methods("GET") + protected.HandleFunc("/fiscal/summary", fiscalHandler.Summary).Methods("GET") + + // Arrêt du serveur + protected.HandleFunc("/shutdown", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusNoContent) + go func() { os.Exit(0) }() + }).Methods("POST") + + // Frontend embarqué + assets, err := web.Assets() + if err != nil { + log.Fatalf("embed frontend: %v", err) + } + fileServer := http.FileServer(http.FS(assets)) + r.PathPrefix("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + path := strings.TrimPrefix(r.URL.Path, "/") + if path == "" { + path = "index.html" + } + if _, err := assets.Open(path); err != nil { + r.URL.Path = "/" + } + fileServer.ServeHTTP(w, r) + }) + + port := os.Getenv("PORT") + if port == "" { + port = "9000" + } + log.Printf("🏠 Rental Manager démarré sur http://localhost:%s", port) + log.Fatal(http.ListenAndServe(":"+port, r)) +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..624fc1a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +services: + rental-manager: + build: . + container_name: rental-manager + restart: unless-stopped + ports: + - "8080:8080" + volumes: + - ./data:/app/data # SQLite + documents persistants + environment: + - PORT=8080 + - TZ=Europe/Paris + healthcheck: + test: ["CMD", "wget", "-qO-", "http://localhost:8080/api/properties"] + interval: 30s + timeout: 5s + retries: 3 diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..2fd27c6 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,2522 @@ +{ + "name": "rental-manager-frontend", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "rental-manager-frontend", + "version": "0.1.0", + "dependencies": { + "chart.js": "^4.4.0", + "lucide-svelte": "^0.303.0" + }, + "devDependencies": { + "@sveltejs/adapter-static": "^3.0.0", + "@sveltejs/kit": "^2.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0", + "autoprefixer": "^10.4.16", + "postcss": "^8.4.32", + "svelte": "^4.2.7", + "tailwindcss": "^3.3.6", + "vite": "^5.0.3" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "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==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", + "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/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.0.tgz", + "integrity": "sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.0.tgz", + "integrity": "sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.0.tgz", + "integrity": "sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.0.tgz", + "integrity": "sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.0.tgz", + "integrity": "sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.0.tgz", + "integrity": "sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.0.tgz", + "integrity": "sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.0.tgz", + "integrity": "sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.0.tgz", + "integrity": "sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.0.tgz", + "integrity": "sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.0.tgz", + "integrity": "sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.0.tgz", + "integrity": "sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.0.tgz", + "integrity": "sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.0.tgz", + "integrity": "sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.0.tgz", + "integrity": "sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.0.tgz", + "integrity": "sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.0.tgz", + "integrity": "sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.0.tgz", + "integrity": "sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.0.tgz", + "integrity": "sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.0.tgz", + "integrity": "sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.0.tgz", + "integrity": "sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.0.tgz", + "integrity": "sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.0.tgz", + "integrity": "sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.0.tgz", + "integrity": "sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.0.tgz", + "integrity": "sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sveltejs/acorn-typescript": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.9.tgz", + "integrity": "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8.9.0" + } + }, + "node_modules/@sveltejs/adapter-static": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.10.tgz", + "integrity": "sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@sveltejs/kit": "^2.0.0" + } + }, + "node_modules/@sveltejs/kit": { + "version": "2.55.0", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.55.0.tgz", + "integrity": "sha512-MdFRjevVxmAknf2NbaUkDF16jSIzXMWd4Nfah0Qp8TtQVoSp3bV4jKt8mX7z7qTUTWvgSaxtR0EG5WJf53gcuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@sveltejs/acorn-typescript": "^1.0.5", + "@types/cookie": "^0.6.0", + "acorn": "^8.14.1", + "cookie": "^0.6.0", + "devalue": "^5.6.4", + "esm-env": "^1.2.2", + "kleur": "^4.1.5", + "magic-string": "^0.30.5", + "mrmime": "^2.0.0", + "set-cookie-parser": "^3.0.0", + "sirv": "^3.0.0" + }, + "bin": { + "svelte-kit": "svelte-kit.js" + }, + "engines": { + "node": ">=18.13" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": "^5.3.3", + "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-3.1.2.tgz", + "integrity": "sha512-Txsm1tJvtiYeLUVRNqxZGKR/mI+CzuIQuc2gn+YCs9rMTowpNZ2Nqt53JdL8KF9bLhAf2ruR/dr9eZCwdTriRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sveltejs/vite-plugin-svelte-inspector": "^2.1.0", + "debug": "^4.3.4", + "deepmerge": "^4.3.1", + "kleur": "^4.1.5", + "magic-string": "^0.30.10", + "svelte-hmr": "^0.16.0", + "vitefu": "^0.2.5" + }, + "engines": { + "node": "^18.0.0 || >=20" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-2.1.0.tgz", + "integrity": "sha512-9QX28IymvBlSCqsCll5t0kQVxipsfhFFL+L2t3nTWfXnddYwxBuAEtTtlaVQpRz9c37BhJjltSeY4AJSC03SSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.0.0 || >=20" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^3.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "vite": "^5.0.0" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/aria-query": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.27", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz", + "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001774", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.10", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.10.tgz", + "integrity": "sha512-sUoJ3IMxx4AyRqO4MLeHlnGDkyXRoUG0/AI9fjK+vS72ekpV0yWVY7O0BVjmBcRtkNcsAO2QDZ4tdKKGoI6YaQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "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.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001781", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001781.tgz", + "integrity": "sha512-RdwNCyMsNBftLjW6w01z8bKEvT6e/5tpPVEgtn22TiLGlstHOVecsX2KHFkD5e/vRnIE4EGzpuIODb3mtswtkw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chart.js": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz", + "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==", + "license": "MIT", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/code-red": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", + "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15", + "@types/estree": "^1.0.1", + "acorn": "^8.10.0", + "estree-walker": "^3.0.3", + "periscopic": "^3.1.0" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/css-tree": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", + "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.0.30", + "source-map-js": "^1.0.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/devalue": { + "version": "5.6.4", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.6.4.tgz", + "integrity": "sha512-Gp6rDldRsFh/7XuouDbxMH3Mx8GMCcgzIb1pDTvNyn8pZGQ22u+Wa+lGV9dQCltFQ7uVw0MhRyb8XDskNFOReA==", + "dev": true, + "license": "MIT" + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.325", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.325.tgz", + "integrity": "sha512-PwfIw7WQSt3xX7yOf5OE/unLzsK9CaN2f/FvV3WjPR1Knoc1T9vePRVV4W1EM301JzzysK51K7FNKcusCr0zYA==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "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/esm-env": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/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/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "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-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-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-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "license": "MIT" + }, + "node_modules/lucide-svelte": { + "version": "0.303.0", + "resolved": "https://registry.npmjs.org/lucide-svelte/-/lucide-svelte-0.303.0.tgz", + "integrity": "sha512-Lt5kH3pGSSQdFDgL+y09xkp8FCGm7pQBdxmRxdpA0/im5AHl6WGDECWdiB4dnBCp8njOgWd0TaSqgkL0dF0efg==", + "license": "ISC", + "peerDependencies": { + "svelte": ">=3 <5" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mdn-data": { + "version": "2.0.30", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", + "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", + "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/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.36", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", + "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/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/periscopic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", + "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^3.0.0", + "is-reference": "^3.0.0" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "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.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/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/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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.60.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz", + "integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.0", + "@rollup/rollup-android-arm64": "4.60.0", + "@rollup/rollup-darwin-arm64": "4.60.0", + "@rollup/rollup-darwin-x64": "4.60.0", + "@rollup/rollup-freebsd-arm64": "4.60.0", + "@rollup/rollup-freebsd-x64": "4.60.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.0", + "@rollup/rollup-linux-arm-musleabihf": "4.60.0", + "@rollup/rollup-linux-arm64-gnu": "4.60.0", + "@rollup/rollup-linux-arm64-musl": "4.60.0", + "@rollup/rollup-linux-loong64-gnu": "4.60.0", + "@rollup/rollup-linux-loong64-musl": "4.60.0", + "@rollup/rollup-linux-ppc64-gnu": "4.60.0", + "@rollup/rollup-linux-ppc64-musl": "4.60.0", + "@rollup/rollup-linux-riscv64-gnu": "4.60.0", + "@rollup/rollup-linux-riscv64-musl": "4.60.0", + "@rollup/rollup-linux-s390x-gnu": "4.60.0", + "@rollup/rollup-linux-x64-gnu": "4.60.0", + "@rollup/rollup-linux-x64-musl": "4.60.0", + "@rollup/rollup-openbsd-x64": "4.60.0", + "@rollup/rollup-openharmony-arm64": "4.60.0", + "@rollup/rollup-win32-arm64-msvc": "4.60.0", + "@rollup/rollup-win32-ia32-msvc": "4.60.0", + "@rollup/rollup-win32-x64-gnu": "4.60.0", + "@rollup/rollup-win32-x64-msvc": "4.60.0", + "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/set-cookie-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.0.tgz", + "integrity": "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "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/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-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/svelte": { + "version": "4.2.20", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.20.tgz", + "integrity": "sha512-eeEgGc2DtiUil5ANdtd8vPwt9AgaMdnuUFnPft9F5oMvU/FHu5IHFic+p1dR/UOB7XU2mX2yHW+NcTch4DCh5Q==", + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.1", + "@jridgewell/sourcemap-codec": "^1.4.15", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/estree": "^1.0.1", + "acorn": "^8.9.0", + "aria-query": "^5.3.0", + "axobject-query": "^4.0.0", + "code-red": "^1.0.3", + "css-tree": "^2.3.1", + "estree-walker": "^3.0.3", + "is-reference": "^3.0.1", + "locate-character": "^3.0.0", + "magic-string": "^0.30.4", + "periscopic": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/svelte-hmr": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz", + "integrity": "sha512-Gyc7cOS3VJzLlfj7wKS0ZnzDVdv3Pn2IuVeJPk9m2skfhcu5bq3wtIZyQGggr7/Iim5rH5cncyQft/kRLupcnA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^12.20 || ^14.13.1 || >= 16" + }, + "peerDependencies": { + "svelte": "^3.19.0 || ^4.0.0" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz", + "integrity": "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..28469f8 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,28 @@ +{ + "name": "rental-manager-frontend", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "@sveltejs/adapter-static": "^3.0.0", + "@sveltejs/kit": "^2.5.0", + "@sveltejs/vite-plugin-svelte": "^3.1.0", + "autoprefixer": "^10.4.16", + "postcss": "^8.4.32", + "svelte": "^4.2.19", + "tailwindcss": "^3.3.6", + "vite": "^5.0.3" + }, + "dependencies": { + "chart.js": "^4.4.0", + "lucide-svelte": "^0.303.0" + }, + "overrides": { + "svelte": "^4.2.19" + } +} \ No newline at end of file diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js new file mode 100644 index 0000000..2aa7205 --- /dev/null +++ b/frontend/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/frontend/src/app.css b/frontend/src/app.css new file mode 100644 index 0000000..0ab2c32 --- /dev/null +++ b/frontend/src/app.css @@ -0,0 +1,8 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + html { font-family: 'Inter', system-ui, sans-serif; } + * { box-sizing: border-box; } +} diff --git a/frontend/src/app.html b/frontend/src/app.html new file mode 100644 index 0000000..a82f7ac --- /dev/null +++ b/frontend/src/app.html @@ -0,0 +1,13 @@ + + + + + + + Rental Manager + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/frontend/src/lib/stores/api.js b/frontend/src/lib/stores/api.js new file mode 100644 index 0000000..961c848 --- /dev/null +++ b/frontend/src/lib/stores/api.js @@ -0,0 +1,112 @@ +import { writable, get } from 'svelte/store'; + +export const currentUser = writable(null); +export const authToken = writable(null); + +const BASE = '/api'; + +async function request(method, path, body, isFormData = false) { + const token = get(authToken); + const headers = {}; + if (token) headers['Authorization'] = token; + if (body && !isFormData) headers['Content-Type'] = 'application/json'; + + const res = await fetch(`${BASE}${path}`, { + method, + headers, + body: isFormData ? body : body ? JSON.stringify(body) : undefined, + credentials: 'include', + }); + + if (res.status === 401) { + currentUser.set(null); + authToken.set(null); + window.location.href = '/login'; + return; + } + if (!res.ok) { + const text = await res.text(); + throw new Error(text || `HTTP ${res.status}`); + } + if (res.status === 204) return null; + return res.json(); +} + +export const api = { + auth: { + login: (email, password) => request('POST', '/auth/login', { email, password }), + logout: () => request('POST', '/auth/logout'), + register: (data) => request('POST', '/auth/register', data), + me: () => request('GET', '/me'), + updateProfile: (data) => request('PUT', '/me', data), + updatePassword: (data) => request('PUT', '/me/password', data), + }, + users: { + list: () => request('GET', '/users'), + delete: (id) => request('DELETE', `/users/${id}`), + }, + categories: { + list: (params = {}) => request('GET', `/categories?${new URLSearchParams(params)}`), + create: (data) => request('POST', '/categories', data), + update: (id, data) => request('PUT', `/categories/${id}`, data), + delete: (id) => request('DELETE', `/categories/${id}`), + }, + properties: { + list: () => request('GET', '/properties'), + get: (id) => request('GET', `/properties/${id}`), + create: (data) => request('POST', '/properties', data), + update: (id, data) => request('PUT', `/properties/${id}`, data), + delete: (id) => request('DELETE', `/properties/${id}`), + }, + transactions: { + list: (params = {}) => request('GET', `/transactions?${new URLSearchParams(params)}`), + create: (data) => request('POST', '/transactions', data), + update: (id, data) => request('PUT', `/transactions/${id}`, data), + delete: (id) => request('DELETE', `/transactions/${id}`), + split: (id, data) => request('POST', `/transactions/${id}/split`, data), + summary: (params = {}) => request('GET', `/transactions/summary?${new URLSearchParams(params)}`), + monthly: (params = {}) => request('GET', `/transactions/monthly?${new URLSearchParams(params)}`), + categories: (params = {}) => request('GET', `/transactions/categories?${new URLSearchParams(params)}`), + }, + calendar: { + list: (params = {}) => request('GET', `/calendar?${new URLSearchParams(params)}`), + createEvent: (data) => request('POST', '/calendar', data), + updateEvent: (id, data) => request('PUT', `/calendar/${id}`, data), + deleteEvent: (id) => request('DELETE', `/calendar/${id}`), + stats: (params = {}) => request('GET', `/calendar/stats?${new URLSearchParams(params)}`), + sync: (propertyId) => request('POST', `/calendar/sync/${propertyId}`), + }, + documents: { + list: (params = {}) => request('GET', `/documents?${new URLSearchParams(params)}`), + upload: (formData) => request('POST', '/documents', formData, true), + download: (id) => `${BASE}/documents/${id}/download`, + delete: (id) => request('DELETE', `/documents/${id}`), + exportUrl: (params = {}) => `${BASE}/documents/export?${new URLSearchParams(params)}`, + }, + + loans: { + list: (params = {}) => request('GET', `/loans?${new URLSearchParams(params)}`), + createWithData: (data) => request('POST', '/loans/create', data), + create: (data) => request('POST', '/loans', data), + delete: (id) => request('DELETE', `/loans/${id}`), + lines: (id, params = {}) => request('GET', `/loans/${id}/lines?${new URLSearchParams(params)}`), + annualSummary: (id, params = {}) => request('GET', `/loans/${id}/summary?${new URLSearchParams(params)}`), + uploadLines: (id, lines) => request('POST', `/loans/${id}/lines`, lines), + splitByDate: (id, date) => request('GET', `/loans/${id}/split?date=${date}`), + splitForDate: (date) => request('GET', `/loans/split?date=${date}`), + }, + fiscal: { + summary: (params = {}) => request('GET', `/fiscal/summary?${new URLSearchParams(params)}`), + exportUrl: (params = {}) => `${BASE}/fiscal/export?${new URLSearchParams(params)}`, + }, +}; + +export const properties = writable([]); +export const selectedProperty = writable(null); + +export async function loadProperties() { + const props = await api.properties.list(); + properties.set(props || []); + const sel = get(selectedProperty); + if (!sel && props?.length > 0) selectedProperty.set(props[0]); +} \ No newline at end of file diff --git a/frontend/src/routes/+layout.js b/frontend/src/routes/+layout.js new file mode 100644 index 0000000..ae88a27 --- /dev/null +++ b/frontend/src/routes/+layout.js @@ -0,0 +1,2 @@ +export const prerender = false; +export const ssr = false; diff --git a/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte new file mode 100644 index 0000000..e15cdf6 --- /dev/null +++ b/frontend/src/routes/+layout.svelte @@ -0,0 +1,133 @@ + + +{#if !ready} +
+
+
+{:else if $currentUser} +
+ +
+ +
+
+{:else} + +{/if} \ No newline at end of file diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte new file mode 100644 index 0000000..00d6b63 --- /dev/null +++ b/frontend/src/routes/+page.svelte @@ -0,0 +1,471 @@ + + +
+ + +
+
+

Dashboard

+

Vue d'ensemble de vos locations

+
+
+ + + + +
+
+ + {#if loading} +
+ {#each [1,2,3,4] as _}
{/each} +
+
+ {#each [1,2,3] as _}
{/each} +
+ {:else} + + +
+
+
+ Revenus {periodLabel} +
+

{fmt(totalIncome)}

+
+
+
+ Dépenses {periodLabel} +
+

{fmt(totalExpense)}

+
+
+
Bénéfice net
+

{fmt(totalBalance)}

+
+
+
+ Occupation ce mois +
+

{occupancyRate}%

+
+
+ + +
+ + +
+

Revenus & dépenses par mois — {selectedYear}

+
+ {#if monthlyData.every(m => m.income === 0 && m.expense === 0)} +
Aucune donnée pour {selectedYear}
+ {:else} + + {/if} +
+
+ + +
+
+

Dépenses par catégorie

+
+ + +
+
+ + {#if categoryExpenses.length === 0} +
Aucune dépense
+ {:else if categoryView === 'chart'} +
+ +
+ {:else} + +
+ + + + + + + + + + {#each categoryExpenses as c, i} + + + + + + {/each} + + + + + + + + +
CatégorieMontant%
+
+ + {c.category} +
+
{fmt(c.amount)} + {totalCatExpense > 0 ? ((c.amount / totalCatExpense) * 100).toFixed(1) : 0}% +
Total{fmt(totalCatExpense)}100%
+
+ {/if} +
+
+ + +
+ + +
+

Par bien — {periodLabel}

+ {#if summary.length === 0} +

Aucune donnée

+ {:else} +
+ {#each summary as s} +
+
+ {s.property_name} + {fmt(s.balance)} +
+ +
+ {#if s.total_income > 0 || s.total_expense > 0} + {@const total = s.total_income + s.total_expense} +
+
+ {/if} +
+
+ ↑ {fmt(s.total_income)} + ↓ {fmt(s.total_expense)} +
+
+ {/each} +
+ {/if} +
+ + +
+

Dernières transactions

+ {#if recentTx.length === 0} +

Aucune transaction

+ {:else} +
+ {#each recentTx as t (t.id)} +
+
+

{t.description || t.category_name || '—'}

+

{fmtDate(t.date)} · {t.property_name}

+
+ + {t.type === 'income' ? '+' : '−'}{fmt(t.amount)} + +
+ {/each} +
+ + Voir toutes → + + {/if} +
+
+ + +
+
+ +

Occupations ce mois

+
+ {#if calEvents.length === 0} +

Aucune occupation ce mois-ci

+ {:else} +
+ {#each calEvents as e (e.id)} +
+
+

+ {e.title || (e.source === 'airbnb' ? 'Réservation Airbnb' : 'Occupation manuelle')} +

+

{e.property_name}

+
+
+

{fmtDate(e.start_date)} → {fmtDate(e.end_date)}

+ + {e.source === 'airbnb' ? 'Airbnb' : 'Manuel'} + +
+
+ {/each} +
+ {/if} +
+ + {/if} +
diff --git a/frontend/src/routes/calendar/+page.svelte b/frontend/src/routes/calendar/+page.svelte new file mode 100644 index 0000000..f75196f --- /dev/null +++ b/frontend/src/routes/calendar/+page.svelte @@ -0,0 +1,275 @@ + + +
+ +
+
+ +

Calendrier

+
+
+ + +
+
+ + {#if syncMsg} +

{syncMsg}

+ {/if} + {#if syncError} +

{syncError}

+ {/if} + + +
+ +
+ + {monthNames[viewMonth]} {viewYear} + +
+
+ + +
+
+

Jours occupés

+

{occupiedDays} / {daysInMonth}

+
+
+

Taux d'occupation

+

{occupancyRate}%

+
+
+

Séjours ce mois

+

{sejoursThisMonth}

+
+
+ + +
+ +
+ {#each dayNames as d} +
{d}
+ {/each} +
+ +
+ {#each calendarDays as day, i} + {@const event = eventByDay[day] ?? null} + {@const occupied = !!event} +
+ {#if day} + + {day} + + {#if event && event.start_date === `${viewYear}-${String(viewMonth+1).padStart(2,'0')}-${String(day).padStart(2,'0')}`} +
+ {event.title || (event.source === 'airbnb' ? 'Airbnb' : 'Locataire')} +
+ {/if} + {/if} +
+ {/each} +
+
+ + +
+ Airbnb (sync auto) + Manuel +
+
+ + +{#if showForm} +
showForm = false}> +
+
+

Ajouter une période d'occupation

+ +
+
+ {#if error}

{error}

{/if} +
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+
+{/if} diff --git a/frontend/src/routes/categories/+page.svelte b/frontend/src/routes/categories/+page.svelte new file mode 100644 index 0000000..cabbde4 --- /dev/null +++ b/frontend/src/routes/categories/+page.svelte @@ -0,0 +1,223 @@ + + +
+
+
+ +

Catégories

+
+ +
+ +

+ Les catégories permettent de classer vos revenus et dépenses pour la comptabilité et la liasse fiscale. + Les catégories déductibles fiscalement sont signalées pour l'export annuel. +

+ + {#if loading} +
+ {#each [1,2,3] as _}
{/each} +
+ {:else} + +
+

+ + Dépenses ({expenses.length}) +

+
+ {#if expenses.length === 0} +

Aucune catégorie de dépense.

+ {:else} + {#each expenses as c, i (c.id)} +
+
+
+ {c.name} + {#if c.tax_deductible} + + Déductible + + {/if} +
+ {#if c.description} +

{c.description}

+ {/if} +
+
+ + +
+
+ {/each} + {/if} +
+
+ + +
+

+ + Revenus ({incomes.length}) +

+
+ {#if incomes.length === 0} +

Aucune catégorie de revenu.

+ {:else} + {#each incomes as c, i (c.id)} +
+
+ {c.name} + {#if c.description} +

{c.description}

+ {/if} +
+
+ + +
+
+ {/each} + {/if} +
+
+ {/if} +
+ + +{#if showForm} +
+
+
+

+ {editingId ? 'Modifier la catégorie' : 'Nouvelle catégorie'} +

+ +
+
+ {#if error} +
+ {error} +
+ {/if} + + +
+ + +
+ +
+ + +
+ +
+ + +
+ + {#if form.type === 'expense'} + + {/if} +
+
+ + +
+
+
+{/if} \ No newline at end of file diff --git a/frontend/src/routes/documents/+page.svelte b/frontend/src/routes/documents/+page.svelte new file mode 100644 index 0000000..31d19b3 --- /dev/null +++ b/frontend/src/routes/documents/+page.svelte @@ -0,0 +1,239 @@ + + +
+
+
+ +

Documents

+
+ + +
+ + Export ZIP + + + +
+
+ + +
+

Ajouter des documents

+
+ + + +
+ + {#if error}

{error}

{/if} + + +
dragover = true} + on:dragleave={() => dragover = false} + on:drop|preventDefault={onDrop} + on:click={() => fileInput.click()} + class="border-2 border-dashed rounded-xl p-8 text-center cursor-pointer transition-colors + {dragover ? 'border-blue-400 bg-blue-50 dark:bg-blue-950/30' : 'border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'}"> + {#if uploading} +
⏳ Upload en cours...
+ {:else} + +

Glissez vos fichiers ici ou cliquez pour choisir

+

PDF, images, tableurs acceptés

+ {/if} +
+ +
+ + +
+ + + +
+ + + {#if loading} +
+ {#each [1,2,3] as _}
{/each} +
+ {:else if documents.length === 0} +
+ +

Aucun document pour ces filtres.

+
+ {:else} + {#each grouped as [catName, docs]} +
+

{catName}

+
+ {#each docs as d, i (d.id)} +
+ {mimeIcon(d.mime_type)} +
+

{d.original_name}

+

{d.property_name} · {fmtDate(d.created_at)}

+
+
+ + +
+
+ {/each} +
+
+ {/each} + {/if} +
diff --git a/frontend/src/routes/fiscal/+page.svelte b/frontend/src/routes/fiscal/+page.svelte new file mode 100644 index 0000000..67d09cd --- /dev/null +++ b/frontend/src/routes/fiscal/+page.svelte @@ -0,0 +1,126 @@ + + +
+
+
+ +

Export fiscal

+
+ +
+ +
+ L'export CSV est formaté pour Excel (séparateur point-virgule, encodage UTF-8 avec BOM). + Il reprend toutes les transactions de l'année sélectionnée avec leur catégorie, + et inclut le total des revenus, dépenses et le bénéfice net. +
+ + +
+ + +
+ + + {#if loading} +
+ {#each [1,2] as _}
{/each} +
+ {:else if summary.length === 0} +

Aucune donnée pour cette sélection.

+ {:else} +
+ {#each summary as s} +
+
+

{s.property_name}

+

Exercice {s.year || filterYear}

+
+
+
+
Revenus
+

{fmt(s.total_income)}

+
+
+
Dépenses
+

{fmt(s.total_expense)}

+
+
+
Bénéfice net
+

{fmt(s.balance)}

+
+
+
+ {/each} +
+ + + {#if summary.length > 1} +
+

Total consolidé — {filterYear}

+
+
+

Revenus totaux

+

{fmt(grandTotal.income)}

+
+
+

Dépenses totales

+

{fmt(grandTotal.expense)}

+
+
+

Bénéfice net

+

{fmt(grandTotal.balance)}

+
+
+
+ {/if} + {/if} +
diff --git a/frontend/src/routes/import/+page.svelte b/frontend/src/routes/import/+page.svelte new file mode 100644 index 0000000..e374aa1 --- /dev/null +++ b/frontend/src/routes/import/+page.svelte @@ -0,0 +1,896 @@ + + +
+
+ +
+

Import bancaire

+

Importez vos relevés QIF — ignorez les virements personnels

+
+
+ + +
+ {#each [['upload','Fichier'], ['preview','Vérification'], ['done','Terminé']] as [s, label], i} +
+
+ {(i === 0 && step !== 'upload') || (i === 1 && step === 'done') ? '✓' : i + 1} +
+ {label} +
+ {#if i < 2}
{/if} + {/each} +
+ + {#if error} +
+ {error} +
+ {/if} + + + {#if step === 'upload'} +
+
dragover = true} + on:dragleave={() => dragover = false} + on:drop|preventDefault={onDrop} + on:click={() => fileInput.click()} + role="button" tabindex="0" + on:keydown={(e) => e.key === 'Enter' && fileInput.click()} + class="border-2 border-dashed rounded-xl p-10 text-center cursor-pointer transition-colors + {dragover ? 'border-blue-400 bg-blue-50 dark:bg-blue-950/30' : 'border-gray-200 dark:border-gray-700 hover:border-gray-300 dark:hover:border-gray-600'}"> + {#if loading} +
+
+ Analyse du fichier... +
+ {:else} + +

Glissez votre fichier QIF ici

+

ou cliquez pour choisir — formats .qif, .qfx

+ {/if} +
+ +
+

Comment exporter depuis votre banque ?

+ Espace client → Mes comptes → Télécharger / Exporter → Format QIF. + Vous pourrez assigner chaque ligne à un appartement, la ventiler sur plusieurs biens (✂️), ou l'ignorer. +
+
+ + + {:else if step === 'preview'} +
+
+ {toImport.length} à importer + {#if ignored.length > 0} + · {ignored.length} ignorée{ignored.length > 1 ? 's' : ''} + {/if} + {#if missingPropCount > 0} + + {missingPropCount} sans bien — import bloqué + + {/if} + {#if uncatCount > 0} + + {uncatCount} sans catégorie — import bloqué + + {/if} +
+ Assigner tous à : + + + +
+
+
+ +
+
+ + + + + + + + + + + + + + {#each transactions as t, i (i)} + {#if t.status !== 'absorbed'} + + + + + + + + + + + + + + + + + + + {/if} + {/each} + +
ActionDateDescriptionBien *CatégorieFrais agenceMontant
+
+ + {#if t.status !== 'ignore' && isLoanPayment(t.amount)} + + {:else} + + {/if} + + {#if t._mergedWithIdx != null} + + {:else} + + {/if} + +
+
+ {fmtDate(t.date)} + {#if t.alreadyImported} +
déjà importée
+ {/if} +
+ {#if t.status === 'split'} + +
+ {#each t.splits as s} +
+ {fmt(s.amount)} + → {properties.find(p => p.id === s.property_id)?.name || '?'} +
+ {/each} + +
+ {:else} + + {/if} +
+ {#if t.status === 'import'} + + {:else if t.status === 'split'} + Ventilée + {:else} + ignorée + {/if} + + {#if t.status === 'import'} + + {/if} + + {#if t.status === 'import' && t.type === 'income'} + { transactions[i].agencyFee = parseFloat(e.target.value) || 0; transactions = [...transactions]; }} + class="w-20 px-2 py-1 rounded border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 text-xs text-right focus:outline-none focus:ring-1 focus:ring-blue-500" + /> + {:else} + + {/if} + + {t.type === 'income' ? '+' : '−'}{fmt(t.amount)} + {#if t._mergedWithIdx != null} +
⛓ 2 lignes fusionnées
+ {/if} + {#if (t.agencyFee || 0) > 0} +
−{fmt(t.agencyFee)} frais
+ {/if} +
+
+
+ +
+ Importer + Ventiler sur plusieurs biens + Ignorer + Fusionner 2 prélèvements → 1 mensualité +
+ + + {:else if step === 'done'} +
+
+ +
+

Import terminé

+

+ {result.imported} transaction{result.imported > 1 ? 's' : ''} importée{result.imported > 1 ? 's' : ''} + {#if result.skipped > 0}· {result.skipped} ignorée{result.skipped > 1 ? 's' : ''}{/if} +

+ {#if result.errors?.length > 0} +
+ {#each result.errors as e}

{e}

{/each} +
+ {/if} +
+ + + Voir les transactions → + +
+
+ {/if} +
+ + +{#if loanPickerIdx !== null} +
+
+
+
+

Choisir la ligne d'amortissement

+

+ {transactions[loanPickerIdx]?.description || '—'} · + {fmt(transactions[loanPickerIdx]?.amount)} +

+
+ +
+
+ {#if loanPickerLoading} +
Chargement...
+ {:else} + {#each loanPickerData as lr} +
+

+ {lr.loan.label} — {lr.loan.property_name || ''} +

+ {#if lr.lines.length === 0} +

Aucune ligne pour cette année.

+ {:else} + + + + + + + + + + + + {#each lr.lines as line} + selectLoanLine(lr, line)}> + + + + + + + {/each} + +
Éch.DateCapitalIntérêtsMensualité
#{line.rank}{fmtDate(line.due_date.substring(0,10))}{fmt(line.capital)}{fmt(line.interest)}{fmt(line.total_amount)}
+ {/if} +
+ {/each} + {/if} +
+
+
+{/if} + + +{#if splitIdx !== null && splitSource} +
+
+
+
+

Ventiler la transaction

+

+ {splitSource.description || '—'} · + + {fmt(splitSource.amount)} + +

+
+ +
+
+ {#if splitError}

{splitError}

{/if} +

Répartissez le montant entre plusieurs biens. Chaque part doit avoir un bien et une catégorie.

+ + {#each splitParts as part, i} +
+
+ +
+ + +
+ {part.type === 'income' ? '+' : '−'}{fmt(Math.abs(part.amount))} +
+ + {#if splitParts.every(p => p.type === splitSource.type)} +
+ updateSplitPct(i, e.target.value)} class="flex-1 accent-blue-600"/> +
+ updateSplitPct(i, e.target.value)} + class="w-14 px-2 py-1 rounded border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 text-gray-900 dark:text-white text-xs text-right focus:outline-none focus:ring-2 focus:ring-blue-500"/> + % +
+
+ {/if} +
+
+ + updateSplitAmount(i, e.target.value)} + class="w-full px-2 py-1.5 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900 text-gray-900 dark:text-white text-xs focus:outline-none focus:ring-2 focus:ring-blue-500"/> +
+
+ + +
+
+ + +
+
+
+ + +
+
+ {/each} + + +
+
+ Revenus + +{fmt(splitNetIncome)} +
+
+ Dépenses + −{fmt(splitNetExpense)} +
+
+ + Net ({splitSource.type === 'income' ? 'revenus − dépenses' : 'dépenses − revenus'}) + + + {fmt(splitNet)} + {#if Math.abs(splitDiff) <= 0.01} + + {:else} + ≠ {fmt(splitSource.amount)} + {/if} + +
+
+
+
+ + +
+
+
+{/if} \ No newline at end of file diff --git a/frontend/src/routes/loans/+page.svelte b/frontend/src/routes/loans/+page.svelte new file mode 100644 index 0000000..e10ac72 --- /dev/null +++ b/frontend/src/routes/loans/+page.svelte @@ -0,0 +1,350 @@ + + +
+
+
+ +
+

Prêts immobiliers

+

Tableau d'amortissement — décomposition intérêts / capital

+
+
+ +
+ +
+

Pourquoi gérer les prêts ici ?

+

+ Vos remboursements mensuels mélangent capital (non déductible) et intérêts (déductibles fiscalement). + Ce module connaît la décomposition exacte pour chaque mois et peut créer automatiquement + les transactions séparées pour la liasse fiscale. +

+
+ + {#if loading} +
+ {#each [1,2] as _}
{/each} +
+ {:else if loans.length === 0} +
+ +

Aucun prêt configuré.

+ +
+ {:else} +
+ {#each loans as loan (loan.id)} +
+
+
+

{loan.label}

+ {loan.reference} +
+

{loan.property_name}

+
+ Capital initial : {fmt(loan.initial_amount)} + Mensualité : {fmt(loan.monthly_payment)} +
+
+
+ + +
+
+ {/each} +
+ {/if} +
+ + +{#if showUpload} +
+
+
+

Ajouter un prêt

+ +
+
+ {#if error} +
+ {error} +
+ {/if} + + +
+ +
+ {#each KNOWN_LOANS as loan} + + {/each} +
+
+ + +
+ + +
+ +

+ Les 216 échéances (2024→2044) sont déjà intégrées et seront chargées automatiquement. +

+
+
+ + +
+
+
+{/if} + + +{#if showDetail} +
+
+
+
+

{showDetail.label}

+

{showDetail.property_name} · {showDetail.reference}

+
+
+ + +
+
+ + {#if annualSummary && annualSummary.months > 0} +
+
+

Échéances

+

{annualSummary.months}

+
+
+

Total payé

+

{fmt(annualSummary.total_payment)}

+
+
+

Intérêts ✓ déductibles

+

{fmt(annualSummary.total_interest)}

+
+
+

Capital remboursé

+

{fmt(annualSummary.total_capital)}

+
+
+ {/if} + +
+ {#if lines.length === 0} +
Aucune échéance pour {yearDetail}.
+ {:else} + + + + + + + + + + + + + {#each lines as l (l.id)} + + + + + + + + + {/each} + +
RangÉchéanceMensualitéIntérêts ✓CapitalCapital restant
{l.rank}{fmtDate(l.due_date)}{fmt(l.total_amount)}{fmt(l.interest)}{fmt(l.capital)}{fmt(l.remaining_capital)}
+ {/if} +
+ +
+

+ Les intérêts en bleu sont déductibles — nécessite catégorie "Intérêts emprunt". +

+ {#if lines.length > 0} + + {/if} +
+
+
+{/if} \ No newline at end of file diff --git a/frontend/src/routes/login/+page.svelte b/frontend/src/routes/login/+page.svelte new file mode 100644 index 0000000..6a33628 --- /dev/null +++ b/frontend/src/routes/login/+page.svelte @@ -0,0 +1,36 @@ + + +
+
+

🏠 Mes Locations

+ {#if error}

{error}

{/if} +
+ + e.key === 'Enter' && submit()} + class="w-full px-4 py-2.5 rounded-lg border border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-white text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"/> + +
+
+
diff --git a/frontend/src/routes/profile/+page.svelte b/frontend/src/routes/profile/+page.svelte new file mode 100644 index 0000000..78a46de --- /dev/null +++ b/frontend/src/routes/profile/+page.svelte @@ -0,0 +1,137 @@ + + +
+
+ +

Mon profil

+
+ + +
+

+ Informations personnelles +

+ + {#if profileMsg} +
+ {profileMsg.text} +
+ {/if} + +
+
+ + +
+
+ + +
+
+ +
+
+
+ + +
+

+ Changer le mot de passe +

+ + {#if passwordMsg} +
+ {passwordMsg.text} +
+ {/if} + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
diff --git a/frontend/src/routes/properties/+page.svelte b/frontend/src/routes/properties/+page.svelte new file mode 100644 index 0000000..68f03a8 --- /dev/null +++ b/frontend/src/routes/properties/+page.svelte @@ -0,0 +1,174 @@ + + +
+
+
+ +

Biens

+
+ +
+ + {#if loading} +
+ {#each [1,2] as _} +
+ {/each} +
+ {:else if properties.length === 0} +
+ +

Aucun bien. Commencez par en ajouter un.

+
+ {:else} +
+ {#each properties as p (p.id)} +
+
+
+

{p.name}

+ {typeLabel[p.type]} +
+

{p.address}

+
+ {#if p.bank_account}🏦 {p.bank_account}{/if} + {#if p.ical_url}✓ iCal configuré{/if} +
+
+
+ {#if p.type === 'airbnb' && p.ical_url} + + {/if} + + +
+
+ {/each} +
+ {/if} +
+ +{#if showForm} +
+
+
+

{editingId ? 'Modifier le bien' : 'Nouveau bien'}

+ +
+
+ {#if error}

{error}

{/if} +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ {#if form.type === 'airbnb'} +
+ + +

Airbnb → Annonce → Paramètres → Calendrier → Exporter

+
+ {/if} +
+ +