7ad9221
commit 7ad9221
124 files changed
+8775 -11363
Showing 56 changed files+5583-7874
This diff is too large and has been truncated. Displaying first 56 files.
.dockerignore.gitignoreAGENTS.mdMakefilePLAN.mdREADME.mdUI_PLAN.mdbackend/.env.examplebackend/Dockerfilebackend/app/config.pybackend/app/db/models.pybackend/app/db/sqlite_db.pybackend/app/db/vector_db.pybackend/app/errors.pybackend/app/routes/alerts.pybackend/app/routes/analytics.pybackend/app/routes/assets.pybackend/app/routes/auth.pybackend/app/routes/copilot.pybackend/app/routes/metrics.pybackend/app/routes/personnel.pybackend/app/routes/stream.pybackend/app/routes/system.pybackend/app/schemas/api.pybackend/app/services/alert_dispatcher.pybackend/app/services/alert_service.pybackend/app/services/analysis_scheduler.pybackend/app/services/camera_manager.pybackend/app/services/camera_ops_service.pybackend/app/services/camera_registry.pybackend/app/services/face_backend.pybackend/app/services/face_engine.pybackend/app/services/genai_engine.pybackend/app/services/health_sentinel.pybackend/app/services/ingestion_service.pybackend/app/services/logging_service.pybackend/app/services/notify.pybackend/app/services/productivity_engine.pybackend/app/services/rule_engine.pybackend/app/services/spatial_memory.pybackend/app/utils/timeutil.pybackend/funnytest.pybackend/main.pybackend/path.pybackend/requirements.txtdocker-compose.ymldocs/diagrams/architecture.svgdocs/diagrams/data-model.svgdocs/diagrams/deployment.svgdocs/diagrams/recognition-pipeline.svgfrontend/css/app.cssfrontend/face_app/.eslintrc.cjsfrontend/face_app/.gitignorefrontend/face_app/README.mdfrontend/face_app/index.htmlfrontend/face_app/package-lock.json
▼.dockerignore
+10-0
| @@ -0,0 +1,10 @@ | ||
1 | +.venv | |
2 | +__pycache__ | |
3 | +*.pyc | |
4 | +data/ | |
5 | +node_modules | |
6 | +dist | |
7 | +*.egg-info | |
8 | +.git | |
9 | +.env | |
10 | +*.log | |
0 | 11 | |
▼.gitignore
+5-0
| @@ -184,3 +184,8 @@ dist-ssr | ||
184 | 184 | *.njsproj |
185 | 185 | *.sln |
186 | 186 | *.sw? |
187 | + | |
188 | +# Runtime data (roster DB, vector index, photos, logs, clips) | |
189 | +data/ | |
190 | +*.sqlite | |
191 | +*.db | |
187 | 192 | |
▼AGENTS.md
+102-0
| @@ -0,0 +1,102 @@ | ||
1 | +# AGENTS.md | |
2 | + | |
3 | +Guidelines for AI agents (and contributors) working in this repository. | |
4 | + | |
5 | +## Project purpose | |
6 | + | |
7 | +**AegisVision — AI Enterprise Surveillance Platform** (a.k.a. `AegisVision`). | |
8 | + | |
9 | +A production-grade, plug-and-play face-recognition monitoring system for offices, | |
10 | +schools, colleges and workplaces. It streams CCTV/webcam feeds, identifies people | |
11 | +in real time against an enrolled roster, tags UNKNOWN faces (never saving new files), | |
12 | +and derives behavioral analytics: | |
13 | + | |
14 | +- Attendance (in/out at gates), live occupancy counts | |
15 | +- Productivity: active / idle-lazy / bunking / phone-distraction times | |
16 | +- Health: drowsiness, fatigue, posture | |
17 | +- Spatial anti-theft and asset-displacement memory | |
18 | +- Real-time incident alerts + a Generative-AI decision copilot | |
19 | + | |
20 | +## Tech stack & architecture | |
21 | + | |
22 | +- **Backend:** Python 3.12, FastAPI, Uvicorn, SQLAlchemy + SQLite, ChromaDB (cosine vector index), OpenCV, `insightface`/ONNX (SCRFD+ArcFace, default) with `face_recognition`/dlib fallback, watchdog, Google GenAI (`gemini-2.5-flash`). Managed with `uv`. | |
23 | +- **Frontend:** vanilla-JS + Tailwind CSS, static SPA served by FastAPI itself (no bundler/build step); fetch wrapper + WebSocket alert feed in `frontend/js/`. | |
24 | +- **Data dirs (runtime, git-ignored):** `data/known_persons/` (plug-and-play roster), `data/vector_db/` (ChromaDB), `data/surveillance.db` (SQLite), `data/logs/`. | |
25 | + | |
26 | +Directory map (the *v2* architecture — see PLAN Phase 0 re: legacy cleanup): | |
27 | + | |
28 | +``` | |
29 | +backend/ | |
30 | + main.py FastAPI entry (lifespan: init DB, sync roster, start watcher) | |
31 | + app/ | |
32 | + config.py Central config/constants/thresholds & camera map | |
33 | + db/ | |
34 | + sqlite_db.py Engine/session/init | |
35 | + models.py Personnel, AttendanceLog, ProductivityLog, HealthLog, AssetLog, IncidentAlert | |
36 | + vector_db.py ChromaDB face-embedding upsert/query/delete (+ dim self-heal) | |
37 | + services/ | |
38 | + face_backend.py Recognition backend (insightface/ONNX 512-d or dlib 128-d, auto-fallback) | |
39 | + face_engine.py Detect → liveness → embed → VectorDB match → UNKNOWN tagging + HUD draw | |
40 | + ingestion_service.py Plug-and-play folder/photo/JSON rostering + directory watcher | |
41 | + productivity_engine.py active/lazy/bunk/phone-status state machine | |
42 | + health_sentinel.py drowsiness/posture/fatigue telemetry | |
43 | + spatial_memory.py Asset-region displacement / anti-theft rules | |
44 | + genai_engine.py Gemini Copilot Q&A (+ rule-based fallback) | |
45 | + alert_dispatcher.py WebSocket fan-out for live alerts | |
46 | + alert_service.py Persist + WS broadcast + external push on new IncidentAlert | |
47 | + notify.py Config-gated push: Telegram bot + JSON webhook (best-effort) | |
48 | + rule_engine.py Background rule-escalation sweep (UNKNOWN×3, phone-overuse, drowsiness) | |
49 | + logging_service.py Rotating file + console logging under data/logs | |
50 | + routes/ | |
51 | + stream.py MJPEG video_feed + WS endpoint | |
52 | + personnel.py enroll/delete personnel | |
53 | + analytics.py dashboard + attendance/productivity/health/assets | |
54 | + alerts.py list/resolve alerts | |
55 | + copilot.py POST /api/copilot/query | |
56 | + assets.py register/list/delete tracked assets (spatial anti-theft) | |
57 | + auth.py · system.py · metrics.py | |
58 | +frontend/ | |
59 | + index.html Tailwind dashboard shell (served by FastAPI at site root) | |
60 | + js/{api,main}.js fetch wrapper + WS helper, tab router | |
61 | + js/views/*.js live · personnel · analytics · productivity · health · incidents · assets · copilot · system | |
62 | + css/app.css · login.html | |
63 | +docs/diagrams/*.svg architecture, recognition pipeline, data model, deployment | |
64 | +legacy: backend/functions/ admin/ models/ settings/ tools/ ← Phase 0 cleanup (pickle + Firebase). Do not extend. | |
65 | +``` | |
66 | + | |
67 | +## Non-negotiables (do not break) | |
68 | + | |
69 | +1. **UNKNOWN faces must never be written to disk** and must be visibly labeled on-frame (`UNKNOWN FACE`). | |
70 | +2. **Plug-and-play roster:** dropping `<person_id>_<Name>/` with `info.json` + photos into | |
71 | + `data/known_persons/` must auto-register (startup sync + live watchdog). | |
72 | +3. **VectorDB + SQLite must stay in sync** on enroll/delete. | |
73 | +4. Entrance/attendance logs are cooldown-throttled (30 s per person) per camera. | |
74 | +5. Frontend never hardcodes a production host — same-origin requests through | |
75 | + `frontend/js/api.js` (relative `/api` paths + WebSocket to `location.host`). | |
76 | + | |
77 | +## Conventions | |
78 | + | |
79 | +- Python: typed signatures, one service per file, route files stay thin (logic → services), SQLAlchemy sessions opened/closed in `try/finally`, Pydantic schemas for API responses. | |
80 | +- Frontend: views are plain JS modules (`frontend/js/views/*.js`) with Tailwind utility classes; native `fetch`/`WebSocket`; DOM rendering, no framework bundler. | |
81 | +- **Do NOT add code comments.** Self-documenting names and minimal docstrings only. | |
82 | +- Add tests for any new service logic under `tests/` (pytest + httpx TestClient). | |
83 | +- Match existing naming (snake_case Python, PascalCase components). | |
84 | + | |
85 | +## How to run | |
86 | + | |
87 | +```bash | |
88 | +# Backend (uv-managed venv already at .venv) | |
89 | +cp backend/.env.example backend/.env # fill GEMINI_API_KEY / camera sources | |
90 | +uv run uvicorn main:app --reload --host 0.0.0.0 --port 8000 # run from backend/ | |
91 | + | |
92 | +# Frontend is served by FastAPI itself (SERVE_FRONTEND=true) — open the site root. | |
93 | +# API docs | |
94 | +open http://localhost:8000/docs | |
95 | +``` | |
96 | + | |
97 | +## Verify before finishing any change | |
98 | + | |
99 | +- `uv run pytest` — add tests alongside new logic (suite lives under `tests/`). | |
100 | +- Backend: `uv run python -m compileall backend/app` or run uvicorn and hit `/`, `/docs`, `/api/analytics/dashboard`. | |
101 | +- Frontend: a UI change is verified by loading the served app at `/` (and `/login.html`) in a browser — no build step. `make build-frontend` checks `frontend/index.html` exists. | |
102 | +- TODO: lint/typecheck commands are not yet configured (frontend `eslint` script exists but is not enforced). | |
0 | 103 | |
▼Makefile
+29-0
| @@ -0,0 +1,29 @@ | ||
1 | +.PHONY: install dev test build-frontend static docker-up docker-down run | |
2 | + | |
3 | +PYTHON := $(shell [ -x .venv/bin/python ] && echo .venv/bin/python || echo python3) | |
4 | +BACKEND_DIR := backend | |
5 | + | |
6 | +install: | |
7 | + $(PYTHON) -m pip install -r $(BACKEND_DIR)/requirements.txt | |
8 | + | |
9 | +dev: | |
10 | + cd $(BACKEND_DIR) && $(CURDIR)/$(PYTHON) -m uvicorn main:app --reload --host 0.0.0.0 --port 8000 | |
11 | + | |
12 | +test: | |
13 | + $(PYTHON) -m pytest tests/ -q | |
14 | + | |
15 | +# The dashboard is a static Tailwind SPA served automatically by FastAPI | |
16 | +# (SERVE_FRONTEND=true). No bundler or build step is required - this target | |
17 | +# only validates the entry point exists. | |
18 | +build-frontend: | |
19 | + @test -f frontend/index.html && echo "dashboard assets are served from frontend/" || (echo "frontend/index.html missing" && exit 1) | |
20 | + | |
21 | +static: build-frontend | |
22 | + | |
23 | +docker-up: | |
24 | + docker compose up -d --build | |
25 | + | |
26 | +docker-down: | |
27 | + docker compose down | |
28 | +run: | |
29 | + ./run.sh | |
0 | 30 | |
▼PLAN.md
+187-0
| @@ -0,0 +1,187 @@ | ||
1 | +# AegisVision — Production Plan (v2 → v3.0) | |
2 | + | |
3 | +Companion to: `AGENTS.md` (repo conventions) · `backend/app/config.py` (config) · `backend/app/db/models.py` (schema) | |
4 | + | |
5 | +## 1. North-star vision | |
6 | + | |
7 | +A **plug-and-play, multi-camera AI surveillance + behavioral monitoring platform** for offices, | |
8 | +schools, colleges and work sites: | |
9 | + | |
10 | +- Enroll anyone by dropping `<person_id>_<Name>/info.json + photos` into `data/known_persons/` (or via the enrollment API). | |
11 | +- Stream from **CCTV / IP cameras / webcams / central server** feeds; identify **many people at once**, fast and accurately. | |
12 | +- Tag **UNKNOWN** faces clearly on-frame with **zero files written to disk** (optional, gated snapshot for later review). | |
13 | +- Track per-person and per-floor: attendance, live occupancy, active/idle-lazy/bunking time, phone-addiction windows, teaching/productive time. | |
14 | +- **Auto update the live dashboard** (metrics, camera grid, incident feed) pushed over WebSocket. | |
15 | +- Monitor **health & safety** (drowsiness, fatigue, posture, fall risk). | |
16 | +- **Remember spatial facts** — knows if something (asset/laptop/bag) is displaced, and whether an unauthorized/UNKNOWN person is near it. | |
17 | +- **Theft-proof alerting** — severity-ranked alerts with acknowledgment, WebSocket + Telegram/webhook push. | |
18 | +- **Generative-AI decision support** — natural-language copilot over live DB context. | |
19 | + | |
20 | +--- | |
21 | + | |
22 | +## 2. Current state (audit, v2.0.0) | |
23 | + | |
24 | +### ✅ Working | |
25 | +- FastAPI shell: lifespan init → SQLite tables → roster sync → directory watcher | |
26 | +- ChromaDB cosine face match + SQLite relational store | |
27 | +- Plug-and-play roster (folder + `info.json`) with live watchdog | |
28 | +- `face_engine.process_frame()` — detect → liveness check → embed → match → tag (**no file writes for UNKNOWN**) | |
29 | +- Gate attendance logging with 30 s/person cooldown; UNKNOWN_FACE incident rows | |
30 | +- Productivity/health engines write `ProductivityLog` / `HealthLog` | |
31 | +- Gemini copilot (flash + rule-based fallback) via `/api/copilot/query` | |
32 | +- React dashboard: 5s poll, WebSocket connect, tabs for Live/Personnel/Productivity/Health/Assets/Copilot | |
33 | +- `/api/personnel/enroll` saves photo+JSON to roster folder | |
34 | + | |
35 | +### ❌ Gaps blocking production (each is a Phase task) | |
36 | +1. **Camera logic dead** — `config.CAMERA_STREAMS` (3 cams) unused; `stream.py` hardcodes `source=0`; no RTSP; every client reopens camera 0. | |
37 | +2. **Multi-photo embedding bug** — vector `add_person_embedding` upserts by `person_id`, so several photos per person **overwrite each other**; only one encoding survives. | |
38 | +3. **Real-time alerts not wired** — `alert_dispatcher.broadcast_alert()` never called; WS only PONG; browser incident feed never sees CRITICAL events. | |
39 | +4. **Spatial memory dead** — no asset-registration API; no object/displacement detection; `spatial_memory.process_asset_surveillance` only fires on UNKNOWN guess. | |
40 | +5. **No occupancy/line-crossing** — no in/out zone logic, counts, concurrent people, or per-person daily aggregates. | |
41 | +6. **Health = heuristics** — eye-std/Lagrange stand-in; no 68-landmark EAR from shoulders, no real fatigue/fall. | |
42 | +7. **Analytics shallow** — returns raw ORM rows (no Pydantic), no time-series, no per-person daily summary. | |
43 | +8. **Copilot single-shot** — no RAG over history, no function/tool calls, one provider. | |
44 | +9. **Frontend hardcodes `localhost:8000`**; no API base env; `VideoStream.jsx` uses dead `/video/*` legacy; data tables untyped. | |
45 | +10. **No auth** — `/api/*` fully open. | |
46 | +11. **Legacy cruft** — `app/functions`, `app/admin`, `app/models`, `app/settings/connectdb.py` (Firebase), pickle encodings, `videoapp.py` reference legacy stack. Confusing. | |
47 | +12. **Deployability** — no `requirements` (only `cmake`), no Docker, no CI, no tests, no NGINX/TLS. | |
48 | + | |
49 | +--- | |
50 | + | |
51 | +## 3. Target architecture | |
52 | + | |
53 | +``` | |
54 | +┌─────────────── Edge/CCTV sources (RTSP / MJPEG / WebRTC) ───────────────┐ | |
55 | +│ cam_entrance · cam_workspace · cam_restricted (config.CAMERA_STREAMS) │ | |
56 | +└──────────────────────────────┬──────────────────────────────────────────┘ | |
57 | + ▼ | |
58 | + Background Camera Workers (pool) | |
59 | + pull frames → share-latest-frame to clients | |
60 | + ▼ | |
61 | + ┌────────────────────────────┐ | |
62 | + │ Frame Pipeline │ | |
63 | + │ detect(face) → track(IOU/ │ | |
64 | + │ ByteTrack) → embed → │ | |
65 | + │ liveness(CNN) → vector │ | |
66 | + │ match(top-N) → label │ | |
67 | + └────────────────────────────┘ | |
68 | + │ │ │ │ | |
69 | + AttendanceLog AttendanceLog ProductivityLog HealthLog | |
70 | + (in/out zone) (occupancy) (active/lazy/ (EAR/posture/ | |
71 | + (spatial/asset) bunk/phone) fatigue/fall) | |
72 | + │ │ │ │ | |
73 | + ▼ ▼ ▼ ▼ | |
74 | + ┌───────────── VectorDB(Chroma) + SQLite ─────────────┐ | |
75 | + │ face_embeddings | personnel | attendance | │ | |
76 | + │ productivity | health | assets | incidents │ | |
77 | + └───────────────────────┬──────────────────────────────┘ | |
78 | + ▼ | |
79 | + ┌────────────────────────── ─────┐ | |
80 | + │ API (FastAPI /api/*) │ | |
81 | + │ WebSocket /api/stream/ws │ | |
82 | + │ Alert dispatcher → push │ | |
83 | + └──────────────┬────────────────┘ | |
84 | + ▼ | |
85 | + React Dashboard | |
86 | + (Live grid · Metrics · Personnel · Productivity · | |
87 | + Health · Spatial · Copilot) | |
88 | +``` | |
89 | + | |
90 | +Layering: | |
91 | +- **Run-time config**: `app/config.py` (or `.env`). | |
92 | +- **Services** hold domain logic; **routes** serialize via Pydantic schemas (step under construction). | |
93 | +- **DB** is the single source of truth; **VectorDB** is the embedding index kept in sync. | |
94 | + | |
95 | +--- | |
96 | + | |
97 | +## 4. Delivery plan by phase | |
98 | + | |
99 | +> Priorities: P0 = must ship in the v3.0 MVP. | |
100 | + | |
101 | +### Phase 0 — Foundation & hygiene (P0) | |
102 | +- [x] Pin deps: `requirements.txt`/`pyproject.toml` (fastapi, uvicorn, sqlalchemy, chromadb, opencv-python, face_recognition/dlib, watchdog, httpx, google-genai, pydantic, python-multipart, websockets). | |
103 | +- [x] Remove legacy dead modules: `backend/app/functions/`, `backend/app/admin/`, `backend/app/models/`, `backend/app/settings/`, `backend/app/tools/`, `backend/funnytest.py`, old `frontend/face_app/src/components/VideoStream.jsx` (point to new MJPEG), legacy `frontend/package.json` stub. | |
104 | +- [x] `.env` + `backend/app/config.py` moves all paths/URLs/cameras/thresholds to env-aware config; keep defaults. | |
105 | +- [x] Pydantic repositories: add `app/schemas/*.py` (Personnel, Attendance, Productivity, Health, Asset, Incident, Dashboard) and serialize all routes. | |
106 | +- [x] Error-handling/exception handlers; structured logging to `data/logs/` (rotating file + console). | |
107 | +- [x] Baseline tests: `tests/test_api.py` (TestClient smoke), `tests/test_ingestion.py`, `tests/test_vectors.py`. | |
108 | +- [x] Dockerfile + docker-compose (backend + frontend) + `Makefile` targets (`dev`, `test`, `build-frontend`, `docker-up/down`). | |
109 | +- **Done-when:** `uvicorn` boots cleanly with no legacy imports; API returns typed JSON; `pytest -q` green. | |
110 | + | |
111 | +### Phase 1 — Multi-camera streaming & crowd identity engine (P0) | |
112 | +- [x] **Camera pool:** from `config.CAMERA_STREAMS` build one background capture worker per source (webcam, RTSP URL, or stream ID); share latest frame; back-pressure; auto-reconnect. | |
113 | +- [x] `/api/stream/video_feed?camera_id=…` serves the pushed frame instead of owning the capture. | |
114 | +- [x] **Tracker**: connect detections frame-to-frame (IOU) so embed+match runs every N frames, not every frame → crowd-capable. | |
115 | +- [ ] Split detection/embedding into optional ONNX/GPU path (e.g. `face_detection` / dlib-CNN / `onnxruntime`). | |
116 | +- [x] Fix multi-photo embeddings: store one vector per photo (`person_id__k`), dedupe on match via best distance. | |
117 | +- [ ] Liveness v2: replace Laplacian heuristic with a lightweight CNN anti-spoof model (Silent-Face-Anti-Spoofing ONNX) scaled by confidence; keep fallback. | |
118 | +- **`-spoof/unknown showcase`:** UNKNOWN still never saved; on-screen label `UNKNOWN FACE` + optional gated low-res snapshot in DB (flag, off by default). | |
119 | +- **Done-when:** 3 configured cameras stream independently; crowd scene at 10+ faces stays > 15 FPS; per-person weekly accuracy ≥ 95 P0 on a labeled clip. | |
120 | + | |
121 | +### Phase 2 — Occupancy, gate zone, and behavioral analytics (P0) | |
122 | +- [x] **Zone/line-crossing**: gate camera computes per-person entry/exit timestamps, live occupancy, max concurrent, today/today counters. | |
123 | +- [x] **Person-level daily summary** — daily aggregates (active_time, idle_time, bunk_time, phone_time, productive_score) via `/api/analytics/person/{id}/{day}`. | |
124 | +- [x] **Analytics endpoints**: `/api/analytics/dashboard` (typed), `/api/analytics/occupancy`, `/api/analytics/person/{id}/{day}`, `/api/analytics/timeseries` (hourly grouped). | |
125 | +- [ ] Productivity state machine hardening (handle skips/debondes, cooldowns, multiple cameras per person). | |
126 | +- **Done-when:** dashboard fails show gates in/out + each person's day breakdown driven by real DB aggregates. | |
127 | + | |
128 | +### Phase 3 — Spatial memory, theft & real alerting (P0) | |
129 | +- [x] `/api/assets` CRUD: register asset with owner + bbox; store in `AssetLog`. | |
130 | +- [x] Displacement detection: `spatial_memory.monitor()` per-frame region-diff → status NORMAL→DISPLACED_MISSING when owner absent; UNKNOWN near → `THEFT_SUSPECTED` → CRITICAL. | |
131 | +- [x] Wire `alert_dispatcher.broadcast_alert()` on every new IncidentAlert (WS push to dashboard). | |
132 | +- [x] Alert acknowledge/resolve broadcasts `ALERT_RESOLVED` over WS; rule-based escalation engine (`rule_engine.py`): UNKNOWN ×3 at gate → CRITICAL, phone-overuse ×3, repeated drowsiness − deduped sweep. | |
133 | +- [x] Push providers: Telegram bot + generic JSON webhook adapter (`notify.py`, config-gated via `TELEGRAM_BOT_TOKEN`/`NOTIFY_WEBHOOK_URL`, severity floor). | |
134 | +- **Done-when:** moving a laptop +1 person who is not its owner triggers CRITICAL within & offline-detectable; WS ALERT pops on dashboard instantly. | |
135 | + | |
136 | +### Phase 4 — Health & safety sentinel v2 (P2) | |
137 | +- [ ] Add per-ring landmarks (dlib 68-point) for real EAR (drowsiness), head-pose / shoulder-line for posture (GOOD/SLOUCH/FALL). | |
138 | +- [ ] Fatigue emissions: evaluate consecutive EAR aboves / head-drop events into `fatigue_score` and DROWSINESS_WARNING with cooldown. | |
139 | +- [ ] Fall-risk: person-region aspect ratio + motion → "PERSON_DOWN" alert (school/office safety). | |
140 | +- [ ] Safer thresholds in config; individual baselines (per-person calibration). | |
141 | + | |
142 | +### Phase 5 — Copilot & decision engine vX (P2) | |
143 | +- [ ] Function-calling (librarian) that run safe SQL over SQLite + returns tabular/summaries instead of static context blocks. | |
144 | +- [ ] Vision capability: ask "show/describe who was at gate 8am" (attachment/camera still). | |
145 | +- [ ] Provider abstraction: Gemini (default) / Ollama / OpenAI-compatible switch via config + env. | |
146 | +- [ ] RAG: index past logs/alerts (Chroma secondary collection) for long-horizon questions. | |
147 | +- [ ] Decision actions: suggested lock-down / notify-HR / acknowledge (surfacing action buttons). | |
148 | + | |
149 | +### Phase 6 — Production hardening & deployment (P0) | |
150 | +- [ ] **Auth + RBAC (JWT)**: admin, security, HR, viewer scopes; protect all `/api` except `/docs`. | |
151 | +- [x] Env-driven API base URL; frontend builds with `VITE_API_BASE` (src/config.js). | |
152 | +- [ ] Testing/CI: pytest (services + routes), Playwright e2e for dashboard; GitHub Actions. | |
153 | +- [ ] Serve: uvicorn workers (1 = camera-writer 'main' + N=match-workers `--workers` caution with pickle state) · NGINX/TLS reverse proxy · health-check endpoints. | |
154 | +- [ ] Observability: structured logs, metrics endpoint (`/api/health`, `/api/stats` frames, cpu, cameras-alive). | |
155 | +- [ ] Docs: setup, deploy, API schema. | |
156 | + | |
157 | +--- | |
158 | + | |
159 | +## 5. Feature roadmap (short → long term) | |
160 | + | |
161 | +- **MVP (v3.0, Phases 0–3)**: multi-camera RTSP, seating tracking, UNKNOWN labeling (no files), gate occupancy + in/out, per-person day summary, spatial/theft alerts with real WS push, JWT auth, Docker, tests. | |
162 | +- **v3.1 (P4–6)**: health v2 (r-drowsiness, posture, fall), copilot v2 (function calling/RAG/vision), NGX + prod CI, DB indexes + partitioning for scale. | |
163 | +- **v4.0**: roaming multi-site central server (regional hub → central aggregation), license/site isolation, mobile app, PKI-secured edge, threat biometry (watchlists), STEM compliance, export=GDPR-style retention. | |
164 | + | |
165 | +--- | |
166 | + | |
167 | +## 6. Acceptance criteria (Definition of Done for v3.0) | |
168 | + | |
169 | +1. 3 cameras (at least one RTSP) stream concurrently on one box; dashboard shows all three. | |
170 | +2. Group scene: 8+ faces scene → each known face labeled at ≥90 conf (dense track) within 1 s; unknown face → `UNKNOWN FACE` in red, no file created. | |
171 | +3. Roster plug-and-play: dropping a new folder populates VectorDB+SQLite and appears on Personnel tab (< 2 s). | |
172 | +4. Gate: entry/exit, today, live occupancy, per-person in/out are computed and rendered. | |
173 | +5. Productivity: per-person active/idle/bunk/phone minutes update on the dashboard each poll; summaries aggregate. | |
174 | +6. Asset displacement → CRITICAL THEFT alert; incident live-pushed over WS; resolvable. | |
175 | +7. Health: DROWSINESS (above cooldown) and POSTURE/FALL events reach alerts page. | |
176 | +8. Copilot answers collect using function-built history (no API key → readable built-in fallback). | |
177 | +9. Auth: unauthenticated dashboard+API → redirect/401; admin-only routes enforced. | |
178 | +10. Tests green locally & CI; API fully typed; README covers setup/deploy. | |
179 | + | |
180 | +--- | |
181 | + | |
182 | +## How to contribute against this plan | |
183 | + | |
184 | +1. Start a branch per phase (or feature). Read `AGENTS.md` first — it holds the rules. | |
185 | +2. Implement top-down; run **backend** via `uvicorn` from `backend/`, **frontend** via `npm run dev` from `frontend/face_app`. | |
186 | +3. Add tests for new service logic; keep DB migrations explicit (new-scope ALTER → SQLite remap before `create_all` upgrade path). | |
187 | +4. Before commit: `pytest -q` + `npm run build` must pass; update `README.md` (and this plan) before finishing the phase. | |
0 | 188 | |
▼README.md
+226-53
| @@ -1,90 +1,262 @@ | ||
1 | -# liveattendence_with_anti_cheat | |
2 | -[](https://wakatime.com/badge/user/d9081c06-f3fd-422d-981c-cd2acc46a3c7/project/fa4105ec-a9a9-4e3e-9e2b-d0d13e165c81) | |
1 | +<div align="center"> | |
3 | 2 | |
3 | +# 🛡️ AegisVision | |
4 | 4 | |
5 | +### AI Enterprise Surveillance & Behavioral-Monitoring Platform | |
5 | 6 | |
7 | +**Plug-and-play, multi-camera face recognition with anti-spoofing liveness, occupancy & gate analytics, spatial anti-theft memory, real-time alerting, and a Generative-AI copilot.** | |
6 | 8 | |
9 | +`FastAPI · ChromaDB · SQLite · OpenCV · insightface/ONNX · dlib · Tailwind CSS · WebSocket` | |
7 | 10 | |
11 | +[](https://www.python.org/) | |
12 | +[](https://fastapi.tiangolo.com/) | |
13 | +[](https://tailwindcss.com/) | |
14 | +[](tests/) | |
15 | +[](docker-compose.yml) | |
16 | +[](LICENSE) | |
8 | 17 | |
9 | -**Secure Face Login with Liveness Detection (Python & React)** | |
18 | +</div> | |
10 | 19 | |
11 | -This repository implements a secure face login system using: | |
20 | +--- | |
12 | 21 | |
13 | -- **Face Detection:** Utilizes Python libraries like OpenCV or Dlib to detect faces in real-time video streams. | |
14 | -- **Anti-Spoofing:** Employs deep learning models trained on liveness detection datasets to distinguish real faces from spoofing attempts (photos, videos). | |
15 | -- **React Frontend:** Built with React for a user-friendly and interactive login experience. | |
22 | +## What it does | |
16 | 23 | |
17 | -This project offers: | |
24 | +AegisVision turns any CCTV camera, webcam, or RTSP stream into an intelligent command center for offices, schools, colleges, and work sites: | |
18 | 25 | |
19 | -- **Enhanced Security:** Prevents unauthorized access through photo or video spoofing. | |
20 | -- **Real-time Processing:** Performs face detection and liveness checks in real-time for seamless user interaction. | |
21 | -- **Open-Source Code:** Accessible on GitHub for learning, customization, and contributions. | |
26 | +- **Enroll by drag-and-drop** — drop a folder `<person_id>_<Name>/` with `info.json` + photos into `data/known_persons/`, and the person is live in minutes (startup sync *and* a folder watchdog). | |
27 | +- **Recognize many people, in real time** — SCRFD/ArcFace via ONNX (lightweight, accurate) with an automatic dlib fallback. | |
28 | +- **Never saves UNKNOWN faces** — unknown people are tagged on-frame in red, *zero files written to disk* (optional, off-by-default gated snapshot). | |
29 | +- **Spoof-resistant** — liveness scoring from face texture, gated by `ANTI_SPOOF_THRESHOLD`. | |
30 | +- **Track behavior** — gate in/out & live occupancy, active/idle-lazy/bunking/phone-distraction time, drowsiness & fatigue telegraphs, spatial anti-theft memory (asset displacement vs. owner). | |
31 | +- **Alert instantly** — rule-based escalation (UNKNOWN×3, phone-overuse×3, repeated drowsiness) with WebSocket push to the dashboard plus Telegram / JSON webhook. | |
32 | +- **Ask questions in plain English** — a Generative-AI copilot over the live DB (rule-based fallback when no API key is set). | |
22 | 33 | |
23 | -Covers Modules: | |
34 | +--- | |
24 | 35 | |
25 | -- face detection | |
26 | -- face recognition | |
27 | -- liveness detection | |
28 | -- anti-spoofing | |
29 | -- deep learning | |
30 | -- Python | |
31 | -- OpenCV | |
32 | -- Dlib | |
33 | -- React | |
34 | -- secure login | |
35 | -- biometrics | |
36 | -- authentication | |
36 | +## Screens | |
37 | 37 | |
38 | -# FontEnd | |
39 | -### Login Page | |
40 | - | |
38 | +| Login & onboarding | Live recognition | | |
39 | +|---|---| | |
40 | +|  |  | | |
41 | 41 | |
42 | +> Illustration clips from an earlier build — the current pipeline is described in the diagrams below. | |
42 | 43 | |
44 | +--- | |
43 | 45 | |
46 | +## System architecture | |
44 | 47 | |
48 | + | |
45 | 49 | |
46 | -### BackEnd | |
50 | +**Data flow at a glance** | |
47 | 51 | |
48 | -- ### Face recognition | |
49 | -- ### Anti-Spoofing | |
50 | -- ### FastAPI | |
51 | -- ### Firebase[As realtime Database] | |
52 | +1. **Sources → Workers** — one background capture worker per configured camera (`CAMERA_STREAMS`), sharing the latest frame with back-pressure and auto-reconnect. | |
53 | +2. **Frame pipeline** — detect → track → anti-spoof → embed → match, per camera. | |
54 | +3. **Behavioral engines** — attendance/gate, productivity, health, and spatial memory consume identities + liveness. | |
55 | +4. **Single source of truth** — SQLite (relational) + Chroma (vector index) stay in sync. | |
56 | +5. **API + push** — typed FastAPI routes, MJPEG + WebSocket streams, alert dispatcher, rule engine, and copilot. | |
52 | 57 | |
53 | -## Backend Stracture | |
54 | - | |
58 | +## Face recognition pipeline | |
55 | 59 | |
56 | -## working process of Face recognition | |
60 | + | |
57 | 61 | |
58 | -### [How the Face Recognition Work using deeplearning ](https://medium.com/@ageitgey/machine-learning-is-fun-part-4-modern-face-recognition-with-deep-learning-c3cffc121d78) | |
59 | - | |
62 | +- **Detection** `SCRFD-500M` (small model, <10ms on CPU) → **IoU tracking** keeps identities across frames and only re-embeds when a track window elapses → **liveness** gate → **ArcFace** `w600k_mbf` aligned **512-d** embeddings → **top-N cosine** lookup in Chroma. | |
63 | +- Thresholds (`FACE_DISTANCE_THRESHOLD`, `FACE_CONFIDENCE_MIN`, `ANTI_SPOOF_THRESHOLD`) live on one place in `backend/app/config.py`. | |
64 | +- The backend is swappable without code changes: `FACE_BACKEND=auto` prefers `insightface`, falls back to `legacy` dlib if the package/model is unavailable, and the vector index **self-heals** if the embedding dimension changes (128 → 512). | |
60 | 65 | |
66 | +## Behavioral analytics & alerting | |
61 | 67 | |
62 | -## Antispoofing | |
68 | +| Gate & occupancy | Productivity & wellbeing | Spatial & alerts | | |
69 | +|---|---|---| | |
70 | +| In/out events per camera with a 30 s/person cooldown, live occupancy, per-person daily summaries. | Per-person active / idle-lazy / bunking / phone-distraction minutes with daily aggregates via `/api/analytics/person/{id}/{day}`. | Asset registry, displacement vs owner (THEFT_SUSPECTED), rule-engine escalation & real WS alert feed. | | |
63 | 71 | |
64 | -### [How to antispoofing work ](https://github.com/minivision-ai/Silent-Face-Anti-Spoofing) | |
72 | +--- | |
65 | 73 | |
66 | - | |
74 | +## Data model | |
67 | 75 | |
68 | -### example: | |
69 | - | |
76 | + | |
70 | 77 | |
78 | +Relational rows are the source of truth; the Chroma vector index mirrors `person_id` ↔ embedding so a person can own many photos (aging, angles, re-enrolment). | |
71 | 79 | |
80 | +## Deployment topology | |
72 | 81 | |
82 | + | |
73 | 83 | |
74 | -# Work Flow | |
75 | -``` ✅ EncodeFace | |
76 | - ✅ Database Connection | |
77 | - ✅ Upload encode Picture to Database | |
78 | - ✅Setup plugins | |
79 | - ✅Fontend | |
80 | - ✅ Building API | |
81 | - ✅ Authentication | |
82 | - ✅ RealtimeDatabase Sync | |
83 | - ❌ Connect Backend With Fontend | |
84 | - ❌ Deploy App | |
84 | +One host does it all (Docker Compose): **NGINX** → **uvicorn** → persistent volumes for SQLite, ChromaDB, roster, and logs. Edge inputs are added by ID; no snowflake config. | |
85 | 85 | |
86 | +--- | |
87 | + | |
88 | +## Tech stack | |
89 | + | |
90 | +| Layer | Tools | | |
91 | +|---|---| | |
92 | +| Backend | Python 3.12 · FastAPI · Uvicorn · Pydantic v2 | | |
93 | +| Recognition | OpenCV · insightface/ONNX (SCRFD + ArcFace) · face_recognition/dlib (fallback) · onnxruntime | | |
94 | +| Data | SQLite (SQLAlchemy 2) · ChromaDB (n-dim cosine) | | |
95 | +| Realtime | WebSocket · Server-Sent Events / MJPEG streaming | | |
96 | +| AI | Google GenAI Gemini (rule-based fallback) | | |
97 | +| Frontend | Vanilla-JS + Tailwind CSS SPA (served by FastAPI, no bundler) | | |
98 | +| Ops | Docker Compose · uv · pytest · watchdog roster watcher | | |
99 | + | |
100 | +--- | |
101 | + | |
102 | +## Quickstart | |
103 | + | |
104 | +### 1. Clone | |
105 | + | |
106 | +```bash | |
107 | +git clone [email protected]:swadhinbiswas/AegisVision.git | |
108 | +cd AegisVision | |
109 | +``` | |
110 | + | |
111 | +### 2. Backend (Python 3.12, managed with `uv`) | |
112 | + | |
113 | +```bash | |
114 | +# Copy the environment template and fill in camera sources / Gemini key / Telegram creds | |
115 | +cp backend/.env.example backend/.env | |
116 | + | |
117 | +# Install locked dependencies | |
118 | +uv sync | |
119 | + | |
120 | +# Run (from the backend/ directory) | |
121 | +uv run uvicorn main:app --reload --host 0.0.0.0 --port 8000 | |
122 | +``` | |
123 | + | |
124 | +> The venv is locked with `uv` — no version drift on heavy native deps (`dlib`, `onnxruntime`, `insightface` optional). | |
125 | + | |
126 | +### 3. Open the dashboard | |
127 | + | |
128 | +The dashboard (vanilla-JS + Tailwind) is served automatically by FastAPI (`SERVE_FRONTEND=true` default) — open <http://localhost:8000>. API docs at <http://localhost:8000/docs>. | |
129 | + | |
130 | +### 4. Add people (plug-and-play) | |
131 | + | |
132 | +``` | |
133 | +data/known_persons/ | |
134 | +└── EMP001_Alice/ | |
135 | + ├── info.json # {"name":"Alice","role":"Engineer","department":"Lab"} | |
136 | + └── photo.jpg # one or more JPEG/PNG photos | |
137 | +``` | |
138 | + | |
139 | +Or via the API → `POST /api/personnel/enroll` (multipart). Deletion is `DELETE /api/personnel/:person_id`. | |
140 | + | |
141 | +--- | |
142 | + | |
143 | +## Configuration reference | |
144 | + | |
145 | +Core knobs in `backend/app/config.py` (all overridable via env): | |
146 | + | |
147 | +| Env var | Default | Purpose | | |
148 | +|---|---|---| | |
149 | +| `CAMERA_STREAMS` | JSON | map of `camera_id` → `name` / `source` (int index or RTSP/MJPEG URL) / `type` | | |
150 | +| `FACE_BACKEND` | `auto` | `auto` (prefer insightface) · `insightface` · `legacy` | | |
151 | +| `FACE_INSIGHTFACE_MODEL` | `buffalo_sc` | model zoo name (`buffalo_sc` light · `buffalo_l` accurate) | | |
152 | +| `FACE_DET_SIZE` | `320` | detector input size | | |
153 | +| `FACE_DISTANCE_THRESHOLD` | `0.60` | cosine-distance ceiling for a KNOWN match | | |
154 | +| `ANTI_SPOOF_THRESHOLD` | `0.70` | liveness gate | | |
155 | +| `ENABLE_UNKNOWN_SNAPSHOTS` | `false` | never store UNKNOWN frames unless explicitly enabled | | |
156 | +| `TELEGRAM_BOT_TOKEN` / `NOTIFY_WEBHOOK_URL` | blank | outbound push (blank = disabled) | | |
157 | +| `GEMINI_API_KEY` | blank | copilot (rule fallback used when blank) | | |
158 | +| `JWT_SECRET` | change-me | session signing (change in prod) | | |
159 | + | |
160 | +Full list is in `backend/app/config.py` with env-aware defaults — see `backend/.env.example`. | |
161 | + | |
162 | +--- | |
163 | + | |
164 | +## API surface | |
165 | + | |
166 | +All `/api/**` responses are Pydantic-typed. Feed: `GET /api/stream/video_feed?camera_id=...` (MJPEG) and `GET /api/stream/ws` (WebSocket events). | |
167 | + | |
168 | +| Group | Endpoints | | |
169 | +|---|---| | |
170 | +| Personnel | `GET /api/personnel` · `GET /api/personnel/:id` · `POST /api/personnel/enroll` · `GET /api/personnel/photo/:id` · `DELETE /api/personnel/:id` | | |
171 | +| Analytics | `GET /api/analytics/dashboard` · `/occupancy` · `/attendance` · `/productivity` · `/health` · `/assets` · `/timeseries` · `/person/:id/:day` · `/camera-activity` | | |
172 | +| Alerts | `GET /api/alerts` · `GET /api/alerts/types` · `POST /api/alerts/resolve/:id` | | |
173 | +| Assets | `GET /api/assets` · `POST /api/assets` · `DELETE /api/assets/:name` | | |
174 | +| Copilot | `POST /api/copilot/query` | | |
175 | +| System | `GET /api/system/health` · `/logs` · `/config` · `GET /api/metrics` | | |
176 | +| Auth | `POST /api/auth/login` · `GET /api/auth/me` | | |
177 | + | |
178 | +--- | |
179 | + | |
180 | +## Repository layout | |
181 | + | |
182 | +``` | |
183 | +. | |
184 | +├── backend/ | |
185 | +│ ├── app/ | |
186 | +│ │ ├── config.py runtime thresholds, paths, camera map | |
187 | +│ │ ├── db/ SQLite (ORM) + ChromaDB vector index | |
188 | +│ │ ├── services/ recognition, ingestion, behavioral engines, | |
189 | +│ │ │ rule engine, alerts, copilot, notify, ops | |
190 | +│ │ ├── routes/ thin HTTP/WS layer (Pydantic-typed) | |
191 | +│ │ └── schemas/ request/response contracts | |
192 | +│ ├── main.py FastAPI entry (lifespan bootstraps everything) | |
193 | +│ └── requirements.txt | |
194 | +├── frontend/ static Tailwind dashboard (served by FastAPI) | |
195 | +├── docs/diagrams/ architecture, pipeline, data model, deployment | |
196 | +├── data/ runtime: roster, SQLite, Chroma, logs (git-ignored) | |
197 | +├── tests/ pytest suite (API, ingestion, vectors, backend) | |
198 | +├── docker-compose.yml single-host compose stack | |
199 | +└── Makefile · requirements.txt dev helpers | |
200 | +``` | |
201 | + | |
202 | +--- | |
203 | + | |
204 | +## Run with Docker | |
205 | + | |
206 | +```bash | |
207 | +docker compose up -d --build | |
208 | +# dashboard → http://localhost:8000 | |
86 | 209 | ``` |
87 | 210 | |
211 | +A `Makefile` gives you `make dev`, `make install`, `make test`, `make build-frontend`, `make docker-up/down`. | |
212 | + | |
213 | +--- | |
214 | + | |
215 | +## Testing | |
216 | + | |
217 | +```bash | |
218 | +# 51 tests — API smoke, plug-and-play ingestion, vector semantics (incl. backend | |
219 | +# swap dim rebuild), recognition backend selection + real-photo embed | |
220 | +make test | |
221 | +``` | |
222 | + | |
223 | +Tests are hermetic (temp data dir, offline-safe `FACE_BACKEND=legacy`). | |
224 | + | |
225 | +--- | |
226 | + | |
227 | +## Roadmap | |
228 | + | |
229 | +| Phase | Focus | Status | | |
230 | +|---|---|---| | |
231 | +| 0–1 | Foundations, camera pool, tracker, ONNX embed path | 🟢 on master | | |
232 | +| 2 | Gate zones, live occupancy, per-person daily analytics, typed analytics | 🟢 on master | | |
233 | +| 3 | Spatial anti-theft, rule escalation, WS + Telegram alerting | 🟢 on master | | |
234 | +| 4 | Health v2 (68-pt landmarks, EAR drowsiness, posture, fall risk) | 🟡 next | | |
235 | +| 5 | Copilot v2 (function-calling SQL, RAG, vision, multi-provider) | 🟡 next | | |
236 | +| 6 | JWT+RBAC everywhere, CI/CD, NGINX+TLS, DB partitioning | 🟡 next | | |
237 | + | |
238 | +Detailed breakdown: see [PLAN.md](PLAN.md) and the repo conventions in [AGENTS.md](AGENTS.md). | |
239 | + | |
240 | +--- | |
241 | + | |
242 | +## Important behavior notes | |
243 | + | |
244 | +- **Average attendance logging is cooldown-throttled** (30 s/person/camera) to keep the store clean. | |
245 | +- **UNKNOWN faces** are labelled on-frame and are heavy gated; snapshots are off by default and never go to disk when disabled. | |
246 | +- The frontend reads `VITE_API_BASE` / a `config.js` bootstrap — it never hardcodes a production host. | |
247 | + | |
248 | +--- | |
249 | + | |
250 | +## Contributing | |
251 | + | |
252 | +1. Read [AGENTS.md](AGENTS.md) — repository conventions, run and verify instructions. | |
253 | +2. Branch per phase/feature. Run `make test` + `python -m compileall backend/app` before finishing. | |
254 | +3. Update [PLAN.md](PLAN.md) when the phase moves. | |
255 | + | |
256 | +--- | |
257 | + | |
258 | +<div align="center"> | |
88 | 259 | |
260 | +MIT © [Swadhin Biswas](https://github.com/swadhinbiswas) — built for offices, schools & workplaces. | |
89 | 261 | |
262 | +</div> | |
90 | 263 | |
▼UI_PLAN.md
+99-0
| @@ -0,0 +1,99 @@ | ||
1 | +# AegisVision — Unified Console (UI Plan) | |
2 | + | |
3 | +Pure-HTML + Tailwind replacement for the React dashboard, plus the analysis of what makes | |
4 | +this an "ultimate" surveillance command center. Served directly by FastAPI (no build step). | |
5 | + | |
6 | +--- | |
7 | + | |
8 | +## 1. Why rebuild | |
9 | + | |
10 | +- React app duplicated views, hardcoded the API host, and carried orphaned MUI/Firebase pages. | |
11 | +- A monitored (no-build) HTML + Tailwind console is simpler to deploy (served by the same | |
12 | + FastAPI process), theme-consistent, and instantly hackable. | |
13 | +- Backend now exposes a typed API (11 route groups) plus WS alerts — the UI just needs to | |
14 | + consume it well. | |
15 | + | |
16 | +## 2. Feature analysis — what makes the "ultimate" console | |
17 | + | |
18 | +### Must-have (already in API, needs first-class UI) | |
19 | +- **Live multi-camera grid** — per-cam MJPEG feed, camera selector, online/offline badges, snapshot. | |
20 | +- **Live occupancy** (gate): currently-inside, peak, today entries/exits. | |
21 | +- **Incident feed** — severity badges, live WS push (ALERT / ALERT_RESOLVED), toast + sound, resolve. | |
22 | +- **Personnel roster** — Enroll via photo upload, delete; search. | |
23 | +- **Productivity / Health tables** — per-person state, drowsiness/posture badges. | |
24 | +- **Spatial assets** — register asset with **visual bbox picker** (click-drag on the camera still), | |
25 | + owner, live status badges. | |
26 | +- **Copilot** — chat UI w/ quick-prompt chips. | |
27 | +- **Analytics** — Chart.js time-series (attendance/productivity/alerts) + per-person daily summary. | |
28 | + | |
29 | +### New "ultimate" additions in this build | |
30 | +- **Auth + admin panel**: login page, JWT session, admin-only actions (enroll/delete, asset CRUD, | |
31 | + alert resolve, system logs). Operator read-only view. | |
32 | +- **System health**: camera worker status, uptime, DB + vector counts, live log tail, config summary. | |
33 | +- **Occupancy trend chart** + alert severity breakdown on the dashboard. | |
34 | +- **Global search** across personnel + incidents. | |
35 | +- **Responsive dark "command-center" theme**; keyboard shortcuts (1–0 to switch views, `/` search). | |
36 | + | |
37 | +### Next (post-MVP, in PLAN.md phases) | |
38 | +- Export CSV of any table · multi-site health · watchlist/biometry · retention policy UI · | |
39 | + real estate/floor heatmaps · PWA offline · i18n. | |
40 | + | |
41 | +--- | |
42 | + | |
43 | +## 3. UI architecture (pure HTML + Tailwind, no framework) | |
44 | + | |
45 | +``` | |
46 | +frontend/ | |
47 | + login.html # admin/operator sign-in | |
48 | + index.html # console shell: sidebar + topbar + <main id="view"> | |
49 | + css/app.css # small custom layer (scrollbars, animations) on top of Tailwind CDN | |
50 | + js/api.js # fetch wrapper (Authorization header, 401→login), WS helper | |
51 | + js/main.js # router (hash-based), sidebar/state, toast, clock, logout | |
52 | + js/views/dashboard.js # KPIs, occupancy, occupancy trend + alert chart | |
53 | + js/views/live.js # MJPEG grid, snapshot, asset bbox picker overlay | |
54 | + js/views/attendance.js # in/out table + per-person day summary modal | |
55 | + js/views/productivity.js # per-person state table | |
56 | + js/views/health.js # drowsiness/posture/fatigue table | |
57 | + js/views/assets.js # register/list/delete assets (uses bbox picker) | |
58 | + js/views/incidents.js # alert feed, filter, resolve | |
59 | + js/views/copilot.js # chat | |
60 | + js/views/analytics.js # Chart.js hourly series (multi-metric) | |
61 | + js/views/personnel.js # enroll modal + table + delete (ADMIN) | |
62 | + js/views/system.js # system health / logs / config (ADMIN) | |
63 | +``` | |
64 | + | |
65 | +- **Tailwind**: Play CDN (`<script src="https://cdn.tailwindcss.com">`) + inline `tailwind.config`. | |
66 | +- **Charts**: Chart.js via CDN. **WS**: `/api/stream/ws` (alert push + PONG keepalive). | |
67 | +- **State**: `localStorage` holds the session token; every API call retries with token and | |
68 | + redirects to `login.html` on 401. | |
69 | + | |
70 | +## 4. Views / routes map | |
71 | + | |
72 | +| View key | Nav label | Backend feeds | | |
73 | +|---|---|---| | |
74 | +| `dashboard` | Command Center | `/api/analytics/dashboard`, `/occupancy`, `/timeseries`, WS | | |
75 | +| `live` | Live Cameras | `/api/stream/video_feed`, `/cameras`, `/snapshot`, WS | | |
76 | +| `attendance` | Attendance | `/api/analytics/attendance`, `/person/{id}/{day}` | | |
77 | +| `productivity` | Productivity | `/api/analytics/productivity` | | |
78 | +| `health` | Health Sentinel | `/api/analytics/health` | | |
79 | +| `assets` | Asset Memory | `/api/assets`, `/api/analytics/assets`, `/api/stream/snapshot` | | |
80 | +| `incidents` | Incident Feed | `/api/alerts`, `/resolve/{id}`, `/types`, WS | | |
81 | +| `copilot` | AI Copilot | `/api/copilot/query` | | |
82 | +| `analytics` | Analytics | `/api/analytics/timeseries` | | |
83 | +| `personnel` | Personnel (ADMIN) | `/api/personnel`, `/enroll`, `/{id}` DELETE | | |
84 | +| `system` | Admin / System (ADMIN) | `/api/system/health`, `/logs`, `/api/auth/me` | | |
85 | + | |
86 | +## 5. Auth model (backed by the new `/api/auth/*`) | |
87 | + | |
88 | +- `POST /api/auth/login` `{username,password}` → `{token, role}` against env `ADMIN_USERNAME`/`ADMIN_PASSWORD`. | |
89 | +- Token is a dependency-free HMAC-SHA256 signed payload (`sub`,`role`,`exp`), `JWT_SECRET` keyed. | |
90 | +- Mutating/admin routes carry `Depends(require_admin)` → 401/403 otherwise. | |
91 | +- Frontend stores token, sends `Authorization: Bearer…`, drops to `login.html` on 401. | |
92 | + | |
93 | +## 6. Definition of done | |
94 | + | |
95 | +- `localhost:8000/login.html` signs in; console opens; every nav view renders real API data. | |
96 | +- Camera tiles stream MJPEG; offline cams show placeholder. | |
97 | +- New alert → WS toast/sound + incident feed rows immediately; admin can resolve. | |
98 | +- bbox picker lets one register an asset by dragging over the snapshot. | |
99 | +- Tests green (existing 19 + auth/system-health). | |
0 | 100 | |
▼backend/.env.example
+50-0
| @@ -0,0 +1,50 @@ | ||
1 | +# AegisVision backend environment | |
2 | +# Copy to `backend/.env` and adjust. Unset values fall back to defaults in app/config.py. | |
3 | + | |
4 | +# ---- Server ---- | |
5 | +HOST=0.0.0.0 | |
6 | +PORT=8000 | |
7 | +RELOAD=true | |
8 | +LOG_LEVEL=INFO | |
9 | + | |
10 | +# ---- AI Copilot (optional - rule-based fallback used when blank) ---- | |
11 | +GEMINI_API_KEY= | |
12 | +GEMINI_MODEL=gemini-2.5-flash | |
13 | + | |
14 | +# ---- Camera streams (JSON). `source` can be an int webcam index or an RTSP/MJPEG url. | |
15 | +# Example RTSP: | |
16 | +# CAMERA_STREAMS={"cam_entrance":{"name":"Main Entrance","source":"rtsp://user:[email protected]:554/Streaming/Channels/101","type":"gate"},"cam_workspace":{"name":"Workstation Floor","source":0,"type":"productivity"},"cam_restricted":{"name":"Finance Vault","source":"rtsp://192.168.1.51:554/stream1","type":"security"}} | |
17 | + | |
18 | +# ---- Face matching ---- | |
19 | +FACE_DISTANCE_THRESHOLD=0.60 | |
20 | +FACE_CONFIDENCE_MIN=0.65 | |
21 | +ANTI_SPOOF_THRESHOLD=0.70 | |
22 | + | |
23 | +# ---- Tracking / performance ---- | |
24 | +TRACK_REEMBED_INTERVAL_SEC=1.5 | |
25 | +TRACK_KEEP_SECONDS=4.0 | |
26 | +GATE_TRACK_CONFIRM_SEC=1.5 | |
27 | + | |
28 | +# ---- Analytics ---- | |
29 | +IDLE_TIMEOUT_SECONDS=300 | |
30 | +PHONE_USAGE_THRESHOLD_SEC=10 | |
31 | + | |
32 | +# ---- Privacy / retention (keep this FALSE: UNKNOWN faces are never stored) ---- | |
33 | +ENABLE_UNKNOWN_SNAPSHOTS=false | |
34 | + | |
35 | +# ---- Rule-based escalation ---- | |
36 | +RULE_SWEEP_INTERVAL_SEC=30 | |
37 | +RULE_WINDOW_MIN=30 | |
38 | +UNKNOWN_GATE_COUNT=3 | |
39 | +PHONE_OVERUSE_COUNT=3 | |
40 | +DROWSINESS_REPEAT_COUNT=3 | |
41 | +ESCALATION_DEDUPE_SEC=600 | |
42 | + | |
43 | +# ---- External push providers (leave blank to disable) ---- | |
44 | +TELEGRAM_BOT_TOKEN= | |
45 | +TELEGRAM_CHAT_ID= | |
46 | +NOTIFY_WEBHOOK_URL= | |
47 | +NOTIFY_MIN_SEVERITY=WARNING | |
48 | + | |
49 | +# ---- Auth (Phase 6; change in production) ---- | |
50 | +JWT_SECRET=change-me-in-production | |
0 | 51 | |
▼backend/Dockerfile
+28-0
| @@ -0,0 +1,28 @@ | ||
1 | +FROM python:3.12-slim | |
2 | + | |
3 | +ENV PYTHONUNBUFFERED=1 \ | |
4 | + PIP_NO_CACHE_DIR=1 \ | |
5 | + DEBIAN_FRONTEND=noninteractive | |
6 | + | |
7 | +RUN apt-get update && apt-get install -y --no-install-recommends \ | |
8 | + build-essential \ | |
9 | + cmake \ | |
10 | + libopenblas-dev \ | |
11 | + liblapack-dev \ | |
12 | + libopencv-dev \ | |
13 | + libgl1 \ | |
14 | + libglib2.0-0 \ | |
15 | + && rm -rf /var/lib/apt/lists/* | |
16 | + | |
17 | +WORKDIR /app | |
18 | + | |
19 | +COPY backend/requirements.txt . | |
20 | +RUN pip install --no-cache-dir setuptools"<81" && \ | |
21 | + pip install --no-cache-dir -r requirements.txt | |
22 | + | |
23 | +COPY backend/ . | |
24 | +COPY frontend/ /frontend/ | |
25 | + | |
26 | +EXPOSE 8000 | |
27 | + | |
28 | +CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] | |
0 | 29 | |
▼backend/app/config.py
+212-0
| @@ -0,0 +1,212 @@ | ||
1 | +import os | |
2 | +from pathlib import Path | |
3 | +from typing import Dict | |
4 | +from pydantic import Field | |
5 | +from pydantic_settings import BaseSettings, SettingsConfigDict | |
6 | + | |
7 | + | |
8 | +class Config(BaseSettings): | |
9 | + """Runtime configuration for AegisVision. Overridable via environment variables or a `.env` file.""" | |
10 | + | |
11 | + model_config = SettingsConfigDict( | |
12 | + env_file=".env", | |
13 | + env_file_encoding="utf-8", | |
14 | + extra="ignore", | |
15 | + ) | |
16 | + | |
17 | + # ---- Core ---- | |
18 | + PROJECT_NAME: str = "AegisVision - AI Enterprise Surveillance Platform" | |
19 | + VERSION: str = "3.0.0" | |
20 | + HOST: str = "0.0.0.0" | |
21 | + PORT: int = 8000 | |
22 | + RELOAD: bool = True | |
23 | + LOG_LEVEL: str = "INFO" | |
24 | + | |
25 | + # ---- Paths (project layout) ---- | |
26 | + BASE_DIR: Path = Field(default_factory=lambda: Path(__file__).resolve().parent.parent.parent) | |
27 | + | |
28 | + @property | |
29 | + def DATA_DIR(self) -> Path: | |
30 | + return self.BASE_DIR / "data" | |
31 | + | |
32 | + @property | |
33 | + def VECTOR_DB_DIR(self) -> Path: | |
34 | + return self.DATA_DIR / "vector_db" | |
35 | + | |
36 | + @property | |
37 | + def KNOWN_PERSONS_DIR(self) -> Path: | |
38 | + return self.DATA_DIR / "known_persons" | |
39 | + | |
40 | + @property | |
41 | + def LOGS_DIR(self) -> Path: | |
42 | + return self.DATA_DIR / "logs" | |
43 | + | |
44 | + @property | |
45 | + def SNAPSHOTS_DIR(self) -> Path: | |
46 | + return self.DATA_DIR / "snapshots" | |
47 | + | |
48 | + @property | |
49 | + def CLIP_DIR(self) -> Path: | |
50 | + return self.DATA_DIR / "clips" | |
51 | + | |
52 | + @property | |
53 | + def SQLITE_URL(self) -> str: | |
54 | + return os.getenv("SQLITE_URL", f"sqlite:///{self.DATA_DIR / 'surveillance.db'}") | |
55 | + | |
56 | + # ---- Face matching thresholds ---- | |
57 | + FACE_DISTANCE_THRESHOLD: float = 0.60 | |
58 | + FACE_CONFIDENCE_MIN: float = 0.65 | |
59 | + ANTI_SPOOF_THRESHOLD: float = 0.70 | |
60 | + # Seconds a track must be held before its label is trusted at the gate. | |
61 | + GATE_TRACK_CONFIRM_SEC: float = 1.5 | |
62 | + # ---- Recognition backend (lightweight + high accuracy) ---- | |
63 | + # auto = prefer the insightface/ONNX pipeline (SCRFD detect + ArcFace embed), | |
64 | + # silently falling back to the legacy dlib `face_recognition` path when the | |
65 | + # package or model is unavailable; `insightface` or `legacy` force a choice. | |
66 | + FACE_BACKEND: str = "auto" | |
67 | + # Model zoo name: "buffalo_sc" (light: SCRFD-500M + w600k_mbf) is the default; | |
68 | + # "buffalo_l" (SCRFD-10G + w600k_r50) trades a little speed for accuracy. | |
69 | + FACE_INSIGHTFACE_MODEL: str = "buffalo_sc" | |
70 | + FACE_DET_SIZE: int = 320 | |
71 | + FACE_USE_GPU: bool = False | |
72 | + | |
73 | + # ---- Analytics thresholds ---- | |
74 | + IDLE_TIMEOUT_SECONDS: int = 300 | |
75 | + PHONE_USAGE_THRESHOLD_SEC: int = 10 | |
76 | + EAR_DROWSINESS_THRESHOLD: float = 0.20 | |
77 | + POSTURE_SLOUCH_RATIO: float = 1.2 | |
78 | + # A face not seen at the gate for this long is considered to have left. | |
79 | + GATE_EXIT_TIMEOUT_SECONDS: int = 60 | |
80 | + | |
81 | + # ---- Spatial memory / anti-theft ---- | |
82 | + ASSET_MOTION_DIFF_THRESHOLD: float = 18.0 # mean grayscale diff (0-255) that counts as movement | |
83 | + ASSET_MOTION_CONFIRM_FRAMES: int = 6 # frames of sustained motion before alerting | |
84 | + ASSET_ALERT_COOLDOWN_SECONDS: int = 120 # min gap between alerts for the same asset | |
85 | + | |
86 | + # ---- Rule-based escalation (alert dedup + severity bump) ---- | |
87 | + RULE_SWEEP_INTERVAL_SEC: int = 30 # how often the rule engine sweeps recent logs | |
88 | + RULE_WINDOW_MIN: int = 30 # look-back window for rule evaluation | |
89 | + UNKNOWN_GATE_COUNT: int = 3 # >= N UNKNOWN gate passes in window -> CRITICAL | |
90 | + PHONE_OVERUSE_COUNT: int = 3 # >= N phone-distraction episodes in window -> WARNING | |
91 | + DROWSINESS_REPEAT_COUNT: int = 3 # >= N drowsiness detections in window -> WARNING | |
92 | + ESCALATION_DEDUPE_SEC: int = 600 # don't re-emit same escalation within this window | |
93 | + | |
94 | + # ---- External push providers (config-gated; disabled when empty) ---- | |
95 | + TELEGRAM_BOT_TOKEN: str = "" | |
96 | + TELEGRAM_CHAT_ID: str = "" | |
97 | + NOTIFY_WEBHOOK_URL: str = "" # generic POST webhook; body is the alert JSON | |
98 | + NOTIFY_MIN_SEVERITY: str = "WARNING" # INFO/WARNING/CRITICAL minimum severity to push | |
99 | + | |
100 | + # ---- Camera & streaming ---- | |
101 | + # `source` may be an int (webcam index) or an RTSP/MJPEG url string. | |
102 | + CAMERA_STREAMS: Dict[str, dict] = { | |
103 | + "cam_entrance": {"name": "Main Entrance Gate", "source": 0, "type": "gate"}, | |
104 | + "cam_workspace": {"name": "Open Workstation Floor 1", "source": 0, "type": "productivity"}, | |
105 | + "cam_restricted": {"name": "Server & Finance Room", "source": 0, "type": "security"}, | |
106 | + } | |
107 | + CAMERA_TYPES: tuple = ("gate", "productivity", "security") | |
108 | + CAMERA_RESOLUTION: str = "640x480" | |
109 | + CAMERA_FPS_LIMIT: int = 25 | |
110 | + CAMERA_RECONNECT_SECONDS: int = 3 | |
111 | + STREAM_BROADCAST_INTERVAL_MS: int = 33 | |
112 | + | |
113 | + # ---- Low-compute surveillance pipeline ---- | |
114 | + # The pricey face pipeline (detect → embed → vector match) does NOT run on | |
115 | + # every frame of every camera. Per camera it runs at most once per | |
116 | + # CAMERA_ANALYZE_INTERVAL_SEC, and only when a cheap grayscale frame-diff | |
117 | + # exceeds CAMERA_MOTION_THRESHOLD — unless CAMERA_ANALYZE_MAX_GAP_SEC has | |
118 | + # elapsed, which forces a re-analysis so gate/occupancy stays truthful even | |
119 | + # in a completely still scene. | |
120 | + CAMERA_ANALYZE_INTERVAL_SEC: float = 0.5 | |
121 | + CAMERA_MOTION_GATE: bool = True | |
122 | + CAMERA_MOTION_THRESHOLD: int = 12 # mean abs-diff (0-255) that counts as motion | |
123 | + CAMERA_MOTION_EMA_ALPHA: float = 0.20 | |
124 | + CAMERA_ANALYZE_MAX_GAP_SEC: float = 3.0 | |
125 | + # To keep the live view alive without encoding every captured frame, JPEG is | |
126 | + # re-encoded at most this often per camera (annotations force an immediate | |
127 | + # publish when the face pipeline does run). | |
128 | + CAMERA_JPEG_PUBLISH_INTERVAL_SEC: float = 0.20 | |
129 | + # ---- Shared analysis budget (corporate multi-camera CPU control) ---- | |
130 | + # The heavy detect→embed→match pipeline is executed on a bounded thread | |
131 | + # pool (so simultaneous busy cameras parallelise across cores instead of | |
132 | + # serialising) and is globally rate-limited: across ALL cameras the face | |
133 | + # pipeline runs at most CAMERA_GLOBAL_ANALYZE_FPS times per second. This is | |
134 | + # the hard ceiling that stops one crowded camera from starving the rest. | |
135 | + CAMERA_ANALYSIS_POOL_SIZE: int = 4 | |
136 | + CAMERA_GLOBAL_ANALYZE_FPS: float = 6.0 | |
137 | + CAMERA_ANALYSIS_TIMEOUT_SEC: float = 2.0 | |
138 | + | |
139 | + # ---- Fleet ops: offline alerting, activity persistence, evidence ---- | |
140 | + # A worker that has produced no frame for this long (feed dead / stream | |
141 | + # unreadable) triggers a CAMERA_OFFLINE incident alert for that camera. | |
142 | + CAMERA_OFFLINE_GRACE_SEC: float = 15.0 | |
143 | + # Min gap before the same camera can be alerted offline again (prevents | |
144 | + # alert spam across flapping feeds). | |
145 | + CAMERA_OFFLINE_COOLDOWN_SEC: float = 300.0 | |
146 | + # How often the fleet housekeeper flushes per-camera activity to SQLite and | |
147 | + # re-checks feed health (seconds). | |
148 | + CAMERA_OPS_SWEEP_SEC: float = 20.0 | |
149 | + # How many days of per-minute camera activity to keep in SQLite. | |
150 | + CAMERA_ACTIVITY_RETENTION_DAYS: int = 30 | |
151 | + # When scene is CROWDED or people >= this, the worker keeps evidence frames. | |
152 | + CAMERA_CROWD_ALERT_AT: int = 5 | |
153 | + # ---- Evidence clips ---- | |
154 | + CLIP_ENABLED: bool = True | |
155 | + CLIP_SECONDS: int = 120 | |
156 | + CLIP_FPS: float = 2.0 | |
157 | + CLIP_COOLDOWN_SEC: float = 300.0 | |
158 | + CLIP_RETENTION_HOURS: int = 72 | |
159 | + # ---- Adaptive capture FPS ---- | |
160 | + # When the shared analysis pool is saturated, capture drops to no lower than | |
161 | + # this FPS so feeds stay live but the CPU wall is respected. | |
162 | + CAMERA_MIN_FPS: int = 8 | |
163 | + | |
164 | + # ---- Tracking ---- | |
165 | + # How often (seconds) a stable track is re-embedded + matched against the vector DB. | |
166 | + TRACK_REEMBED_INTERVAL_SEC: float = 1.5 | |
167 | + TRACK_KEEP_SECONDS: float = 4.0 | |
168 | + TRACK_IOU_MIN: float = 0.35 | |
169 | + | |
170 | + # ---- Privacy / retention ---- | |
171 | + # When True, low-res UNKNOWN face crops are stored under data/snapshots (config-gated). | |
172 | + ENABLE_UNKNOWN_SNAPSHOTS: bool = False | |
173 | + SNAPSHOT_MAX_PER_UNKNOWN: int = 3 | |
174 | + | |
175 | + # ---- AI copilot ---- | |
176 | + GEMINI_API_KEY: str = "" | |
177 | + GEMINI_MODEL: str = "gemini-2.5-flash" | |
178 | + | |
179 | + # ---- API (matches frontend VITE_API_BASE) ---- | |
180 | + API_BASE_URL: str = "http://localhost:8000" | |
181 | + | |
182 | + # ---- Auth (Phase 6; change in production) ---- | |
183 | + JWT_SECRET: str = "change-me-in-production" | |
184 | + JWT_ALGORITHM: str = "HS256" | |
185 | + JWT_EXPIRE_MINUTES: int = 480 | |
186 | + ADMIN_USERNAME: str = "admin" | |
187 | + ADMIN_PASSWORD: str = "admin123" | |
188 | + | |
189 | + # ---- Frontend serving (v3) ---- | |
190 | + # The no-build HTML console (frontend/index.html, login.html) is served by | |
191 | + # the same FastAPI process at the site root when SERVE_FRONTEND is enabled. | |
192 | + SERVE_FRONTEND: bool = True | |
193 | + FRONTEND_DIST: Path = Field(default_factory=lambda: Path(__file__).resolve().parent.parent.parent / "frontend") | |
194 | + | |
195 | + | |
196 | +config = Config() | |
197 | + | |
198 | +# Ensure runtime directories exist. | |
199 | +for path in [config.DATA_DIR, config.VECTOR_DB_DIR, config.KNOWN_PERSONS_DIR, config.LOGS_DIR, config.SNAPSHOTS_DIR, config.CLIP_DIR]: | |
200 | + path.mkdir(parents=True, exist_ok=True) | |
201 | + | |
202 | +# ---- Derive camera sources from env overrides (JSON) ---- | |
203 | +def _source_value(value): | |
204 | + try: | |
205 | + return int(value) | |
206 | + except (ValueError, TypeError): | |
207 | + return value | |
208 | + | |
209 | +for _cam_id, _cfg in config.CAMERA_STREAMS.items(): | |
210 | + if isinstance(_cfg.get("source"), (str, int)): | |
211 | + if isinstance(_cfg["source"], str): | |
212 | + _cfg["source"] = _source_value(_cfg["source"]) | |
0 | 213 | |
▼backend/app/db/models.py
+117-0
| @@ -0,0 +1,117 @@ | ||
1 | +from sqlalchemy import Column, Integer, String, Float, Boolean, DateTime, Text, ForeignKey, UniqueConstraint | |
2 | +from sqlalchemy.orm import relationship | |
3 | +from app.db.sqlite_db import Base | |
4 | +from app.utils.timeutil import utcnow | |
5 | + | |
6 | +class CameraStream(Base): | |
7 | + __tablename__ = "camera_streams" | |
8 | + | |
9 | + id = Column(String, primary_key=True, index=True) | |
10 | + name = Column(String, nullable=False) | |
11 | + source = Column(String, nullable=False) # int index or RTSP/HTTP URL | |
12 | + type = Column(String, default="gate") # gate, productivity, security | |
13 | + resolution = Column(String, nullable=True) | |
14 | + fps_limit = Column(Integer, nullable=True) | |
15 | + active = Column(Boolean, default=True) | |
16 | + created_at = Column(DateTime, default=utcnow) | |
17 | + updated_at = Column(DateTime, default=utcnow, onupdate=utcnow) | |
18 | + | |
19 | + | |
20 | +class Personnel(Base): | |
21 | + __tablename__ = "personnel" | |
22 | + | |
23 | + person_id = Column(String, primary_key=True, index=True) | |
24 | + name = Column(String, nullable=False) | |
25 | + role = Column(String, default="Employee") # Employee, Student, Teacher, Visitor, Admin | |
26 | + department = Column(String, default="General") | |
27 | + email = Column(String, nullable=True) | |
28 | + phone = Column(String, nullable=True) | |
29 | + enrolled_at = Column(DateTime, default=utcnow) | |
30 | + metadata_json = Column(Text, default="{}") | |
31 | + is_active = Column(Boolean, default=True) | |
32 | + | |
33 | + attendance_logs = relationship("AttendanceLog", back_populates="personnel") | |
34 | + productivity_logs = relationship("ProductivityLog", back_populates="personnel") | |
35 | + health_logs = relationship("HealthLog", back_populates="personnel") | |
36 | + assets = relationship("AssetLog", back_populates="owner") | |
37 | + | |
38 | +class AttendanceLog(Base): | |
39 | + __tablename__ = "attendance_logs" | |
40 | + | |
41 | + id = Column(Integer, primary_key=True, autoincrement=True) | |
42 | + person_id = Column(String, ForeignKey("personnel.person_id"), index=True, nullable=True) | |
43 | + name = Column(String, default="UNKNOWN") | |
44 | + timestamp = Column(DateTime, default=utcnow, index=True) | |
45 | + event_type = Column(String, default="ENTRY") # ENTRY, EXIT, GATE_PASS | |
46 | + camera_id = Column(String, default="cam_entrance") | |
47 | + confidence = Column(Float, default=0.0) | |
48 | + is_spoof = Column(Boolean, default=False) | |
49 | + | |
50 | + personnel = relationship("Personnel", back_populates="attendance_logs") | |
51 | + | |
52 | +class ProductivityLog(Base): | |
53 | + __tablename__ = "productivity_logs" | |
54 | + | |
55 | + id = Column(Integer, primary_key=True, autoincrement=True) | |
56 | + person_id = Column(String, ForeignKey("personnel.person_id"), index=True) | |
57 | + timestamp = Column(DateTime, default=utcnow, index=True) | |
58 | + status = Column(String, default="PRODUCTIVE") # PRODUCTIVE, LAZY_IDLE, BUNKING, PHONE_DISTRACTED | |
59 | + duration_seconds = Column(Integer, default=0) | |
60 | + camera_id = Column(String, default="cam_workspace") | |
61 | + notes = Column(String, nullable=True) | |
62 | + | |
63 | + personnel = relationship("Personnel", back_populates="productivity_logs") | |
64 | + | |
65 | +class HealthLog(Base): | |
66 | + __tablename__ = "health_logs" | |
67 | + | |
68 | + id = Column(Integer, primary_key=True, autoincrement=True) | |
69 | + person_id = Column(String, ForeignKey("personnel.person_id"), index=True) | |
70 | + timestamp = Column(DateTime, default=utcnow, index=True) | |
71 | + drowsiness_detected = Column(Boolean, default=False) | |
72 | + eye_aspect_ratio = Column(Float, default=0.30) | |
73 | + posture_status = Column(String, default="GOOD") # GOOD, SLOUCHING, FALL_RISK | |
74 | + fatigue_score = Column(Float, default=0.0) | |
75 | + camera_id = Column(String, default="cam_workspace") | |
76 | + | |
77 | + personnel = relationship("Personnel", back_populates="health_logs") | |
78 | + | |
79 | +class AssetLog(Base): | |
80 | + __tablename__ = "asset_logs" | |
81 | + | |
82 | + id = Column(Integer, primary_key=True, autoincrement=True) | |
83 | + asset_name = Column(String, nullable=False, index=True) | |
84 | + owner_person_id = Column(String, ForeignKey("personnel.person_id"), nullable=True) | |
85 | + last_seen_bbox = Column(String, default="[]") # "[x1, y1, x2, y2]" | |
86 | + camera_id = Column(String, default="cam_workspace") | |
87 | + status = Column(String, default="NORMAL") # NORMAL, MOVED, DISPLACED_MISSING, THEFT_SUSPECTED | |
88 | + updated_at = Column(DateTime, default=utcnow, onupdate=utcnow) | |
89 | + | |
90 | + owner = relationship("Personnel", back_populates="assets") | |
91 | + | |
92 | +class IncidentAlert(Base): | |
93 | + __tablename__ = "incident_alerts" | |
94 | + | |
95 | + id = Column(Integer, primary_key=True, autoincrement=True) | |
96 | + timestamp = Column(DateTime, default=utcnow, index=True) | |
97 | + alert_type = Column(String, nullable=False) # UNKNOWN_FACE, THEFT_ATTEMPT, BUNKING_EXCEEDED, PHONE_OVERUSE, DROWSINESS_WARNING | |
98 | + severity = Column(String, default="WARNING") # INFO, WARNING, CRITICAL | |
99 | + description = Column(Text, nullable=False) | |
100 | + camera_id = Column(String, default="cam_main") | |
101 | + person_id = Column(String, nullable=True) | |
102 | + is_resolved = Column(Boolean, default=False) | |
103 | + | |
104 | + | |
105 | +class CameraActivityLog(Base): | |
106 | + __tablename__ = "camera_activity_log" | |
107 | + __table_args__ = (UniqueConstraint("camera_id", "bucket", name="uq_camera_activity_bucket"),) | |
108 | + | |
109 | + id = Column(Integer, primary_key=True, autoincrement=True) | |
110 | + camera_id = Column(String, nullable=False, index=True) | |
111 | + bucket = Column(DateTime, index=True, nullable=False) # minute start (UTC, naive) | |
112 | + samples = Column(Integer, default=0) | |
113 | + avg_motion = Column(Float, default=0.0) | |
114 | + peak_motion = Column(Float, default=0.0) | |
115 | + crowd_seconds = Column(Integer, default=0) | |
116 | + people_seen = Column(Integer, default=0) | |
117 | + analyzed_count = Column(Integer, default=0) | |
0 | 118 | |
▼backend/app/db/sqlite_db.py
+21-0
| @@ -0,0 +1,21 @@ | ||
1 | +from sqlalchemy import create_engine | |
2 | +from sqlalchemy.orm import sessionmaker, declarative_base | |
3 | +from app.config import config | |
4 | + | |
5 | +engine = create_engine( | |
6 | + config.SQLITE_URL, | |
7 | + connect_args={"check_same_thread": False} | |
8 | +) | |
9 | + | |
10 | +SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) | |
11 | +Base = declarative_base() | |
12 | + | |
13 | +def get_db(): | |
14 | + db = SessionLocal() | |
15 | + try: | |
16 | + yield db | |
17 | + finally: | |
18 | + db.close() | |
19 | + | |
20 | +def init_sqlite_db(): | |
21 | + Base.metadata.create_all(bind=engine) | |
0 | 22 | |
▼backend/app/db/vector_db.py
+135-0
| @@ -0,0 +1,135 @@ | ||
1 | +import chromadb | |
2 | +from chromadb.config import Settings | |
3 | +from typing import List, Dict, Any, Optional | |
4 | +from app.config import config | |
5 | + | |
6 | + | |
7 | +class VectorDBManager: | |
8 | + """ChromaDB interface for face embeddings (cosine space). | |
9 | + | |
10 | + Each enrolled photo produces its own vector so a single person may own | |
11 | + several embeddings (aging, angles, re-enrolment). `embedding_id` is unique | |
12 | + per photo (e.g. `EMP01::photo.jpg`) while `person_id` groups them. | |
13 | + """ | |
14 | + | |
15 | + def __init__(self): | |
16 | + self.client = chromadb.PersistentClient( | |
17 | + path=str(config.VECTOR_DB_DIR), | |
18 | + settings=Settings(anonymized_telemetry=False), | |
19 | + ) | |
20 | + self.collection = self.client.get_or_create_collection( | |
21 | + name="face_embeddings", | |
22 | + metadata={"hnsw:space": "cosine"}, | |
23 | + ) | |
24 | + | |
25 | + def _stored_dim(self) -> Optional[int]: | |
26 | + """Dimension of the vectors already persisted, if any.""" | |
27 | + try: | |
28 | + got = self.collection.get(include=["embeddings"], limit=1) | |
29 | + embs = got.get("embeddings") | |
30 | + if embs is not None and len(embs) > 0 and embs[0] is not None: | |
31 | + return len(embs[0]) | |
32 | + except Exception: | |
33 | + pass | |
34 | + return None | |
35 | + | |
36 | + def _ensure_dim(self, dim: int): | |
37 | + """Rebuild the collection if the embedding dimensionality changed | |
38 | + (e.g. index written by the legacy dlib backend is 128-d but the | |
39 | + insightface backend emits 512-d). Chroma refuses mixed dims in one | |
40 | + collection, so wipe and start fresh on a guaranteed mismatch.""" | |
41 | + stored = self._stored_dim() | |
42 | + if stored is not None and stored != dim: | |
43 | + print(f"[VectorDB] embedding dim changed {stored}->{dim}; rebuilding face_embeddings collection") | |
44 | + self.reset_face_embeddings() | |
45 | + | |
46 | + def reset_face_embeddings(self): | |
47 | + """Drop and recreate the collection (also used on backend dimension change).""" | |
48 | + try: | |
49 | + self.client.delete_collection("face_embeddings") | |
50 | + except Exception: | |
51 | + pass | |
52 | + self.collection = self.client.get_or_create_collection( | |
53 | + name="face_embeddings", | |
54 | + metadata={"hnsw:space": "cosine"}, | |
55 | + ) | |
56 | + | |
57 | + def add_face_embedding( | |
58 | + self, | |
59 | + embedding_id: str, | |
60 | + person_id: str, | |
61 | + embedding: List[float], | |
62 | + metadata: Dict[str, Any], | |
63 | + ): | |
64 | + """Upsert a single face snapshot. embedding_id must be unique.""" | |
65 | + self._ensure_dim(len(embedding)) | |
66 | + meta = {**(metadata or {}), "person_id": person_id, "embedding_id": embedding_id} | |
67 | + try: | |
68 | + self.collection.upsert( | |
69 | + ids=[embedding_id], | |
70 | + embeddings=[embedding], | |
71 | + metadatas=[meta], | |
72 | + ) | |
73 | + except Exception as e: | |
74 | + if "dimension" not in str(e).lower(): | |
75 | + raise | |
76 | + # Chroma pins a dimension per collection even when empty (e.g. an | |
77 | + # index from the previous backend). Recreate the collection and retry. | |
78 | + print(f"[VectorDB] dimension mismatch on write; rebuilding collection: {e}") | |
79 | + self.reset_face_embeddings() | |
80 | + self.collection.upsert( | |
81 | + ids=[embedding_id], | |
82 | + embeddings=[embedding], | |
83 | + metadatas=[meta], | |
84 | + ) | |
85 | + | |
86 | + def query_face(self, embedding: List[float], n_results: int = 1) -> Dict[str, Any]: | |
87 | + """Return closest vector as (person_id, cosine distance, metadata).""" | |
88 | + self._ensure_dim(len(embedding)) | |
89 | + if self.collection.count() == 0: | |
90 | + return {"match_id": None, "embedding_id": None, "distance": 1.0, "metadata": {}} | |
91 | + | |
92 | + results = self.collection.query( | |
93 | + query_embeddings=[embedding], | |
94 | + n_results=n_results, | |
95 | + ) | |
96 | + | |
97 | + if not results or not results["ids"] or not results["ids"][0]: | |
98 | + return {"match_id": None, "embedding_id": None, "distance": 1.0, "metadata": {}} | |
99 | + | |
100 | + embedding_id = results["ids"][0][0] | |
101 | + distance = results["distances"][0][0] | |
102 | + metadata = results["metadatas"][0][0] if results.get("metadatas") else {} | |
103 | + person_id = metadata.get("person_id", embedding_id) | |
104 | + | |
105 | + return { | |
106 | + "match_id": person_id, | |
107 | + "embedding_id": embedding_id, | |
108 | + "distance": float(distance), | |
109 | + "metadata": metadata, | |
110 | + } | |
111 | + | |
112 | + def delete_person_embeddings(self, person_id: str): | |
113 | + """Remove every vector belonging to a person.""" | |
114 | + try: | |
115 | + self.collection.delete(where={"person_id": person_id}) | |
116 | + except Exception: | |
117 | + pass | |
118 | + | |
119 | + def count_embeddings(self) -> int: | |
120 | + try: | |
121 | + return self.collection.count() | |
122 | + except Exception: | |
123 | + return 0 | |
124 | + | |
125 | + def count_persons(self) -> int: | |
126 | + """Distinct person_id count across stored metadata.""" | |
127 | + try: | |
128 | + data = self.collection.get(include=["metadatas"]) | |
129 | + ids = {m.get("person_id") for m in (data.get("metadatas") or []) if m} | |
130 | + return len(ids) | |
131 | + except Exception: | |
132 | + return 0 | |
133 | + | |
134 | + | |
135 | +vector_db = VectorDBManager() | |
0 | 136 | |
▼backend/app/errors.py
+35-0
| @@ -0,0 +1,35 @@ | ||
1 | +import logging | |
2 | + | |
3 | +from fastapi import FastAPI, Request | |
4 | +from fastapi.exceptions import RequestValidationError | |
5 | +from fastapi.responses import JSONResponse | |
6 | +from starlette.exceptions import HTTPException as StarletteHTTPException | |
7 | + | |
8 | +logger = logging.getLogger("aegisvision.exceptions") | |
9 | + | |
10 | + | |
11 | +def add_exception_handlers(app: FastAPI) -> None: | |
12 | + @app.exception_handler(StarletteHTTPException) | |
13 | + async def http_exception_handler(request: Request, exc: StarletteHTTPException): | |
14 | + if exc.status_code >= 500: | |
15 | + logger.error("HTTP %s on %s %s", exc.status_code, request.method, request.url.path) | |
16 | + return JSONResponse( | |
17 | + status_code=exc.status_code, | |
18 | + content={"status": "error", "detail": str(exc.detail)}, | |
19 | + ) | |
20 | + | |
21 | + @app.exception_handler(RequestValidationError) | |
22 | + async def validation_exception_handler(request: Request, exc: RequestValidationError): | |
23 | + logger.warning("Validation error on %s %s: %s", request.method, request.url.path, exc.errors()) | |
24 | + return JSONResponse( | |
25 | + status_code=422, | |
26 | + content={"status": "error", "detail": "Invalid request", "errors": exc.errors()}, | |
27 | + ) | |
28 | + | |
29 | + @app.exception_handler(Exception) | |
30 | + async def unhandled_exception_handler(request: Request, exc: Exception): | |
31 | + logger.exception("Unhandled error on %s %s", request.method, request.url.path) | |
32 | + return JSONResponse( | |
33 | + status_code=500, | |
34 | + content={"status": "error", "detail": "Internal server error"}, | |
35 | + ) | |
0 | 36 | |
▼backend/app/routes/alerts.py
+42-0
| @@ -0,0 +1,42 @@ | ||
1 | +from fastapi import APIRouter, Depends, HTTPException | |
2 | +from sqlalchemy.orm import Session | |
3 | +from app.db.sqlite_db import get_db | |
4 | +from app.db.models import IncidentAlert | |
5 | +from app.schemas.api import IncidentListOut, IncidentOut | |
6 | +from app.services.alert_dispatcher import alert_dispatcher | |
7 | +from app.routes.auth import require_admin | |
8 | + | |
9 | +router = APIRouter(prefix="/api/alerts", tags=["Incident & Alert Management"]) | |
10 | + | |
11 | + | |
12 | +@router.get("/", response_model=IncidentListOut) | |
13 | +def get_alerts(resolved: bool = False, limit: int = 50, db: Session = Depends(get_db)): | |
14 | + query = db.query(IncidentAlert) | |
15 | + if not resolved: | |
16 | + query = query.filter(IncidentAlert.is_resolved == False) | |
17 | + alerts = query.order_by(IncidentAlert.timestamp.desc()).limit(limit).all() | |
18 | + return IncidentListOut(count=len(alerts), data=[IncidentOut.model_validate(a) for a in alerts]) | |
19 | + | |
20 | + | |
21 | +@router.post("/resolve/{alert_id}", response_model=IncidentOut) | |
22 | +def resolve_alert(alert_id: int, db: Session = Depends(get_db), auth: dict = Depends(require_admin)): | |
23 | + alert = db.query(IncidentAlert).filter(IncidentAlert.id == alert_id).first() | |
24 | + if not alert: | |
25 | + raise HTTPException(status_code=404, detail="Alert not found") | |
26 | + alert.is_resolved = True | |
27 | + db.commit() | |
28 | + db.refresh(alert) | |
29 | + alert_dispatcher.broadcast_sync({ | |
30 | + "type": "ALERT_RESOLVED", | |
31 | + "id": alert.id, | |
32 | + "alert_type": alert.alert_type, | |
33 | + "timestamp": alert.timestamp.isoformat() if alert.timestamp else None, | |
34 | + }) | |
35 | + return IncidentOut.model_validate(alert) | |
36 | + | |
37 | + | |
38 | +@router.get("/types") | |
39 | +def get_alert_types(db: Session = Depends(get_db)): | |
40 | + rows = db.query(IncidentAlert.alert_type, IncidentAlert.severity).distinct().all() | |
41 | + types = [{"alert_type": t, "severity": s} for t, s in rows] | |
42 | + return {"status": "success", "count": len(types), "data": types} | |
0 | 43 | |
▼backend/app/routes/analytics.py
+211-0
| @@ -0,0 +1,211 @@ | ||
1 | +from datetime import datetime, timedelta | |
2 | +from typing import List | |
3 | +from fastapi import APIRouter, Depends, HTTPException | |
4 | +from sqlalchemy.orm import Session | |
5 | +from app.services.camera_manager import camera_manager | |
6 | +from app.services.camera_registry import camera_registry | |
7 | +from app.db.sqlite_db import get_db | |
8 | +from app.db.models import Personnel, AttendanceLog, ProductivityLog, HealthLog, AssetLog, IncidentAlert, CameraActivityLog | |
9 | +from app.utils.timeutil import utcnow | |
10 | +from app.schemas.api import ( | |
11 | + AttendanceListOut, | |
12 | + AttendanceOut, | |
13 | + ProductivityListOut, | |
14 | + ProductivityOut, | |
15 | + HealthListOut, | |
16 | + HealthOut, | |
17 | + AssetListOut, | |
18 | + AssetOut, | |
19 | + DashboardOut, | |
20 | + DashboardSummary, | |
21 | + PersonDailyOut, | |
22 | + OccupancyOut, | |
23 | +) | |
24 | + | |
25 | +router = APIRouter(prefix="/api/analytics", tags=["Surveillance Analytics"]) | |
26 | + | |
27 | + | |
28 | +@router.get("/dashboard", response_model=DashboardOut) | |
29 | +def get_dashboard_summary(db: Session = Depends(get_db)): | |
30 | + today_start = utcnow().replace(hour=0, minute=0, second=0, microsecond=0) | |
31 | + | |
32 | + summary = DashboardSummary( | |
33 | + total_personnel=db.query(Personnel).count(), | |
34 | + today_attendance=db.query(AttendanceLog).filter(AttendanceLog.timestamp >= today_start).count(), | |
35 | + unresolved_alerts=db.query(IncidentAlert).filter(IncidentAlert.is_resolved == False).count(), | |
36 | + total_assets=db.query(AssetLog).count(), | |
37 | + theft_threats=db.query(AssetLog).filter(AssetLog.status == "THEFT_SUSPECTED").count(), | |
38 | + productive_sessions=db.query(ProductivityLog).filter( | |
39 | + ProductivityLog.timestamp >= today_start, ProductivityLog.status == "PRODUCTIVE" | |
40 | + ).count(), | |
41 | + phone_distractions=db.query(ProductivityLog).filter( | |
42 | + ProductivityLog.timestamp >= today_start, ProductivityLog.status == "PHONE_DISTRACTED" | |
43 | + ).count(), | |
44 | + bunking_incidents=db.query(ProductivityLog).filter( | |
45 | + ProductivityLog.timestamp >= today_start, ProductivityLog.status == "BUNKING" | |
46 | + ).count(), | |
47 | + health_warnings=db.query(HealthLog).filter( | |
48 | + HealthLog.timestamp >= today_start, HealthLog.drowsiness_detected == True | |
49 | + ).count(), | |
50 | + ) | |
51 | + | |
52 | + return DashboardOut(status="success", timestamp=utcnow().isoformat(), summary=summary) | |
53 | + | |
54 | + | |
55 | +@router.get("/occupancy", response_model=List[OccupancyOut]) | |
56 | +def get_occupancy(db: Session = Depends(get_db)): | |
57 | + """Live gate occupancy across the registered gate cameras.""" | |
58 | + result = [] | |
59 | + for cam in camera_manager.list_cameras(): | |
60 | + if cam.get("type") != "gate" or not cam.get("active"): | |
61 | + continue | |
62 | + occ = cam.get("occupancy") | |
63 | + if occ is None: | |
64 | + continue | |
65 | + occ.pop("camera_id", None) | |
66 | + result.append(OccupancyOut(camera_id=cam["camera_id"], **occ)) | |
67 | + return result | |
68 | + | |
69 | + | |
70 | +@router.get("/camera-activity") | |
71 | +def get_camera_activity(camera_id: str, hours: int = 24, db: Session = Depends(get_db)): | |
72 | + """Per-camera persisted activity series (minute buckets) for 24h/7d charts.""" | |
73 | + if hours < 1 or hours > 168: | |
74 | + raise HTTPException(status_code=400, detail="hours must be between 1 and 168") | |
75 | + cfg = camera_registry.get(camera_id) | |
76 | + if cfg is None: | |
77 | + raise HTTPException(status_code=404, detail="Camera not found") | |
78 | + since = utcnow() - timedelta(hours=hours) | |
79 | + rows = ( | |
80 | + db.query(CameraActivityLog) | |
81 | + .filter(CameraActivityLog.camera_id == camera_id, CameraActivityLog.bucket >= since) | |
82 | + .order_by(CameraActivityLog.bucket.asc()) | |
83 | + .all() | |
84 | + ) | |
85 | + return { | |
86 | + "status": "success", | |
87 | + "camera_id": camera_id, | |
88 | + "hours": hours, | |
89 | + "granularity": "minute", | |
90 | + "data": [ | |
91 | + { | |
92 | + "bucket": r.bucket.isoformat(), | |
93 | + "avg_motion": r.avg_motion, | |
94 | + "peak_motion": r.peak_motion, | |
95 | + "crowd_seconds": r.crowd_seconds, | |
96 | + "people_seen": r.people_seen, | |
97 | + "analyzed_count": r.analyzed_count, | |
98 | + } | |
99 | + for r in rows | |
100 | + ], | |
101 | + } | |
102 | + | |
103 | + | |
104 | +@router.get("/attendance", response_model=AttendanceListOut) | |
105 | +def get_attendance_logs(limit: int = 50, db: Session = Depends(get_db)): | |
106 | + logs = db.query(AttendanceLog).order_by(AttendanceLog.timestamp.desc()).limit(limit).all() | |
107 | + return AttendanceListOut(count=len(logs), data=[AttendanceOut.model_validate(l) for l in logs]) | |
108 | + | |
109 | + | |
110 | +@router.get("/productivity", response_model=ProductivityListOut) | |
111 | +def get_productivity_logs(limit: int = 50, db: Session = Depends(get_db)): | |
112 | + logs = db.query(ProductivityLog).order_by(ProductivityLog.timestamp.desc()).limit(limit).all() | |
113 | + return ProductivityListOut(count=len(logs), data=[ProductivityOut.model_validate(l) for l in logs]) | |
114 | + | |
115 | + | |
116 | +@router.get("/health", response_model=HealthListOut) | |
117 | +def get_health_logs(limit: int = 50, db: Session = Depends(get_db)): | |
118 | + logs = db.query(HealthLog).order_by(HealthLog.timestamp.desc()).limit(limit).all() | |
119 | + return HealthListOut(count=len(logs), data=[HealthOut.model_validate(l) for l in logs]) | |
120 | + | |
121 | + | |
122 | +@router.get("/assets", response_model=AssetListOut) | |
123 | +def get_asset_logs(db: Session = Depends(get_db)): | |
124 | + assets = db.query(AssetLog).all() | |
125 | + return AssetListOut(count=len(assets), data=[AssetOut.model_validate(a) for a in assets]) | |
126 | + | |
127 | + | |
128 | +@router.get("/timeseries") | |
129 | +def get_timeseries(metric: str = "attendance", hours: int = 24, db: Session = Depends(get_db)): | |
130 | + """Hourly-grouped event counts (`metric` = attendance | productivity | health | alerts).""" | |
131 | + metric_map = { | |
132 | + "attendance": (AttendanceLog, "event_type"), | |
133 | + "productivity": (ProductivityLog, "status"), | |
134 | + "health": (HealthLog, "drowsiness_detected"), | |
135 | + "alerts": (IncidentAlert, "severity"), | |
136 | + } | |
137 | + if metric not in metric_map or hours < 1 or hours > 168: | |
138 | + raise HTTPException(status_code=400, detail="metric must be attendance|productivity|health|alerts and 1<=hours<=168") | |
139 | + | |
140 | + model, label_field = metric_map[metric] | |
141 | + since = utcnow() - timedelta(hours=hours) | |
142 | + rows = db.query(model).filter(model.timestamp >= since).all() | |
143 | + | |
144 | + buckets: dict[str, dict] = {} | |
145 | + for r in rows: | |
146 | + key = r.timestamp.strftime("%Y-%m-%dT%H:00:00") | |
147 | + bucket = buckets.setdefault(key, {"bucket": key, "count": 0, "by_label": {}}) | |
148 | + bucket["count"] += 1 | |
149 | + label = str(getattr(r, label_field)) | |
150 | + bucket["by_label"][label] = bucket["by_label"].get(label, 0) + 1 | |
151 | + | |
152 | + return { | |
153 | + "status": "success", | |
154 | + "metric": metric, | |
155 | + "hours": hours, | |
156 | + "data": [buckets[k] for k in sorted(buckets)], | |
157 | + } | |
158 | + | |
159 | + | |
160 | +@router.get("/person/{person_id}/{day}", response_model=PersonDailyOut) | |
161 | +def get_person_daily(person_id: str, day: str, db: Session = Depends(get_db)): | |
162 | + """Per-person daily summary (Phase 2 aggregation). `day` = YYYY-MM-DD (local).""" | |
163 | + from datetime import datetime as _dt | |
164 | + | |
165 | + try: | |
166 | + day_start = _dt.strptime(day, "%Y-%m-%d") | |
167 | + except ValueError: | |
168 | + return PersonDailyOut(person_id=person_id, date=day) | |
169 | + | |
170 | + day_end = day_start + timedelta(days=1) | |
171 | + logs = ( | |
172 | + db.query(ProductivityLog) | |
173 | + .filter( | |
174 | + ProductivityLog.person_id == person_id, | |
175 | + ProductivityLog.timestamp >= day_start, | |
176 | + ProductivityLog.timestamp < day_end, | |
177 | + ) | |
178 | + .all() | |
179 | + ) | |
180 | + | |
181 | + seconds = {"PRODUCTIVE": 0, "LAZY_IDLE": 0, "BUNKING": 0, "PHONE_DISTRACTED": 0} | |
182 | + for log in logs: | |
183 | + seconds[log.status] = seconds.get(log.status, 0) + int(log.duration_seconds or 0) | |
184 | + | |
185 | + entries = db.query(AttendanceLog).filter( | |
186 | + AttendanceLog.person_id == person_id, | |
187 | + AttendanceLog.timestamp >= day_start, | |
188 | + AttendanceLog.timestamp < day_end, | |
189 | + AttendanceLog.event_type == "ENTRY", | |
190 | + ).count() | |
191 | + exits = db.query(AttendanceLog).filter( | |
192 | + AttendanceLog.person_id == person_id, | |
193 | + AttendanceLog.timestamp >= day_start, | |
194 | + AttendanceLog.timestamp < day_end, | |
195 | + AttendanceLog.event_type == "EXIT", | |
196 | + ).count() | |
197 | + | |
198 | + total = max(1, sum(seconds.values())) | |
199 | + score = round(seconds["PRODUCTIVE"] / total, 2) | |
200 | + | |
201 | + return PersonDailyOut( | |
202 | + person_id=person_id, | |
203 | + date=day, | |
204 | + active_seconds=seconds["PRODUCTIVE"], | |
205 | + idle_seconds=seconds["LAZY_IDLE"], | |
206 | + bunk_seconds=seconds["BUNKING"], | |
207 | + phone_seconds=seconds["PHONE_DISTRACTED"], | |
208 | + productive_score=score, | |
209 | + entries=entries, | |
210 | + exits=exits, | |
211 | + ) | |
0 | 212 | |
▼backend/app/routes/assets.py
+48-0
| @@ -0,0 +1,48 @@ | ||
1 | +from typing import List | |
2 | +from fastapi import APIRouter, Depends, HTTPException | |
3 | +from pydantic import BaseModel | |
4 | +from sqlalchemy.orm import Session | |
5 | + | |
6 | +from app.db.sqlite_db import get_db | |
7 | +from app.db.models import AssetLog | |
8 | +from app.services.spatial_memory import spatial_memory | |
9 | +from app.routes.auth import require_admin | |
10 | +from app.schemas.api import AssetListOut, AssetOut | |
11 | + | |
12 | +router = APIRouter(prefix="/api/assets", tags=["Spatial Memory & Anti-Theft"]) | |
13 | + | |
14 | + | |
15 | +class AssetRegisterRequest(BaseModel): | |
16 | + asset_name: str | |
17 | + owner_person_id: str | |
18 | + bbox: List[int] = None # [x1, y1, x2, y2] | |
19 | + camera_id: str = "cam_workspace" | |
20 | + | |
21 | + | |
22 | +@router.get("/", response_model=AssetListOut) | |
23 | +def list_assets(db: Session = Depends(get_db)): | |
24 | + assets = db.query(AssetLog).all() | |
25 | + return AssetListOut(count=len(assets), data=[AssetOut.model_validate(a) for a in assets]) | |
26 | + | |
27 | + | |
28 | +@router.post("/", response_model=AssetOut) | |
29 | +def register_asset(req: AssetRegisterRequest, db: Session = Depends(get_db), auth: dict = Depends(require_admin)): | |
30 | + if not req.bbox or len(req.bbox) != 4: | |
31 | + raise HTTPException(status_code=400, detail="bbox must be [x1, y1, x2, y2]") | |
32 | + spatial_memory.register_or_update_asset( | |
33 | + asset_name=req.asset_name, | |
34 | + owner_id=req.owner_person_id, | |
35 | + bbox=[int(v) for v in req.bbox], | |
36 | + camera_id=req.camera_id, | |
37 | + ) | |
38 | + asset = db.query(AssetLog).filter(AssetLog.asset_name == req.asset_name).first() | |
39 | + if not asset: | |
40 | + raise HTTPException(status_code=500, detail="Failed to register asset") | |
41 | + return AssetOut.model_validate(asset) | |
42 | + | |
43 | + | |
44 | +@router.delete("/{asset_name}") | |
45 | +def delete_asset(asset_name: str, auth: dict = Depends(require_admin)): | |
46 | + if not spatial_memory.delete_asset(asset_name): | |
47 | + raise HTTPException(status_code=404, detail="Asset not found") | |
48 | + return {"status": "success", "message": f"Deleted asset {asset_name}"} | |
0 | 49 | |
▼backend/app/routes/auth.py
+73-0
| @@ -0,0 +1,73 @@ | ||
1 | +import base64 | |
2 | +import hashlib | |
3 | +import hmac | |
4 | +import json | |
5 | +import time | |
6 | + | |
7 | +from fastapi import APIRouter, Depends, Header, HTTPException, status as http_status | |
8 | +from pydantic import BaseModel | |
9 | + | |
10 | +from app.config import config | |
11 | + | |
12 | + | |
13 | +def _b64(data: bytes) -> str: | |
14 | + return base64.urlsafe_b64encode(data).decode().rstrip("=") | |
15 | + | |
16 | + | |
17 | +def _b64d(data: str) -> bytes: | |
18 | + return base64.urlsafe_b64decode(data + "=" * (-len(data) % 4)) | |
19 | + | |
20 | + | |
21 | +def create_token(username: str, role: str = "admin", expires_in_sec: int = None) -> str: | |
22 | + """Minimal HMAC-SHA256 JWT (no external dependency).""" | |
23 | + ttl = expires_in_sec or (config.JWT_EXPIRE_MINUTES * 60) | |
24 | + header = {"alg": "HS256", "typ": "JWT"} | |
25 | + payload = {"sub": username, "role": role, "exp": int(time.time()) + ttl} | |
26 | + signing_input = f"{_b64(json.dumps(header).encode())}.{_b64(json.dumps(payload).encode())}" | |
27 | + signature = _b64(hmac.new(config.JWT_SECRET.encode(), signing_input.encode(), hashlib.sha256).digest()) | |
28 | + return f"{signing_input}.{signature}" | |
29 | + | |
30 | + | |
31 | +def verify_token(token: str) -> dict: | |
32 | + try: | |
33 | + header_part, payload_part, sig_part = token.split(".") | |
34 | + expected = _b64(hmac.new(config.JWT_SECRET.encode(), f"{header_part}.{payload_part}".encode(), hashlib.sha256).digest()) | |
35 | + if not hmac.compare_digest(expected, sig_part): | |
36 | + raise ValueError("bad signature") | |
37 | + payload = json.loads(_b64d(payload_part)) | |
38 | + if int(payload.get("exp", 0)) < time.time(): | |
39 | + raise ValueError("expired") | |
40 | + return payload | |
41 | + except Exception: | |
42 | + raise HTTPException(status_code=http_status.HTTP_401_UNAUTHORIZED, detail="Invalid or expired session") | |
43 | + | |
44 | + | |
45 | +def require_admin(authorization: str = Header(None)) -> dict: | |
46 | + if not authorization or not authorization.lower().startswith("bearer "): | |
47 | + raise HTTPException(status_code=401, detail="Missing authorization token") | |
48 | + payload = verify_token(authorization[7:]) | |
49 | + if payload.get("role") != "admin": | |
50 | + raise HTTPException(status_code=403, detail="Admin privileges required") | |
51 | + return payload | |
52 | + | |
53 | + | |
54 | +# ---- FastAPI router ---- | |
55 | +router = APIRouter(prefix="/api/auth", tags=["Authentication & Sessions"]) | |
56 | + | |
57 | + | |
58 | +class LoginRequest(BaseModel): | |
59 | + username: str | |
60 | + password: str | |
61 | + | |
62 | + | |
63 | +@router.post("/login") | |
64 | +def login(req: LoginRequest): | |
65 | + if req.username != config.ADMIN_USERNAME or req.password != config.ADMIN_PASSWORD: | |
66 | + raise HTTPException(status_code=401, detail="Invalid credentials") | |
67 | + token = create_token(req.username, role="admin") | |
68 | + return {"status": "success", "token": token, "role": "admin", "username": req.username} | |
69 | + | |
70 | + | |
71 | +@router.get("/me") | |
72 | +def me(auth: dict = Depends(require_admin)): | |
73 | + return {"status": "success", "username": auth.get("sub"), "role": auth.get("role")} | |
0 | 74 | |
▼backend/app/routes/copilot.py
+17-0
| @@ -0,0 +1,17 @@ | ||
1 | +from pydantic import BaseModel | |
2 | +from fastapi import APIRouter | |
3 | +from app.services.genai_engine import genai_engine | |
4 | + | |
5 | +router = APIRouter(prefix="/api/copilot", tags=["Gemini AI Decision Copilot"]) | |
6 | + | |
7 | +class CopilotQueryRequest(BaseModel): | |
8 | + query: str | |
9 | + | |
10 | +@router.post("/query") | |
11 | +def query_copilot(req: CopilotQueryRequest): | |
12 | + answer = genai_engine.query_copilot(req.query) | |
13 | + return { | |
14 | + "status": "success", | |
15 | + "query": req.query, | |
16 | + "response": answer | |
17 | + } | |
0 | 18 | |
▼backend/app/routes/metrics.py
+49-0
| @@ -0,0 +1,49 @@ | ||
1 | +"""Prometheus text metrics for fleet/analysis gauges (text/plain scrape endpoint).""" | |
2 | + | |
3 | +import time | |
4 | + | |
5 | +from fastapi import APIRouter, Depends, Response | |
6 | +from sqlalchemy.orm import Session | |
7 | + | |
8 | +from app.db.sqlite_db import get_db | |
9 | +from app.db.models import IncidentAlert | |
10 | +from app.services.analysis_scheduler import analysis_scheduler | |
11 | +from app.services.camera_manager import camera_manager | |
12 | + | |
13 | +router = APIRouter(tags=["Metrics"]) | |
14 | + | |
15 | +_START_TIME = time.time() | |
16 | + | |
17 | + | |
18 | +@router.get("/api/metrics") | |
19 | +def prometheus_metrics(db: Session = Depends(get_db)): | |
20 | + cameras = camera_manager.list_cameras() | |
21 | + total = len(cameras) | |
22 | + online = sum(1 for c in cameras if c["online"]) | |
23 | + busy = sum(1 for c in cameras if c["online"] and c.get("scene_state") in ("BUSY", "CROWDED")) | |
24 | + unresolved = db.query(IncidentAlert).filter(IncidentAlert.is_resolved == False).count() | |
25 | + | |
26 | + lines = [ | |
27 | + "# HELP aegis_analysis_budget_available Global face-analysis token budget remaining.", | |
28 | + "# TYPE aegis_analysis_budget_available gauge", | |
29 | + f"aegis_analysis_budget_available {analysis_scheduler.available()}", | |
30 | + "# HELP aegis_analysis_queue Depth of the bounded analysis work queue.", | |
31 | + "# TYPE aegis_analysis_queue gauge", | |
32 | + f"aegis_analysis_queue {analysis_scheduler.queue_depth()}", | |
33 | + "# HELP aegis_cameras_total Configured cameras.", | |
34 | + "# TYPE aegis_cameras_total gauge", | |
35 | + f"aegis_cameras_total {total}", | |
36 | + "# HELP aegis_cameras_online Cameras delivering frames right now.", | |
37 | + "# TYPE aegis_cameras_online gauge", | |
38 | + f"aegis_cameras_online {online}", | |
39 | + "# HELP aegis_cameras_busy Cameras in a busy/crowded scene.", | |
40 | + "# TYPE aegis_cameras_busy gauge", | |
41 | + f"aegis_cameras_busy {busy}", | |
42 | + "# HELP aegis_alerts_unresolved Open incidents.", | |
43 | + "# TYPE aegis_alerts_unresolved gauge", | |
44 | + f"aegis_alerts_unresolved {unresolved}", | |
45 | + "# HELP aegis_uptime_seconds Process uptime.", | |
46 | + "# TYPE aegis_uptime_seconds gauge", | |
47 | + f"aegis_uptime_seconds {round(time.time() - _START_TIME, 1)}", | |
48 | + ] | |
49 | + return Response(content="\n".join(lines) + "\n", media_type="text/plain; version=0.0.4") | |
0 | 50 | |
▼backend/app/routes/personnel.py
+108-0
| @@ -0,0 +1,108 @@ | ||
1 | +import os | |
2 | +import json | |
3 | +import shutil | |
4 | +from typing import List, Optional | |
5 | +from fastapi import APIRouter, Depends, UploadFile, File, Form, HTTPException | |
6 | +from sqlalchemy.orm import Session | |
7 | +from app.db.sqlite_db import get_db | |
8 | +from app.db.models import Personnel | |
9 | +from app.db.vector_db import vector_db | |
10 | +from app.services.ingestion_service import ingestion_service | |
11 | +from app.config import config | |
12 | +from app.routes.auth import require_admin | |
13 | +from app.schemas.api import PersonnelListOut, PersonnelOut | |
14 | + | |
15 | +router = APIRouter(prefix="/api/personnel", tags=["Personnel Management"]) | |
16 | + | |
17 | + | |
18 | +@router.get("/", response_model=PersonnelListOut) | |
19 | +def get_all_personnel(db: Session = Depends(get_db)): | |
20 | + personnel = db.query(Personnel).all() | |
21 | + return PersonnelListOut(count=len(personnel), data=[PersonnelOut.model_validate(p) for p in personnel]) | |
22 | + | |
23 | + | |
24 | +@router.get("/{person_id}", response_model=PersonnelOut) | |
25 | +def get_personnel_by_id(person_id: str, db: Session = Depends(get_db)): | |
26 | + person = db.query(Personnel).filter(Personnel.person_id == person_id).first() | |
27 | + if not person: | |
28 | + raise HTTPException(status_code=404, detail="Personnel not found") | |
29 | + return PersonnelOut.model_validate(person) | |
30 | + | |
31 | + | |
32 | +@router.post("/enroll") | |
33 | +async def enroll_personnel( | |
34 | + person_id: str = Form(...), | |
35 | + name: str = Form(...), | |
36 | + role: str = Form("Employee"), | |
37 | + department: str = Form("General"), | |
38 | + email: str = Form(""), | |
39 | + phone: str = Form(""), | |
40 | + photo: UploadFile = File(...), | |
41 | + db: Session = Depends(get_db), | |
42 | + auth: dict = Depends(require_admin), | |
43 | +): | |
44 | + """ | |
45 | + Plug-and-Play Personnel Enrollment API. | |
46 | + Saves photo to data/known_persons/<person_id>_<name>/, computes vector embedding, | |
47 | + upserts into ChromaDB and SQLite DB. | |
48 | + """ | |
49 | + if not photo.filename.lower().endswith((".jpg", ".jpeg", ".png")): | |
50 | + raise HTTPException(status_code=400, detail="Invalid image format. Only JPG/PNG allowed.") | |
51 | + | |
52 | + folder_name = f"{person_id}_{name.replace(' ', '_')}" | |
53 | + target_dir = os.path.join(config.KNOWN_PERSONS_DIR, folder_name) | |
54 | + os.makedirs(target_dir, exist_ok=True) | |
55 | + | |
56 | + img_save_path = os.path.join(target_dir, photo.filename) | |
57 | + with open(img_save_path, "wb") as buffer: | |
58 | + shutil.copyfileobj(photo.file, buffer) | |
59 | + | |
60 | + metadata = { | |
61 | + "name": name, | |
62 | + "role": role, | |
63 | + "department": department, | |
64 | + "email": email, | |
65 | + "phone": phone | |
66 | + } | |
67 | + | |
68 | + info_json_path = os.path.join(target_dir, "info.json") | |
69 | + with open(info_json_path, "w") as f: | |
70 | + json.dump(metadata, f, indent=2) | |
71 | + | |
72 | + # Process image for VectorDB & SQLite sync | |
73 | + ingestion_service.register_person_from_folder(target_dir) | |
74 | + | |
75 | + return { | |
76 | + "status": "success", | |
77 | + "message": f"Successfully enrolled {name} ({person_id})", | |
78 | + "data": metadata | |
79 | + } | |
80 | + | |
81 | + | |
82 | +@router.get("/photo/{person_id}") | |
83 | +def get_person_photo(person_id: str, db: Session = Depends(get_db)): | |
84 | + """Return path of the person's enrolled photo (if any) for avatar rendering.""" | |
85 | + folder = None | |
86 | + for item in os.listdir(config.KNOWN_PERSONS_DIR): | |
87 | + if item.startswith(person_id + "_") or item == person_id: | |
88 | + folder = os.path.join(config.KNOWN_PERSONS_DIR, item) | |
89 | + break | |
90 | + if not folder or not os.path.isdir(folder): | |
91 | + raise HTTPException(status_code=404, detail="No photo on record") | |
92 | + for f in os.listdir(folder): | |
93 | + if f.lower().endswith((".jpg", ".jpeg", ".png")): | |
94 | + return {"status": "success", "photo_path": os.path.join(folder, f)} | |
95 | + raise HTTPException(status_code=404, detail="No photo on record") | |
96 | + | |
97 | + | |
98 | +@router.delete("/{person_id}") | |
99 | +def delete_personnel(person_id: str, db: Session = Depends(get_db), auth: dict = Depends(require_admin)): | |
100 | + person = db.query(Personnel).filter(Personnel.person_id == person_id).first() | |
101 | + if not person: | |
102 | + raise HTTPException(status_code=404, detail="Personnel not found") | |
103 | + | |
104 | + vector_db.delete_person_embeddings(person_id) | |
105 | + db.delete(person) | |
106 | + db.commit() | |
107 | + | |
108 | + return {"status": "success", "message": f"Deleted personnel {person_id}"} | |
0 | 109 | |
▼backend/app/routes/stream.py
+209-0
| @@ -0,0 +1,209 @@ | ||
1 | +import re | |
2 | +import time | |
3 | +from pathlib import Path | |
4 | +from typing import Optional | |
5 | + | |
6 | +from fastapi import APIRouter, Depends, HTTPException, Response, WebSocket, WebSocketDisconnect | |
7 | +from fastapi.responses import FileResponse, StreamingResponse | |
8 | +from pydantic import BaseModel, Field | |
9 | + | |
10 | +from app.config import config | |
11 | +from app.services.camera_manager import camera_manager | |
12 | +from app.services.camera_registry import camera_registry | |
13 | +from app.services.analysis_scheduler import analysis_scheduler | |
14 | +from app.services.alert_dispatcher import alert_dispatcher | |
15 | +from app.routes.auth import require_admin | |
16 | + | |
17 | +router = APIRouter(prefix="/api/stream", tags=["Video Stream & Surveillance"]) | |
18 | + | |
19 | +_ID_RE = re.compile(r"^[A-Za-z0-9][A-Za-z0-9_.-]{1,63}$") | |
20 | + | |
21 | + | |
22 | +class CameraCreate(BaseModel): | |
23 | + camera_id: str = Field(min_length=2, max_length=64) | |
24 | + name: str = Field(min_length=1, max_length=128) | |
25 | + source: str = Field(min_length=1) | |
26 | + type: str = "gate" | |
27 | + resolution: Optional[str] = "640x480" | |
28 | + fps_limit: Optional[int] = Field(default=None, ge=1, le=60) | |
29 | + | |
30 | + | |
31 | +class CameraUpdate(BaseModel): | |
32 | + name: Optional[str] = Field(default=None, min_length=1, max_length=128) | |
33 | + source: Optional[str] = None | |
34 | + type: Optional[str] = None | |
35 | + resolution: Optional[str] = None | |
36 | + fps_limit: Optional[int] = Field(default=None, ge=1, le=60) | |
37 | + active: Optional[bool] = None | |
38 | + | |
39 | + | |
40 | +def _redact(cam: dict) -> dict: | |
41 | + clone = dict(cam) | |
42 | + clone["source"] = camera_registry.redact_source(str(cam.get("source"))) | |
43 | + return clone | |
44 | + | |
45 | + | |
46 | +def _iter_camera(camera_id: str): | |
47 | + """Serve the latest annotated frame pushed by the camera worker for `camera_id`.""" | |
48 | + interval = config.STREAM_BROADCAST_INTERVAL_MS / 1000.0 | |
49 | + while True: | |
50 | + frame = camera_manager.get_latest(camera_id) | |
51 | + yield (b'--frame\r\n' | |
52 | + b'Content-Type: image/jpeg\r\n\r\n' + frame.jpeg + b'\r\n') | |
53 | + time.sleep(interval) | |
54 | + | |
55 | + | |
56 | +@router.get("/video_feed") | |
57 | +async def video_feed(camera_id: str = "cam_entrance"): | |
58 | + return StreamingResponse( | |
59 | + _iter_camera(camera_id), | |
60 | + media_type="multipart/x-mixed-replace; boundary=frame", | |
61 | + ) | |
62 | + | |
63 | + | |
64 | +@router.get("/snapshot") | |
65 | +async def snapshot(camera_id: str = "cam_entrance"): | |
66 | + frame = camera_manager.get_latest(camera_id) | |
67 | + return Response(content=frame.jpeg, media_type="image/jpeg") | |
68 | + | |
69 | + | |
70 | +@router.get("/cameras") | |
71 | +async def cameras(): | |
72 | + joined = [_redact(c) for c in camera_manager.list_cameras()] | |
73 | + return {"status": "success", "cameras": joined} | |
74 | + | |
75 | + | |
76 | +@router.get("/cameras/{camera_id}") | |
77 | +async def camera_detail(camera_id: str): | |
78 | + cam = camera_manager.get_camera(camera_id) | |
79 | + if cam is None: | |
80 | + raise HTTPException(status_code=404, detail="Camera not found") | |
81 | + return {"status": "success", "camera": _redact(cam)} | |
82 | + | |
83 | + | |
84 | +@router.get("/cameras/{camera_id}/history") | |
85 | +async def camera_history(camera_id: str, limit: int = 180): | |
86 | + """Per-camera situational trace (motion, scene, people) for sparklines.""" | |
87 | + cfg = camera_registry.get(camera_id) | |
88 | + if cfg is None: | |
89 | + raise HTTPException(status_code=404, detail="Camera not found") | |
90 | + limit = max(1, min(720, limit)) | |
91 | + worker = camera_manager.workers.get(camera_id) | |
92 | + history = worker.get_history(limit=limit) if worker else [] | |
93 | + return {"status": "success", "camera_id": camera_id, "history": history} | |
94 | + | |
95 | + | |
96 | +@router.get("/clips") | |
97 | +async def list_clips(camera_id: str = ""): | |
98 | + """Evidence clips saved for an incident (empty = across the whole fleet).""" | |
99 | + workers = camera_manager.workers | |
100 | + if camera_id: | |
101 | + if camera_id not in workers: | |
102 | + raise HTTPException(status_code=404, detail="Camera not found or inactive") | |
103 | + clips = workers[camera_id].list_clips() | |
104 | + else: | |
105 | + clips = [] | |
106 | + for cam, worker in workers.items(): | |
107 | + clips.extend(worker.list_clips()) | |
108 | + clips.sort(key=lambda c: c["timestamp"], reverse=True) | |
109 | + return {"status": "success", "count": len(clips), "clips": clips[:200]} | |
110 | + | |
111 | + | |
112 | +@router.get("/clips/{filename}") | |
113 | +async def serve_clip(filename: str): | |
114 | + """Serve a saved evidence clip (path-traversal safe).""" | |
115 | + safe = Path(filename).name | |
116 | + if not safe.endswith(".mp4"): | |
117 | + raise HTTPException(status_code=400, detail="Invalid clip name") | |
118 | + path = config.CLIP_DIR / safe | |
119 | + if not path.is_file(): | |
120 | + raise HTTPException(status_code=404, detail="Clip not found") | |
121 | + return FileResponse(path, media_type="video/mp4") | |
122 | + | |
123 | + | |
124 | +@router.get("/fleet/summary") | |
125 | +async def fleet_summary(): | |
126 | + cameras = camera_manager.list_cameras() | |
127 | + busy = sum(1 for c in cameras if c["online"] and c.get("scene_state") in ("BUSY", "CROWDED")) | |
128 | + return { | |
129 | + "status": "success", | |
130 | + "total": len(cameras), | |
131 | + "online": sum(1 for c in cameras if c["online"]), | |
132 | + "active_cameras": sum(1 for c in cameras if c["active"]), | |
133 | + "busy": busy, | |
134 | + "analysis_budget_available": analysis_scheduler.available(), | |
135 | + "analysis_queue": analysis_scheduler.queue_depth(), | |
136 | + } | |
137 | + | |
138 | + | |
139 | +@router.post("/cameras", status_code=201) | |
140 | +async def add_camera(req: CameraCreate, auth: dict = Depends(require_admin)): | |
141 | + if not _ID_RE.match(req.camera_id): | |
142 | + raise HTTPException(status_code=422, detail="camera_id must match ^[A-Za-z0-9][A-Za-z0-9_.-]{1,63}$") | |
143 | + if req.type not in config.CAMERA_TYPES: | |
144 | + raise HTTPException(status_code=400, detail=f"type must be one of {', '.join(config.CAMERA_TYPES)}") | |
145 | + if req.resolution and not re.match(r"^\d{3,5}x\d{3,5}$", req.resolution): | |
146 | + raise HTTPException(status_code=400, detail="resolution must look like WxH, e.g. 640x480") | |
147 | + try: | |
148 | + camera_registry.normalize_source(req.source) | |
149 | + except ValueError as e: | |
150 | + raise HTTPException(status_code=400, detail=str(e)) | |
151 | + if camera_registry.exists(req.camera_id): | |
152 | + raise HTTPException(status_code=409, detail=f"camera {req.camera_id!r} already registered") | |
153 | + try: | |
154 | + cfg = camera_manager.add_camera( | |
155 | + camera_id=req.camera_id, | |
156 | + name=req.name, | |
157 | + source=req.source, | |
158 | + cam_type=req.type, | |
159 | + resolution=req.resolution, | |
160 | + fps_limit=req.fps_limit, | |
161 | + ) | |
162 | + except Exception as e: | |
163 | + raise HTTPException(status_code=500, detail=f"failed to start camera: {e}") | |
164 | + return {"status": "success", "camera": _redact(cfg)} | |
165 | + | |
166 | + | |
167 | +@router.patch("/cameras/{camera_id}") | |
168 | +def update_camera(camera_id: str, req: CameraUpdate, auth: dict = Depends(require_admin)): | |
169 | + fields = {k: v for k, v in req.model_dump().items() if v is not None} | |
170 | + if "type" in fields and fields["type"] not in config.CAMERA_TYPES: | |
171 | + raise HTTPException(status_code=400, detail=f"type must be one of {', '.join(config.CAMERA_TYPES)}") | |
172 | + if "resolution" in fields and fields["resolution"] and not re.match(r"^\d{3,5}x\d{3,5}$", fields["resolution"]): | |
173 | + raise HTTPException(status_code=400, detail="resolution must look like WxH, e.g. 640x480") | |
174 | + if "source" in fields: | |
175 | + try: | |
176 | + camera_registry.normalize_source(fields["source"]) | |
177 | + except ValueError as e: | |
178 | + raise HTTPException(status_code=400, detail=str(e)) | |
179 | + cfg = camera_manager.update_camera(camera_id, **fields) | |
180 | + if cfg is None: | |
181 | + raise HTTPException(status_code=404, detail="Camera not found") | |
182 | + return {"status": "success", "camera": _redact(cfg)} | |
183 | + | |
184 | + | |
185 | +@router.delete("/cameras/{camera_id}") | |
186 | +def delete_camera(camera_id: str, auth: dict = Depends(require_admin)): | |
187 | + removed = camera_manager.remove_camera(camera_id) | |
188 | + if not removed: | |
189 | + raise HTTPException(status_code=404, detail="Camera not found") | |
190 | + return {"status": "success", "message": f"Camera {camera_id} unregistered"} | |
191 | + | |
192 | + | |
193 | +@router.post("/cameras/{camera_id}/restart") | |
194 | +def restart_camera(camera_id: str, auth: dict = Depends(require_admin)): | |
195 | + if camera_registry.get(camera_id) is None: | |
196 | + raise HTTPException(status_code=404, detail="Camera not found") | |
197 | + camera_manager.restart(camera_id) | |
198 | + return {"status": "success", "camera": _redact(camera_manager.get_camera(camera_id))} | |
199 | + | |
200 | + | |
201 | +@router.websocket("/ws") | |
202 | +async def websocket_endpoint(websocket: WebSocket): | |
203 | + await alert_dispatcher.connect(websocket) | |
204 | + try: | |
205 | + while True: | |
206 | + await websocket.receive_text() | |
207 | + await websocket.send_text(f'{{"type": "PONG", "timestamp": "{utcnow().isoformat()}"}}') | |
208 | + except WebSocketDisconnect: | |
209 | + alert_dispatcher.disconnect(websocket) | |
0 | 210 | |
▼backend/app/routes/system.py
+86-0
| @@ -0,0 +1,86 @@ | ||
1 | +import time | |
2 | +from app.utils.timeutil import utcnow | |
3 | +from datetime import datetime | |
4 | + | |
5 | +from fastapi import APIRouter, Depends, Query | |
6 | +from sqlalchemy.orm import Session | |
7 | + | |
8 | +from app.config import config | |
9 | +from app.db.sqlite_db import get_db | |
10 | +from app.db.models import AssetLog, AttendanceLog, HealthLog, IncidentAlert, Personnel, ProductivityLog | |
11 | +from app.db.vector_db import vector_db | |
12 | +from app.routes.auth import require_admin | |
13 | +from app.services.camera_manager import camera_manager | |
14 | + | |
15 | +_START_TIME = time.time() | |
16 | + | |
17 | +router = APIRouter(prefix="/api/system", tags=["System Health & Admin"]) | |
18 | + | |
19 | +_SENSITIVE_SUBSTR = ("JWT", "PASSWORD", "TOKEN", "SECRET", "API_KEY", "KEY") | |
20 | + | |
21 | + | |
22 | +@router.get("/health") | |
23 | +def system_health(db: Session = Depends(get_db)): | |
24 | + cameras = [] | |
25 | + for cam in camera_manager.list_cameras(): | |
26 | + cameras.append({ | |
27 | + "camera_id": cam["camera_id"], | |
28 | + "name": cam.get("name", cam["camera_id"]), | |
29 | + "type": cam.get("type", "gate"), | |
30 | + "online": bool(cam.get("online", False)), | |
31 | + "last_frame_age": cam.get("last_frame_age"), | |
32 | + }) | |
33 | + | |
34 | + today_start = utcnow().replace(hour=0, minute=0, second=0, microsecond=0) | |
35 | + counts = { | |
36 | + "personnel": db.query(Personnel).count(), | |
37 | + "attendance_today": db.query(AttendanceLog).filter(AttendanceLog.timestamp >= today_start).count(), | |
38 | + "productivity": db.query(ProductivityLog).count(), | |
39 | + "health_logs": db.query(HealthLog).count(), | |
40 | + "alerts": db.query(IncidentAlert).count(), | |
41 | + "unresolved_alerts": db.query(IncidentAlert).filter(IncidentAlert.is_resolved == False).count(), | |
42 | + "assets": db.query(AssetLog).count(), | |
43 | + } | |
44 | + | |
45 | + try: | |
46 | + vector_stats = {"embeddings": vector_db.count_embeddings(), "persons_indexed": vector_db.count_persons()} | |
47 | + except Exception: | |
48 | + vector_stats = {"embeddings": 0, "persons_indexed": 0} | |
49 | + | |
50 | + return { | |
51 | + "status": "success", | |
52 | + "uptime_seconds": round(time.time() - _START_TIME, 1), | |
53 | + "version": config.VERSION, | |
54 | + "timestamp": utcnow().isoformat(), | |
55 | + "cameras": cameras, | |
56 | + "counts": counts, | |
57 | + "vector_stats": vector_stats, | |
58 | + } | |
59 | + | |
60 | + | |
61 | +@router.get("/logs") | |
62 | +def system_logs( | |
63 | + lines: int = Query(40, ge=1, le=500), | |
64 | + auth: dict = Depends(require_admin), | |
65 | +): | |
66 | + """Tail of the active application log (admin only).""" | |
67 | + log_file = config.LOGS_DIR / "aegisvision.log" | |
68 | + if not log_file.exists(): | |
69 | + return {"status": "success", "lines": []} | |
70 | + with open(log_file, "r", encoding="utf-8", errors="replace") as f: | |
71 | + tail = f.readlines()[-lines:] | |
72 | + return {"status": "success", "lines": [line.rstrip("\n") for line in tail]} | |
73 | + | |
74 | + | |
75 | +@router.get("/config") | |
76 | +def system_config(auth: dict = Depends(require_admin)): | |
77 | + """Sanitized runtime configuration overview (admin only; secrets omitted).""" | |
78 | + safe = {} | |
79 | + for key, value in config.model_dump().items(): | |
80 | + if any(s in key.upper() for s in _SENSITIVE_SUBSTR): | |
81 | + continue | |
82 | + if isinstance(value, (str, int, float, bool)): | |
83 | + safe[key] = value | |
84 | + safe.pop("CAMERA_STREAMS", None) | |
85 | + safe["cameras"] = {cid: cfg.get("name") for cid, cfg in config.CAMERA_STREAMS.items()} | |
86 | + return {"status": "success", "config": safe} | |
0 | 87 | |
▼backend/app/schemas/api.py
+167-0
| @@ -0,0 +1,167 @@ | ||
1 | +"""Pydantic response schemas for AegisVision API. `from_attributes` maps SQLAlchemy rows.""" | |
2 | + | |
3 | +from datetime import datetime | |
4 | +from typing import List, Optional | |
5 | +from pydantic import BaseModel, ConfigDict | |
6 | + | |
7 | + | |
8 | +class ORMModel(BaseModel): | |
9 | + model_config = ConfigDict(from_attributes=True) | |
10 | + | |
11 | + | |
12 | +# ---- Personnel ---- | |
13 | +class PersonnelOut(ORMModel): | |
14 | + person_id: str | |
15 | + name: str | |
16 | + role: str | |
17 | + department: str | |
18 | + email: Optional[str] = None | |
19 | + phone: Optional[str] = None | |
20 | + enrolled_at: Optional[datetime] = None | |
21 | + is_active: bool = True | |
22 | + metadata_json: str = "{}" | |
23 | + | |
24 | + | |
25 | +class PersonnelListOut(BaseModel): | |
26 | + status: str = "success" | |
27 | + count: int = 0 | |
28 | + data: List[PersonnelOut] = [] | |
29 | + | |
30 | + | |
31 | +# ---- Attendance ---- | |
32 | +class AttendanceOut(ORMModel): | |
33 | + id: int | |
34 | + person_id: Optional[str] = None | |
35 | + name: str = "UNKNOWN" | |
36 | + timestamp: Optional[datetime] = None | |
37 | + event_type: str = "ENTRY" | |
38 | + camera_id: str = "cam_entrance" | |
39 | + confidence: float = 0.0 | |
40 | + is_spoof: bool = False | |
41 | + | |
42 | + | |
43 | +class AttendanceListOut(BaseModel): | |
44 | + status: str = "success" | |
45 | + count: int = 0 | |
46 | + data: List[AttendanceOut] = [] | |
47 | + | |
48 | + | |
49 | +# ---- Productivity ---- | |
50 | +class ProductivityOut(ORMModel): | |
51 | + id: int | |
52 | + person_id: str | |
53 | + timestamp: Optional[datetime] = None | |
54 | + status: str = "PRODUCTIVE" | |
55 | + duration_seconds: int = 0 | |
56 | + camera_id: Optional[str] = None | |
57 | + notes: Optional[str] = None | |
58 | + | |
59 | + | |
60 | +class ProductivityListOut(BaseModel): | |
61 | + status: str = "success" | |
62 | + count: int = 0 | |
63 | + data: List[ProductivityOut] = [] | |
64 | + | |
65 | + | |
66 | +# ---- Health ---- | |
67 | +class HealthOut(ORMModel): | |
68 | + id: int | |
69 | + person_id: str | |
70 | + timestamp: Optional[datetime] = None | |
71 | + drowsiness_detected: bool = False | |
72 | + eye_aspect_ratio: float = 0.30 | |
73 | + posture_status: str = "GOOD" | |
74 | + fatigue_score: float = 0.0 | |
75 | + camera_id: Optional[str] = None | |
76 | + | |
77 | + | |
78 | +class HealthListOut(BaseModel): | |
79 | + status: str = "success" | |
80 | + count: int = 0 | |
81 | + data: List[HealthOut] = [] | |
82 | + | |
83 | + | |
84 | +# ---- Assets ---- | |
85 | +class AssetOut(ORMModel): | |
86 | + id: int | |
87 | + asset_name: str | |
88 | + owner_person_id: Optional[str] = None | |
89 | + last_seen_bbox: str = "[]" | |
90 | + camera_id: str = "cam_workspace" | |
91 | + status: str = "NORMAL" | |
92 | + updated_at: Optional[datetime] = None | |
93 | + | |
94 | + | |
95 | +class AssetListOut(BaseModel): | |
96 | + status: str = "success" | |
97 | + count: int = 0 | |
98 | + data: List[AssetOut] = [] | |
99 | + | |
100 | + | |
101 | +# ---- Incidents ---- | |
102 | +class IncidentOut(ORMModel): | |
103 | + id: int | |
104 | + timestamp: Optional[datetime] = None | |
105 | + alert_type: str | |
106 | + severity: str = "WARNING" | |
107 | + description: str | |
108 | + camera_id: Optional[str] = None | |
109 | + person_id: Optional[str] = None | |
110 | + is_resolved: bool = False | |
111 | + | |
112 | + | |
113 | +class IncidentListOut(BaseModel): | |
114 | + status: str = "success" | |
115 | + count: int = 0 | |
116 | + data: List[IncidentOut] = [] | |
117 | + | |
118 | + | |
119 | +# ---- Dashboard ---- | |
120 | +class DashboardSummary(BaseModel): | |
121 | + total_personnel: int = 0 | |
122 | + today_attendance: int = 0 | |
123 | + unresolved_alerts: int = 0 | |
124 | + total_assets: int = 0 | |
125 | + theft_threats: int = 0 | |
126 | + productive_sessions: int = 0 | |
127 | + phone_distractions: int = 0 | |
128 | + bunking_incidents: int = 0 | |
129 | + health_warnings: int = 0 | |
130 | + | |
131 | + | |
132 | +class DashboardOut(BaseModel): | |
133 | + status: str = "success" | |
134 | + timestamp: str | |
135 | + summary: DashboardSummary | |
136 | + | |
137 | + | |
138 | +# ---- Occupancy & person daily summary (Phase 2) ---- | |
139 | +class OccupancyOut(BaseModel): | |
140 | + camera_id: str | |
141 | + currently_inside: int = 0 | |
142 | + peak_inside: int = 0 | |
143 | + total_entries: int = 0 | |
144 | + total_exits: int = 0 | |
145 | + | |
146 | + | |
147 | +class PersonDailyOut(BaseModel): | |
148 | + person_id: str | |
149 | + date: str | |
150 | + active_seconds: int = 0 | |
151 | + idle_seconds: int = 0 | |
152 | + bunk_seconds: int = 0 | |
153 | + phone_seconds: int = 0 | |
154 | + productive_score: float = 0.0 | |
155 | + entries: int = 0 | |
156 | + exits: int = 0 | |
157 | + | |
158 | + | |
159 | +# ---- Copilot ---- | |
160 | +class CopilotQueryRequest(BaseModel): | |
161 | + query: str | |
162 | + | |
163 | + | |
164 | +class CopilotQueryOut(BaseModel): | |
165 | + status: str = "success" | |
166 | + query: str | |
167 | + response: str | |
0 | 168 | |
▼backend/app/services/alert_dispatcher.py
+52-0
| @@ -0,0 +1,52 @@ | ||
1 | +import asyncio | |
2 | +import json | |
3 | +from typing import List | |
4 | +from fastapi import WebSocket | |
5 | + | |
6 | + | |
7 | +class AlertDispatcher: | |
8 | + """Thread-safe WebSocket alert broadcaster. | |
9 | + | |
10 | + `broadcast_sync` may be called from any thread (camera workers, generators); | |
11 | + it schedules the fan-out on the main FastAPI event loop. | |
12 | + """ | |
13 | + | |
14 | + def __init__(self): | |
15 | + self.active_connections: List[WebSocket] = [] | |
16 | + self._loop: asyncio.AbstractEventLoop | None = None | |
17 | + | |
18 | + def bind_loop(self, loop: asyncio.AbstractEventLoop): | |
19 | + self._loop = loop | |
20 | + | |
21 | + async def connect(self, websocket: WebSocket): | |
22 | + await websocket.accept() | |
23 | + self.active_connections.append(websocket) | |
24 | + | |
25 | + def disconnect(self, websocket: WebSocket): | |
26 | + if websocket in self.active_connections: | |
27 | + self.active_connections.remove(websocket) | |
28 | + | |
29 | + async def _broadcast(self, payload: str): | |
30 | + disconnected = [] | |
31 | + for connection in self.active_connections: | |
32 | + try: | |
33 | + await connection.send_text(payload) | |
34 | + except Exception: | |
35 | + disconnected.append(connection) | |
36 | + for conn in disconnected: | |
37 | + self.disconnect(conn) | |
38 | + | |
39 | + def broadcast_sync(self, alert_data: dict): | |
40 | + payload = json.dumps(alert_data) | |
41 | + if self._loop is not None and self.active_connections: | |
42 | + try: | |
43 | + asyncio.run_coroutine_threadsafe(self._broadcast(payload), self._loop) | |
44 | + except RuntimeError: | |
45 | + pass | |
46 | + | |
47 | + @property | |
48 | + def client_count(self) -> int: | |
49 | + return len(self.active_connections) | |
50 | + | |
51 | + | |
52 | +alert_dispatcher = AlertDispatcher() | |
0 | 53 | |
▼backend/app/services/alert_service.py
+97-0
| @@ -0,0 +1,97 @@ | ||
1 | +from app.utils.timeutil import utcnow | |
2 | +from app.db.sqlite_db import SessionLocal | |
3 | +from app.db.models import IncidentAlert | |
4 | +from app.services.alert_dispatcher import alert_dispatcher | |
5 | +from app.services.notify import push_alert | |
6 | + | |
7 | + | |
8 | +def emit_alert( | |
9 | + alert_type: str, | |
10 | + severity: str = "WARNING", | |
11 | + description: str = "", | |
12 | + camera_id: str = "cam_main", | |
13 | + person_id: str | None = None, | |
14 | +) -> IncidentAlert | None: | |
15 | + """Persists an IncidentAlert and pushes it live to dashboard clients. | |
16 | + | |
17 | + Thread-safe: designed to be called from camera workers / services. | |
18 | + """ | |
19 | + db = SessionLocal() | |
20 | + alert = None | |
21 | + try: | |
22 | + alert = IncidentAlert( | |
23 | + alert_type=alert_type, | |
24 | + severity=severity, | |
25 | + description=description, | |
26 | + camera_id=camera_id, | |
27 | + person_id=person_id, | |
28 | + is_resolved=False, | |
29 | + ) | |
30 | + db.add(alert) | |
31 | + db.commit() | |
32 | + db.refresh(alert) | |
33 | + except Exception as e: | |
34 | + db.rollback() | |
35 | + print(f"[AlertService Error] {e}") | |
36 | + finally: | |
37 | + db.close() | |
38 | + | |
39 | + if alert is not None: | |
40 | + payload = { | |
41 | + "type": "ALERT", | |
42 | + "id": alert.id, | |
43 | + "alert_type": alert.alert_type, | |
44 | + "severity": alert.severity, | |
45 | + "description": alert.description, | |
46 | + "camera_id": alert.camera_id, | |
47 | + "person_id": alert.person_id, | |
48 | + "timestamp": alert.timestamp.isoformat() if alert.timestamp else utcnow().isoformat(), | |
49 | + } | |
50 | + alert_dispatcher.broadcast_sync(payload) | |
51 | + push_alert(payload) | |
52 | + | |
53 | + return alert | |
54 | + | |
55 | + | |
56 | +def has_pending_alert(camera_id: str, alert_type: str) -> bool: | |
57 | + """True if an unresolved alert of `alert_type` already exists for the camera.""" | |
58 | + db = SessionLocal() | |
59 | + try: | |
60 | + return ( | |
61 | + db.query(IncidentAlert) | |
62 | + .filter( | |
63 | + IncidentAlert.camera_id == camera_id, | |
64 | + IncidentAlert.alert_type == alert_type, | |
65 | + IncidentAlert.is_resolved == False, | |
66 | + ) | |
67 | + .first() | |
68 | + is not None | |
69 | + ) | |
70 | + finally: | |
71 | + db.close() | |
72 | + | |
73 | + | |
74 | +def resolve_camera_alerts(camera_id: str, alert_type: str) -> int: | |
75 | + """Mark all open alerts of `alert_type` for the camera as resolved. Returns count.""" | |
76 | + db = SessionLocal() | |
77 | + count = 0 | |
78 | + try: | |
79 | + rows = ( | |
80 | + db.query(IncidentAlert) | |
81 | + .filter( | |
82 | + IncidentAlert.camera_id == camera_id, | |
83 | + IncidentAlert.alert_type == alert_type, | |
84 | + IncidentAlert.is_resolved == False, | |
85 | + ) | |
86 | + .all() | |
87 | + ) | |
88 | + for row in rows: | |
89 | + row.is_resolved = True | |
90 | + db.commit() | |
91 | + count = len(rows) | |
92 | + except Exception as e: | |
93 | + db.rollback() | |
94 | + print(f"[AlertService Error] {e}") | |
95 | + finally: | |
96 | + db.close() | |
97 | + return count | |
0 | 98 | |
▼backend/app/services/analysis_scheduler.py
+69-0
| @@ -0,0 +1,69 @@ | ||
1 | +"""Shared analysis budget for the camera fleet. | |
2 | + | |
3 | +Every camera already refuses to run the face pipeline faster than its local | |
4 | +`CAMERA_ANALYZE_INTERVAL_SEC` and skips idle scenes entirely. On top of that, | |
5 | +a corporate site with dozens of cameras can still peg the CPU if several very | |
6 | +busy cameras detect at once. `AnalysisScheduler` fixes that with two levers: | |
7 | + | |
8 | +- a *global token bucket*: across all cameras the expensive pipeline runs at | |
9 | + most `CAMERA_GLOBAL_ANALYZE_FPS` times per second (the hard ceiling); | |
10 | +- a *bounded thread pool*: the detections that DO run are parallelised across | |
11 | + `CAMERA_ANALYSIS_POOL_SIZE` threads, so busy cameras overlap instead of | |
12 | + serialising on the GIL. | |
13 | + | |
14 | +Forced re-analysis (a still gate camera hitting its max-gap timer) bypasses the | |
15 | +bucket — it is already self-limiting to one run per camera per gap, so it can | |
16 | +never flood the pool. | |
17 | +""" | |
18 | + | |
19 | +import threading | |
20 | +import time | |
21 | +from concurrent.futures import Future, ThreadPoolExecutor | |
22 | +from typing import Callable | |
23 | + | |
24 | +from app.config import config | |
25 | + | |
26 | + | |
27 | +class AnalysisScheduler: | |
28 | + def __init__(self): | |
29 | + self._pool = ThreadPoolExecutor( | |
30 | + max_workers=max(1, config.CAMERA_ANALYSIS_POOL_SIZE), | |
31 | + thread_name_prefix="cam-analysis", | |
32 | + ) | |
33 | + self._bucket: float = float(config.CAMERA_GLOBAL_ANALYZE_FPS) | |
34 | + self._last_refill = time.time() | |
35 | + self._lock = threading.Lock() | |
36 | + | |
37 | + @property | |
38 | + def _capacity(self) -> float: | |
39 | + return max(1.0, config.CAMERA_GLOBAL_ANALYZE_FPS) | |
40 | + | |
41 | + def _refill(self): | |
42 | + now = time.time() | |
43 | + self._bucket = min(self._capacity, self._bucket + self._capacity * (now - self._last_refill)) | |
44 | + self._last_refill = now | |
45 | + | |
46 | + def try_acquire(self) -> bool: | |
47 | + """Consume one global analysis slot if it is available.""" | |
48 | + with self._lock: | |
49 | + self._refill() | |
50 | + if self._bucket >= 1.0: | |
51 | + self._bucket -= 1.0 | |
52 | + return True | |
53 | + return False | |
54 | + | |
55 | + def available(self) -> float: | |
56 | + with self._lock: | |
57 | + self._refill() | |
58 | + return round(self._bucket, 2) | |
59 | + | |
60 | + def run(self, fn: Callable): | |
61 | + """Submit `fn` to the bounded pool. Caller owns the returned future.""" | |
62 | + return self._pool.submit(fn) | |
63 | + | |
64 | + def queue_depth(self) -> int: | |
65 | + pending = self._pool._work_queue.qsize() # bound to this scheduler | |
66 | + return pending | |
67 | + | |
68 | + | |
69 | +analysis_scheduler = AnalysisScheduler() | |
0 | 70 | |
▼backend/app/services/camera_manager.py
+661-0
| @@ -0,0 +1,661 @@ | ||
1 | +import os | |
2 | +import threading | |
3 | +import time | |
4 | +from collections import deque | |
5 | +from dataclasses import dataclass, field | |
6 | +from datetime import datetime, timezone | |
7 | +from typing import Any, Dict, List, Optional | |
8 | + | |
9 | +import cv2 | |
10 | +import numpy as np | |
11 | + | |
12 | +from app.config import config | |
13 | +from app.services.camera_registry import camera_registry | |
14 | +from app.services.analysis_scheduler import analysis_scheduler | |
15 | +from app.services.face_engine import face_engine | |
16 | +from app.services.productivity_engine import productivity_engine | |
17 | +from app.services.health_sentinel import health_sentinel | |
18 | +from app.services.spatial_memory import spatial_memory | |
19 | +from app.services.alert_service import emit_alert | |
20 | +from app.db.sqlite_db import SessionLocal | |
21 | +from app.db.models import AttendanceLog | |
22 | + | |
23 | + | |
24 | +@dataclass | |
25 | +class CameraFrame: | |
26 | + camera_id: str | |
27 | + timestamp: float | |
28 | + jpeg: bytes | |
29 | + detections: list = field(default_factory=list) | |
30 | + offline: bool = False | |
31 | + | |
32 | + | |
33 | +class CameraWorker(threading.Thread): | |
34 | + """Runs the per-camera pipeline under a low-compute time budget. | |
35 | + | |
36 | + The expensive face pipeline does not run on every captured frame. Each | |
37 | + frame only pays for: grab, downscale-to-grayscale, and a cheap frame-diff. | |
38 | + The full detect → track → embed → label → analytics pipeline runs at most | |
39 | + once per `CAMERA_ANALYZE_INTERVAL_SEC`, and only when motion is seen — | |
40 | + unless the max-gap timer forces a re-analysis so gate/occupancy never goes | |
41 | + stale in a perfectly still scene. JPEG publish is throttled separately, so | |
42 | + idle cameras cost almost nothing beyond capture. | |
43 | + """ | |
44 | + | |
45 | + ATTENDANCE_COOLDOWN_SECONDS = 30 | |
46 | + | |
47 | + def __init__(self, camera_id: str, camera_config: dict): | |
48 | + super().__init__(name=f"cam-{camera_id}", daemon=True) | |
49 | + self.camera_id = camera_id | |
50 | + self.cam_label = camera_config.get("name", camera_id) | |
51 | + self.source = camera_config.get("source", 0) | |
52 | + self.cam_type = camera_config.get("type", "gate") | |
53 | + self.resolution = camera_config.get("resolution") or config.CAMERA_RESOLUTION | |
54 | + self.fps_limit = camera_config.get("fps_limit") or config.CAMERA_FPS_LIMIT | |
55 | + | |
56 | + self._latest = CameraFrame(camera_id=camera_id, timestamp=0.0, jpeg=b"") | |
57 | + self._lock = threading.Lock() | |
58 | + self._shutdown = threading.Event() | |
59 | + self._presence: dict = {} | |
60 | + self._last_attendance: dict = {} | |
61 | + self._peak = 0 | |
62 | + self._today_entries = 0 | |
63 | + self._today_exits = 0 | |
64 | + | |
65 | + # Low-compute bookkeeping (worker thread only). | |
66 | + self._last_analyze = 0.0 | |
67 | + self._last_publish = 0.0 | |
68 | + self._start_time = time.time() | |
69 | + self._prev_gray: Optional[np.ndarray] = None | |
70 | + self._analyzed_frames = 0 | |
71 | + self._captured_frames = 0 | |
72 | + self._motion_level = 0.0 | |
73 | + self._scene_state = "IDLE" | |
74 | + self._people_count = 0 | |
75 | + self._history: deque = deque(maxlen=720) | |
76 | + self._last_history_ts = 0.0 | |
77 | + | |
78 | + # Feed health + fleet ops. | |
79 | + self._last_frame_time = 0.0 | |
80 | + self._offline_since: Optional[float] = None | |
81 | + | |
82 | + # Per-minute activity accumulator, drained by the fleet housekeeper. | |
83 | + self._activity_minutes: Dict[str, dict] = {} | |
84 | + | |
85 | + # Evidence clip buffer (bounded ring of (ts, jpeg) at CLIP_FPS). | |
86 | + self._clip_buf: deque = deque(maxlen=int(config.CLIP_SECONDS * config.CLIP_FPS)) | |
87 | + self._last_clip_frame = 0.0 | |
88 | + self._last_clip_save = 0.0 | |
89 | + self._clip_lock = threading.Lock() | |
90 | + | |
91 | + # Analysis telemetry + adaptive capture pacing. | |
92 | + self._analysis_latency_ms: deque = deque(maxlen=50) | |
93 | + self._defer_stamps: deque = deque(maxlen=64) | |
94 | + self._adaptive_fps = int(self.fps_limit or 0) | |
95 | + | |
96 | + # ---- lifecycle ---- | |
97 | + def run(self): | |
98 | + while not self._shutdown.is_set(): | |
99 | + self._capture_loop() | |
100 | + if self._shutdown.wait(config.CAMERA_RECONNECT_SECONDS): | |
101 | + break | |
102 | + | |
103 | + def stop(self): | |
104 | + self._shutdown.set() | |
105 | + | |
106 | + def _capture_loop(self): | |
107 | + cap = cv2.VideoCapture(self.source) | |
108 | + try: | |
109 | + if not cap.isOpened(): | |
110 | + raise RuntimeError(f"unable to open source {self.source!r}") | |
111 | + width, height = (int(v) for v in self.resolution.split("x")) | |
112 | + cap.set(cv2.CAP_PROP_FRAME_WIDTH, width) | |
113 | + cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height) | |
114 | + | |
115 | + last_frame_time = 0.0 | |
116 | + | |
117 | + while not self._shutdown.is_set(): | |
118 | + frame_interval = self._frame_interval() | |
119 | + ok, frame = cap.read() | |
120 | + if not ok: | |
121 | + break | |
122 | + now = time.time() | |
123 | + if frame_interval and (now - last_frame_time) < frame_interval: | |
124 | + time.sleep(max(0.0, frame_interval - (now - last_frame_time))) | |
125 | + last_frame_time = time.time() | |
126 | + self._process_frame(frame, now) | |
127 | + self._update_adaptive_fps(now) | |
128 | + | |
129 | + self._publish_offline("Stream not readable") | |
130 | + self._save_clip("feed-lost") | |
131 | + except Exception as e: | |
132 | + self._publish_offline(str(e)) | |
133 | + print(f"[Camera {self.camera_id}] {e}") | |
134 | + finally: | |
135 | + cap.release() | |
136 | + | |
137 | + def _frame_interval(self) -> float: | |
138 | + fps = self._adaptive_fps | |
139 | + return 1.0 / fps if fps > 0 else 0.0 | |
140 | + | |
141 | + def _update_adaptive_fps(self, now: float): | |
142 | + """Halve capture FPS while the shared analysis budget is starved, then | |
143 | + restore it once the pool breathes. Keeps feeds live under load.""" | |
144 | + cap = max(1, int(self.fps_limit or config.CAMERA_FPS_LIMIT)) | |
145 | + if len(self._defer_stamps) >= 3 and (now - self._defer_stamps[-3]) < 10.0: | |
146 | + target = max(config.CAMERA_MIN_FPS, cap // 2) | |
147 | + if 0 < target < self._adaptive_fps: | |
148 | + self._adaptive_fps = target | |
149 | + elif self._adaptive_fps != cap and self._defer_stamps and (now - self._defer_stamps[-1]) > 30.0: | |
150 | + self._adaptive_fps = cap | |
151 | + | |
152 | + # ---- low-compute decisioning ---- | |
153 | + @staticmethod | |
154 | + def _motion_diff(gray: np.ndarray, prev: Optional[np.ndarray]) -> float: | |
155 | + if prev is None or prev.shape != gray.shape: | |
156 | + return 0.0 | |
157 | + return float(cv2.absdiff(gray, prev).mean()) | |
158 | + | |
159 | + def _should_analyze(self, motion: float, now: float) -> tuple: | |
160 | + """Return (should_run, force). `force` bypasses the global rate bucket.""" | |
161 | + if not config.CAMERA_MOTION_GATE: | |
162 | + return (now - self._last_analyze >= config.CAMERA_ANALYZE_INTERVAL_SEC, False) | |
163 | + elapsed = now - self._last_analyze | |
164 | + if elapsed < config.CAMERA_ANALYZE_INTERVAL_SEC: | |
165 | + return (False, False) | |
166 | + active = motion >= config.CAMERA_MOTION_THRESHOLD | |
167 | + stale = elapsed >= config.CAMERA_ANALYZE_MAX_GAP_SEC | |
168 | + return (active or stale, stale) | |
169 | + | |
170 | + def _resolve_scene(self, motion: float): | |
171 | + if motion >= 0.35: | |
172 | + self._scene_state = "CROWDED" | |
173 | + elif motion >= 0.15: | |
174 | + self._scene_state = "BUSY" | |
175 | + elif motion >= 0.05: | |
176 | + self._scene_state = "ACTIVE" | |
177 | + else: | |
178 | + self._scene_state = "IDLE" | |
179 | + | |
180 | + # ---- pipeline ---- | |
181 | + def _process_frame(self, frame: np.ndarray, now: float): | |
182 | + self._captured_frames += 1 | |
183 | + self._last_frame_time = now | |
184 | + self._offline_since = None | |
185 | + | |
186 | + gray_small = cv2.cvtColor(cv2.resize(frame, (160, 120)), cv2.COLOR_BGR2GRAY) | |
187 | + motion = self._motion_diff(gray_small, self._prev_gray) | |
188 | + self._prev_gray = gray_small | |
189 | + | |
190 | + alpha = config.CAMERA_MOTION_EMA_ALPHA | |
191 | + self._motion_level = alpha * motion + (1 - alpha) * self._motion_level | |
192 | + self._resolve_scene(self._motion_level) | |
193 | + | |
194 | + should_run, force = self._should_analyze(motion, now) | |
195 | + analyzed = False | |
196 | + if should_run and (force or analysis_scheduler.try_acquire()): | |
197 | + self._last_analyze = now | |
198 | + self._analyzed_frames += 1 | |
199 | + analyzed = True | |
200 | + annotated, detections = self._run_face_pipeline(frame) | |
201 | + self._people_count = len(detections) | |
202 | + self._consume_detections(detections, annotated, now) | |
203 | + self._publish_frame(annotated, now, force=True) | |
204 | + self._maybe_trigger_clip(now) | |
205 | + else: | |
206 | + if should_run: | |
207 | + self._defer_stamps.append(now) | |
208 | + self._publish_frame(frame, now, force=False) | |
209 | + self._maybe_capture_clip(frame, now) | |
210 | + self._record_history(now, analyzed) | |
211 | + | |
212 | + def _run_face_pipeline(self, frame: np.ndarray): | |
213 | + """Offload the expensive detection to the shared, bounded pool.""" | |
214 | + t0 = time.perf_counter() | |
215 | + try: | |
216 | + future = analysis_scheduler.run(lambda f=frame: face_engine.process_frame(f)) | |
217 | + result = future.result(timeout=config.CAMERA_ANALYSIS_TIMEOUT_SEC) | |
218 | + except Exception: | |
219 | + result = (frame, []) | |
220 | + self._analysis_latency_ms.append((time.perf_counter() - t0) * 1000.0) | |
221 | + return result | |
222 | + | |
223 | + # ---- fleet ops: evidence clips + minute-level activity ---- | |
224 | + def _maybe_capture_clip(self, frame: np.ndarray, now: float): | |
225 | + if not config.CLIP_ENABLED: | |
226 | + return | |
227 | + if now - self._last_clip_frame < (1.0 / config.CLIP_FPS): | |
228 | + return | |
229 | + self._last_clip_frame = now | |
230 | + small = cv2.resize(frame, (0, 0), fx=0.5, fy=0.5) | |
231 | + ok, buf = cv2.imencode(".jpg", small, [cv2.IMWRITE_JPEG_QUALITY, 70]) | |
232 | + if ok: | |
233 | + self._clip_buf.append((now, buf.tobytes())) | |
234 | + | |
235 | + def _maybe_trigger_clip(self, now: float): | |
236 | + if (now - self._last_clip_save) < config.CLIP_COOLDOWN_SEC: | |
237 | + return None | |
238 | + if self._scene_state == "CROWDED" or self._people_count >= config.CAMERA_CROWD_ALERT_AT: | |
239 | + self._last_clip_save = now | |
240 | + return self._save_clip("crowd") | |
241 | + return None | |
242 | + | |
243 | + def _save_clip(self, reason: str): | |
244 | + if not config.CLIP_ENABLED: | |
245 | + return None | |
246 | + with self._clip_lock: | |
247 | + frames = list(self._clip_buf) | |
248 | + if len(frames) < 4: | |
249 | + return None | |
250 | + safe = "".join(ch for ch in reason if ch.isalnum() or ch in ("-", "_")) or "event" | |
251 | + config.CLIP_DIR.mkdir(parents=True, exist_ok=True) | |
252 | + path = config.CLIP_DIR / f"{self.camera_id}_{int(time.time())}_{safe}.mp4" | |
253 | + first = cv2.imdecode(np.frombuffer(frames[0][1], np.uint8), cv2.IMREAD_COLOR) | |
254 | + h, w = first.shape[:2] | |
255 | + writer = cv2.VideoWriter(str(path), cv2.VideoWriter_fourcc(*"mp4v"), config.CLIP_FPS, (w, h)) | |
256 | + if not writer.isOpened(): | |
257 | + print(f"[Camera {self.camera_id}] no MP4 codec; evidence clip skipped") | |
258 | + return None | |
259 | + with self._clip_lock: | |
260 | + frames_now = list(self._clip_buf) | |
261 | + try: | |
262 | + for _, jpeg in frames_now: | |
263 | + frame = cv2.imdecode(np.frombuffer(jpeg, np.uint8), cv2.IMREAD_COLOR) | |
264 | + if frame is not None: | |
265 | + writer.write(frame) | |
266 | + finally: | |
267 | + writer.release() | |
268 | + return str(path) | |
269 | + | |
270 | + def _accumulate_activity(self, now: float, analyzed: bool): | |
271 | + key = datetime.fromtimestamp(now, tz=timezone.utc).replace(tzinfo=None, second=0, microsecond=0) | |
272 | + agg = self._activity_minutes.get(key) | |
273 | + if agg is None: | |
274 | + agg = { | |
275 | + "bucket": key, "motion_sum": 0.0, "motion_n": 0, "peak_motion": 0.0, | |
276 | + "crowd_seconds": 0, "people_seen": 0, "analyzed_count": 0, | |
277 | + } | |
278 | + self._activity_minutes[key] = agg | |
279 | + agg["motion_sum"] += float(self._motion_level) * 100.0 | |
280 | + agg["motion_n"] += 1 | |
281 | + agg["peak_motion"] = max(agg["peak_motion"], float(self._motion_level) * 100.0) | |
282 | + if self._scene_state in ("BUSY", "CROWDED"): | |
283 | + agg["crowd_seconds"] += 1 | |
284 | + agg["people_seen"] = max(agg["people_seen"], self._people_count) | |
285 | + if analyzed: | |
286 | + agg["analyzed_count"] += 1 | |
287 | + | |
288 | + def drain_activity(self) -> list: | |
289 | + """Return the accumulated minute-buckets and clear them. Housekeeper.""" | |
290 | + with self._lock: | |
291 | + minutes = list(self._activity_minutes.values()) | |
292 | + self._activity_minutes.clear() | |
293 | + entries = [] | |
294 | + for agg in minutes: | |
295 | + n = max(1, agg["motion_n"]) | |
296 | + entries.append({ | |
297 | + "camera_id": self.camera_id, | |
298 | + "bucket": agg["bucket"], | |
299 | + "samples": n, | |
300 | + "avg_motion": round(agg["motion_sum"] / n, 1), | |
301 | + "peak_motion": round(agg["peak_motion"], 1), | |
302 | + "crowd_seconds": agg["crowd_seconds"], | |
303 | + "people_seen": agg["people_seen"], | |
304 | + "analyzed_count": agg["analyzed_count"], | |
305 | + }) | |
306 | + return entries | |
307 | + | |
308 | + def is_offline(self, now: float) -> bool: | |
309 | + with self._lock: | |
310 | + if self._offline_since is not None: | |
311 | + return (now - self._offline_since) >= config.CAMERA_OFFLINE_GRACE_SEC | |
312 | + if self._last_frame_time and (now - self._last_frame_time) >= config.CAMERA_OFFLINE_GRACE_SEC: | |
313 | + return True | |
314 | + return False | |
315 | + | |
316 | + def list_clips(self) -> list: | |
317 | + if not config.CLIP_DIR.exists(): | |
318 | + return [] | |
319 | + clips = [] | |
320 | + for p in sorted(config.CLIP_DIR.glob(f"{self.camera_id}_*.mp4"), reverse=True): | |
321 | + clips.append({ | |
322 | + "camera_id": self.camera_id, | |
323 | + "filename": p.name, | |
324 | + "size_bytes": p.stat().st_size, | |
325 | + "timestamp": p.stat().st_mtime, | |
326 | + }) | |
327 | + return clips | |
328 | + | |
329 | + def _record_history(self, now: float, analyzed: bool): | |
330 | + if now - self._last_history_ts < 1.0: | |
331 | + return | |
332 | + self._last_history_ts = now | |
333 | + with self._lock: | |
334 | + self._history.append({ | |
335 | + "t": round(now, 1), | |
336 | + "motion_score": round(self._motion_level * 100.0, 1), | |
337 | + "scene_state": self._scene_state, | |
338 | + "people_seen": self._people_count, | |
339 | + "analyzed": analyzed, | |
340 | + }) | |
341 | + self._accumulate_activity(now, analyzed) | |
342 | + | |
343 | + def get_history(self, limit: int = 60) -> list: | |
344 | + with self._lock: | |
345 | + return list(self._history)[-limit:] | |
346 | + | |
347 | + def _consume_detections(self, detections: List[Dict[str, Any]], annotated: np.ndarray, now: float): | |
348 | + for det in detections: | |
349 | + if self.cam_type == "gate": | |
350 | + self._handle_gate_detection(det, now) | |
351 | + else: | |
352 | + person_id = det["person_id"] | |
353 | + productivity_engine.update_presence( | |
354 | + person_id, True, self.camera_id, frame=annotated, face_bbox=det["bbox"] | |
355 | + ) | |
356 | + health_sentinel.analyze_health_and_posture( | |
357 | + person_id, annotated, det["bbox"], camera_id=self.camera_id | |
358 | + ) | |
359 | + if self.cam_type != "gate": | |
360 | + spatial_memory.monitor(self.camera_id, annotated, detections) | |
361 | + else: | |
362 | + self._evict_gone_persons(now) | |
363 | + | |
364 | + def _publish_frame(self, frame: np.ndarray, now: float, force: bool): | |
365 | + if not force and (now - self._last_publish) < config.CAMERA_JPEG_PUBLISH_INTERVAL_SEC: | |
366 | + return | |
367 | + self._last_publish = now | |
368 | + ok, buffer = cv2.imencode(".jpg", frame) | |
369 | + jpeg = buffer.tobytes() if ok else b"" | |
370 | + with self._lock: | |
371 | + self._latest = CameraFrame( | |
372 | + camera_id=self.camera_id, | |
373 | + timestamp=now, | |
374 | + jpeg=jpeg, | |
375 | + detections=[], | |
376 | + ) | |
377 | + | |
378 | + # ---- gate occupancy (in/out, live count, peak, totals) ---- | |
379 | + def _handle_gate_detection(self, det: dict, now: float): | |
380 | + person_id = det["person_id"] | |
381 | + if person_id == "UNKNOWN": | |
382 | + if person_id in self._last_attendance and (now - self._last_attendance[person_id]) < 30: | |
383 | + return | |
384 | + self._last_attendance[person_id] = now | |
385 | + self._log_attendance(det, now) | |
386 | + emit_alert( | |
387 | + alert_type="UNKNOWN_FACE", | |
388 | + severity="WARNING", | |
389 | + description=f"Unregistered/Unknown individual detected at {self.camera_id}.", | |
390 | + camera_id=self.camera_id, | |
391 | + person_id="UNKNOWN", | |
392 | + ) | |
393 | + return | |
394 | + | |
395 | + if person_id not in self._presence: | |
396 | + self._presence[person_id] = now | |
397 | + self._today_entries += 1 | |
398 | + self._peak = max(self._peak, len(self._presence)) | |
399 | + self._log_attendance(det, now) | |
400 | + else: | |
401 | + self._presence[person_id] = now | |
402 | + | |
403 | + def _evict_gone_persons(self, now: float): | |
404 | + timeout = config.GATE_EXIT_TIMEOUT_SECONDS | |
405 | + for person_id in list(self._presence.keys()): | |
406 | + if now - self._presence[person_id] > timeout: | |
407 | + del self._presence[person_id] | |
408 | + self._today_exits += 1 | |
409 | + db = SessionLocal() | |
410 | + try: | |
411 | + db.add(AttendanceLog( | |
412 | + person_id=person_id, | |
413 | + name=person_id, | |
414 | + confidence=0.0, | |
415 | + camera_id=self.camera_id, | |
416 | + event_type="EXIT", | |
417 | + )) | |
418 | + db.commit() | |
419 | + except Exception as e: | |
420 | + db.rollback() | |
421 | + print(f"[Camera {self.camera_id} EXIT Error] {e}") | |
422 | + finally: | |
423 | + db.close() | |
424 | + | |
425 | + def get_occupancy(self) -> dict: | |
426 | + with self._lock: | |
427 | + return { | |
428 | + "camera_id": self.camera_id, | |
429 | + "currently_inside": len(self._presence), | |
430 | + "peak_inside": self._peak, | |
431 | + "today_entries": self._today_entries, | |
432 | + "today_exits": self._today_exits, | |
433 | + } | |
434 | + | |
435 | + def _log_attendance(self, det: dict, now: float): | |
436 | + person_id = det["person_id"] | |
437 | + db = SessionLocal() | |
438 | + try: | |
439 | + db.add(AttendanceLog( | |
440 | + person_id=person_id if person_id != "UNKNOWN" else None, | |
441 | + name=det["name"], | |
442 | + confidence=det["confidence"], | |
443 | + camera_id=self.camera_id, | |
444 | + event_type="ENTRY", | |
445 | + )) | |
446 | + db.commit() | |
447 | + except Exception as e: | |
448 | + db.rollback() | |
449 | + print(f"[Camera {self.camera_id} Attendance Error] {e}") | |
450 | + finally: | |
451 | + db.close() | |
452 | + | |
453 | + def _publish_offline(self, message: str): | |
454 | + blank = np.zeros((480, 640, 3), np.uint8) | |
455 | + cv2.putText(blank, f"Camera Offline ({self.cam_label})", (90, 220), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) | |
456 | + cv2.putText(blank, message, (150, 260), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (90, 90, 90), 1) | |
457 | + ok, buffer = cv2.imencode(".jpg", blank) | |
458 | + with self._lock: | |
459 | + if self._offline_since is None: | |
460 | + self._offline_since = time.time() | |
461 | + self._last_frame_time = 0.0 | |
462 | + self._latest = CameraFrame( | |
463 | + camera_id=self.camera_id, | |
464 | + timestamp=time.time(), | |
465 | + jpeg=buffer.tobytes() if ok else b"", | |
466 | + offline=True, | |
467 | + ) | |
468 | + | |
469 | + # ---- read ---- | |
470 | + def get_latest(self) -> CameraFrame: | |
471 | + with self._lock: | |
472 | + return self._latest | |
473 | + | |
474 | + def get_status(self) -> dict: | |
475 | + uptime = max(1.0, time.time() - self._start_time) | |
476 | + latency = round(float(np.mean(self._analysis_latency_ms)), 1) if self._analysis_latency_ms else 0.0 | |
477 | + with self._lock: | |
478 | + return { | |
479 | + "motion_score": round(self._motion_level * 100.0, 1), | |
480 | + "scene_state": self._scene_state, | |
481 | + "people_seen": self._people_count, | |
482 | + "analyzed_frames": self._analyzed_frames, | |
483 | + "captured_frames": self._captured_frames, | |
484 | + "analysis_rate": round(self._analyzed_frames / uptime, 2), | |
485 | + "captured_per_analysis": round(self._captured_frames / max(1, self._analyzed_frames), 1), | |
486 | + "analysis_latency_ms": latency, | |
487 | + "adaptive_fps": self._adaptive_fps, | |
488 | + "scheduler_deferred": len(self._defer_stamps), | |
489 | + "offline_since": round(time.time() - self._offline_since, 1) if self._offline_since else None, | |
490 | + "clip_buffered_frames": len(self._clip_buf), | |
491 | + "occupancy": ( | |
492 | + { | |
493 | + "camera_id": self.camera_id, | |
494 | + "currently_inside": len(self._presence), | |
495 | + "peak_inside": self._peak, | |
496 | + "today_entries": self._today_entries, | |
497 | + "today_exits": self._today_exits, | |
498 | + } | |
499 | + if self.cam_type == "gate" | |
500 | + else None | |
501 | + ), | |
502 | + } | |
503 | + | |
504 | + | |
505 | +class CameraManager: | |
506 | + """Owns one CameraWorker per active camera from the runtime registry.""" | |
507 | + | |
508 | + def __init__(self): | |
509 | + self.workers: dict = {} | |
510 | + self._lock = threading.Lock() | |
511 | + | |
512 | + # ---- lifecycle ---- | |
513 | + def start_all(self): | |
514 | + for cam_id, cfg in camera_registry.configs().items(): | |
515 | + if cfg["active"]: | |
516 | + self.start(cam_id, cfg) | |
517 | + | |
518 | + def start(self, camera_id: str, cam_config: Optional[dict] = None): | |
519 | + with self._lock: | |
520 | + if camera_id in self.workers: | |
521 | + return | |
522 | + cam_config = cam_config or camera_registry.get(camera_id) | |
523 | + if cam_config is None or not cam_config["active"]: | |
524 | + return | |
525 | + worker = CameraWorker(camera_id, cam_config) | |
526 | + worker.start() | |
527 | + self.workers[camera_id] = worker | |
528 | + | |
529 | + def stop(self, camera_id: str): | |
530 | + with self._lock: | |
531 | + worker = self.workers.pop(camera_id, None) | |
532 | + if worker is not None: | |
533 | + worker.stop() | |
534 | + worker.join(timeout=5) | |
535 | + | |
536 | + def stop_all(self): | |
537 | + with self._lock: | |
538 | + workers = list(self.workers.values()) | |
539 | + self.workers.clear() | |
540 | + for worker in workers: | |
541 | + worker.stop() | |
542 | + for worker in workers: | |
543 | + worker.join(timeout=5) | |
544 | + | |
545 | + def restart(self, camera_id: str): | |
546 | + self.stop(camera_id) | |
547 | + cfg = camera_registry.get(camera_id) | |
548 | + if cfg and cfg["active"]: | |
549 | + self.start(camera_id, cfg) | |
550 | + else: | |
551 | + self.stop(camera_id) | |
552 | + | |
553 | + # ---- runtime CRUD (delegates to the registry, then applies) ---- | |
554 | + def add_camera(self, camera_id: str, name: str, source: Any, cam_type: str, | |
555 | + resolution: Optional[str] = None, fps_limit: Optional[int] = None) -> dict: | |
556 | + cfg = camera_registry.add(camera_id, name, source, cam_type, resolution, fps_limit) | |
557 | + self.start(camera_id, cfg) | |
558 | + return cfg | |
559 | + | |
560 | + def update_camera(self, camera_id: str, **fields) -> Optional[dict]: | |
561 | + cfg = camera_registry.update(camera_id, **fields) | |
562 | + if cfg is None: | |
563 | + return None | |
564 | + if cfg["active"]: | |
565 | + if camera_id in self.workers: | |
566 | + self.restart(camera_id) | |
567 | + else: | |
568 | + self.start(camera_id, cfg) | |
569 | + else: | |
570 | + self.stop(camera_id) | |
571 | + return cfg | |
572 | + | |
573 | + def set_active(self, camera_id: str, active: bool) -> Optional[dict]: | |
574 | + cfg = camera_registry.set_active(camera_id, active) | |
575 | + if cfg is None: | |
576 | + return None | |
577 | + if active: | |
578 | + self.start(camera_id, cfg) | |
579 | + else: | |
580 | + self.stop(camera_id) | |
581 | + return cfg | |
582 | + | |
583 | + def remove_camera(self, camera_id: str) -> bool: | |
584 | + self.stop(camera_id) | |
585 | + if camera_registry.delete(camera_id): | |
586 | + return True | |
587 | + if camera_registry.get(camera_id) is not None: | |
588 | + camera_registry.set_active(camera_id, False) | |
589 | + return True | |
590 | + return False | |
591 | + | |
592 | + # ---- read ---- | |
593 | + def get_latest(self, camera_id: str) -> CameraFrame: | |
594 | + worker = self.workers.get(camera_id) | |
595 | + if worker is not None: | |
596 | + return worker.get_latest() | |
597 | + return self._static_offline(camera_id) | |
598 | + | |
599 | + def get_camera(self, camera_id: str) -> Optional[dict]: | |
600 | + cfg = camera_registry.get(camera_id) | |
601 | + if cfg is None: | |
602 | + return None | |
603 | + return self._serialize(cfg) | |
604 | + | |
605 | + def list_cameras(self) -> List[dict]: | |
606 | + cameras = [] | |
607 | + for cam_id, cfg in camera_registry.configs().items(): | |
608 | + cameras.append(self._serialize(cfg)) | |
609 | + return cameras | |
610 | + | |
611 | + def _serialize(self, cfg: dict) -> dict: | |
612 | + cam_id = cfg["camera_id"] | |
613 | + worker = self.workers.get(cam_id) | |
614 | + if worker is not None: | |
615 | + frame = worker.get_latest() | |
616 | + cam = { | |
617 | + "camera_id": cam_id, | |
618 | + "name": cfg["name"], | |
619 | + "source": cfg["source"], | |
620 | + "type": cfg.get("type", "gate"), | |
621 | + "active": bool(cfg.get("active", True)), | |
622 | + "resolution": cfg.get("resolution"), | |
623 | + "fps_limit": cfg.get("fps_limit"), | |
624 | + "online": not frame.offline, | |
625 | + "last_frame_age": round(float(time.time() - frame.timestamp), 1) if frame.timestamp else None, | |
626 | + } | |
627 | + cam.update(worker.get_status()) | |
628 | + cam["history"] = worker.get_history(limit=60) | |
629 | + return cam | |
630 | + return { | |
631 | + "camera_id": cam_id, | |
632 | + "name": cfg["name"], | |
633 | + "source": cfg["source"], | |
634 | + "type": cfg.get("type", "gate"), | |
635 | + "active": bool(cfg.get("active", True)), | |
636 | + "resolution": cfg.get("resolution"), | |
637 | + "fps_limit": cfg.get("fps_limit"), | |
638 | + "online": False, | |
639 | + "last_frame_age": None, | |
640 | + "motion_score": 0.0, | |
641 | + "scene_state": "OFFLINE", | |
642 | + "people_seen": 0, | |
643 | + "captured_per_analysis": 0.0, | |
644 | + "analysis_latency_ms": 0.0, | |
645 | + "adaptive_fps": cfg.get("fps_limit") or 0, | |
646 | + "scheduler_deferred": 0, | |
647 | + "offline_since": None, | |
648 | + "clip_buffered_frames": 0, | |
649 | + "occupancy": None, | |
650 | + "history": [], | |
651 | + } | |
652 | + | |
653 | + @staticmethod | |
654 | + def _static_offline(camera_id: str) -> CameraFrame: | |
655 | + blank = np.zeros((480, 640, 3), np.uint8) | |
656 | + cv2.putText(blank, f"Camera Offline ({camera_id})", (90, 240), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 0, 255), 2) | |
657 | + ok, buffer = cv2.imencode(".jpg", blank) | |
658 | + return CameraFrame(camera_id=camera_id, timestamp=time.time(), jpeg=buffer.tobytes() if ok else b"", offline=True) | |
659 | + | |
660 | + | |
661 | +camera_manager = CameraManager() | |
0 | 662 | |
▼backend/app/services/camera_ops_service.py
+153-0
| @@ -0,0 +1,153 @@ | ||
1 | +"""Fleet housekeeper: offline alerting, activity persistence, retention. | |
2 | + | |
3 | +Runs once per `CAMERA_OPS_SWEEP_SEC` in a daemon thread: | |
4 | +- detects cameras whose feed has been dead for `CAMERA_OFFLINE_GRACE_SEC` | |
5 | + and raises a `CAMERA_OFFLINE` incident alert (deduped + cooldown-gated), | |
6 | + auto-resolving it when the feed comes back; | |
7 | +- drains each worker's per-minute activity into `CameraActivityLog`; | |
8 | +- prunes activity rows and evidence clip files beyond their retention windows. | |
9 | +""" | |
10 | + | |
11 | +import threading | |
12 | +import time | |
13 | +from datetime import timedelta | |
14 | + | |
15 | +from app.config import config | |
16 | +from app.db.sqlite_db import SessionLocal | |
17 | +from app.db.models import CameraActivityLog | |
18 | +from app.services.alert_service import emit_alert, resolve_camera_alerts, has_pending_alert | |
19 | +from app.utils.timeutil import utcnow | |
20 | + | |
21 | + | |
22 | +class CameraOpsService: | |
23 | + def __init__(self): | |
24 | + self._stop = threading.Event() | |
25 | + self._thread: threading.Thread | None = None | |
26 | + self._last_offline_alert: dict[str, float] = {} | |
27 | + | |
28 | + # ---- lifecycle ---- | |
29 | + def start(self): | |
30 | + if self._thread and self._thread.is_alive(): | |
31 | + return | |
32 | + self._stop.clear() | |
33 | + self._thread = threading.Thread(target=self._run, name="camera-ops", daemon=True) | |
34 | + self._thread.start() | |
35 | + | |
36 | + def stop(self): | |
37 | + self._stop.set() | |
38 | + if self._thread is not None: | |
39 | + self._thread.join(timeout=5) | |
40 | + | |
41 | + def _run(self): | |
42 | + while not self._stop.wait(config.CAMERA_OPS_SWEEP_SEC): | |
43 | + try: | |
44 | + self.sweep_once() | |
45 | + except Exception as e: | |
46 | + print(f"[CameraOps] sweep error: {e}") | |
47 | + | |
48 | + # ---- per-sweep work ---- | |
49 | + def sweep_once(self): | |
50 | + now = time.time() | |
51 | + for cam in self._workers(): | |
52 | + self._check_feed_health(cam, now) | |
53 | + entries = [] | |
54 | + for cam in self._workers(): | |
55 | + entries.extend(cam.drain_activity()) | |
56 | + if entries: | |
57 | + self.save_activity(entries) | |
58 | + self.prune_activity() | |
59 | + self.prune_clips() | |
60 | + | |
61 | + def _workers(self): | |
62 | + from app.services.camera_manager import camera_manager | |
63 | + | |
64 | + return [w for w in camera_manager.workers.values() if getattr(w, "camera_id", None)] | |
65 | + | |
66 | + # ---- offline alerting ---- | |
67 | + def _check_feed_health(self, worker, now: float): | |
68 | + offline = worker.is_offline(now) | |
69 | + if offline and not self._offline_alerted(worker.camera_id, now): | |
70 | + if not has_pending_alert(worker.camera_id, "CAMERA_OFFLINE"): | |
71 | + emit_alert( | |
72 | + alert_type="CAMERA_OFFLINE", | |
73 | + severity="CRITICAL", | |
74 | + description=f"Camera {worker.camera_id} ({worker.cam_label}) feed is down or unreadable.", | |
75 | + camera_id=worker.camera_id, | |
76 | + ) | |
77 | + self._last_offline_alert[worker.camera_id] = now | |
78 | + elif not offline and self._offline_alerted(worker.camera_id, now): | |
79 | + resolved = resolve_camera_alerts(worker.camera_id, "CAMERA_OFFLINE") | |
80 | + if resolved: | |
81 | + print(f"[CameraOps] camera {worker.camera_id} recovered — resolved {resolved} alert(s)") | |
82 | + self._last_offline_alert.pop(worker.camera_id, None) | |
83 | + | |
84 | + def _offline_alerted(self, camera_id: str, now: float) -> bool: | |
85 | + last = self._last_offline_alert.get(camera_id) | |
86 | + return last is not None and (now - last) < config.CAMERA_OFFLINE_COOLDOWN_SEC | |
87 | + | |
88 | + # ---- activity persistence ---- | |
89 | + @staticmethod | |
90 | + def save_activity(entries: list): | |
91 | + if not entries: | |
92 | + return | |
93 | + db = SessionLocal() | |
94 | + try: | |
95 | + for e in entries: | |
96 | + row = ( | |
97 | + db.query(CameraActivityLog) | |
98 | + .filter_by(camera_id=e["camera_id"], bucket=e["bucket"]) | |
99 | + .first() | |
100 | + ) | |
101 | + if row is None: | |
102 | + db.add(CameraActivityLog(**e)) | |
103 | + continue | |
104 | + total = row.samples + e["samples"] | |
105 | + if total: | |
106 | + row.avg_motion = round( | |
107 | + (row.avg_motion * row.samples + e["avg_motion"] * e["samples"]) / total, 1 | |
108 | + ) | |
109 | + row.samples = total | |
110 | + row.peak_motion = max(row.peak_motion, e["peak_motion"]) | |
111 | + row.crowd_seconds += e["crowd_seconds"] | |
112 | + row.people_seen = max(row.people_seen, e["people_seen"]) | |
113 | + row.analyzed_count += e["analyzed_count"] | |
114 | + db.commit() | |
115 | + except Exception as e: | |
116 | + db.rollback() | |
117 | + print(f"[CameraOps] activity flush error: {e}") | |
118 | + finally: | |
119 | + db.close() | |
120 | + | |
121 | + def prune_activity(self): | |
122 | + cutoff = utcnow() - timedelta(days=config.CAMERA_ACTIVITY_RETENTION_DAYS) | |
123 | + db = SessionLocal() | |
124 | + try: | |
125 | + deleted = ( | |
126 | + db.query(CameraActivityLog).filter(CameraActivityLog.bucket < cutoff).delete() | |
127 | + ) | |
128 | + db.commit() | |
129 | + if deleted: | |
130 | + print(f"[CameraOps] pruned {deleted} stale activity buckets") | |
131 | + except Exception as e: | |
132 | + db.rollback() | |
133 | + print(f"[CameraOps] activity prune error: {e}") | |
134 | + finally: | |
135 | + db.close() | |
136 | + | |
137 | + def prune_clips(self): | |
138 | + if not config.CLIP_ENABLED or not config.CLIP_DIR.exists(): | |
139 | + return | |
140 | + cutoff = time.time() - config.CLIP_RETENTION_HOURS * 3600 | |
141 | + removed = 0 | |
142 | + for p in config.CLIP_DIR.glob("*.mp4"): | |
143 | + try: | |
144 | + if p.stat().st_mtime < cutoff: | |
145 | + p.unlink() | |
146 | + removed += 1 | |
147 | + except OSError: | |
148 | + pass | |
149 | + if removed: | |
150 | + print(f"[CameraOps] pruned {removed} stale evidence clips") | |
151 | + | |
152 | + | |
153 | +camera_ops_service = CameraOpsService() | |
0 | 154 | |
▼backend/app/services/camera_registry.py
+233-0
| @@ -0,0 +1,233 @@ | ||
1 | +"""Runtime camera registry. | |
2 | + | |
3 | +The camera set a worker fleet runs is the *merged* view of: | |
4 | + | |
5 | +1. the baseline map `config.CAMERA_STREAMS` (env / settings defaults), and | |
6 | +2. persistent overrides in the `camera_streams` SQLite table. | |
7 | + | |
8 | +A row whose `id` matches a baseline camera replaces that baseline's | |
9 | +name/source/type and (important for corporate ops) a row with `active=False` | |
10 | +disables the camera for this session *across restarts* — the baseline entry | |
11 | +comes back only if the override row is deleted. Brand-new camera ids are pure | |
12 | +additions. This gives operators a durable, admin-only way to manage the fleet | |
13 | +without touching environment variables. | |
14 | +""" | |
15 | + | |
16 | +import threading | |
17 | +from datetime import datetime | |
18 | +from typing import Any, Dict, Optional | |
19 | + | |
20 | +from sqlalchemy import select | |
21 | + | |
22 | +from app.config import config | |
23 | +from app.db.sqlite_db import SessionLocal | |
24 | +from app.db.models import CameraStream | |
25 | + | |
26 | +_VALID_SCHEMES = ("http", "https", "rtsp", "rtsps") | |
27 | + | |
28 | + | |
29 | +class CameraRegistry: | |
30 | + def __init__(self): | |
31 | + self._lock = threading.Lock() | |
32 | + | |
33 | + # ---- source validation ---- | |
34 | + @staticmethod | |
35 | + def normalize_source(source: Any) -> Any: | |
36 | + """Return a worker-ready source (int index, or validated stream URL).""" | |
37 | + if isinstance(source, int): | |
38 | + return source | |
39 | + if isinstance(source, float): | |
40 | + if source == int(source): | |
41 | + return int(source) | |
42 | + raise ValueError("camera source must be an integer index or a stream URL") | |
43 | + text = str(source).strip() | |
44 | + if not text: | |
45 | + raise ValueError("camera source cannot be empty") | |
46 | + try: | |
47 | + return int(text) | |
48 | + except ValueError: | |
49 | + pass | |
50 | + scheme, _, _ = text.partition("://") | |
51 | + if scheme.lower() not in _VALID_SCHEMES: | |
52 | + raise ValueError( | |
53 | + f"camera source URL must use one of {', '.join(_VALID_SCHEMES)} schemes" | |
54 | + ) | |
55 | + return text | |
56 | + | |
57 | + @staticmethod | |
58 | + def redact_source(source: Any) -> str: | |
59 | + """Strip credentials out of an RTSP/HTTP url for human-facing output.""" | |
60 | + text = str(source) | |
61 | + if "://" not in text: | |
62 | + return text | |
63 | + prefix, rest = text.split("://", 1) | |
64 | + userinfo, _, rest_after = rest.rpartition("@") | |
65 | + if not userinfo or not rest.startswith(userinfo + "@"): | |
66 | + return text | |
67 | + head, _, tail = userinfo.partition(":") | |
68 | + if ":" not in userinfo: | |
69 | + return prefix + "://" + "***@" + rest_after | |
70 | + return prefix + "://" + head + ":***@" + rest_after | |
71 | + | |
72 | + # ------------------------------------------------------------------ | |
73 | + # persistence primitives | |
74 | + # ------------------------------------------------------------------ | |
75 | + def _get_row(self, db, camera_id: str) -> Optional[Any]: | |
76 | + return db.execute(select(CameraStream).where(CameraStream.id == camera_id)).scalar_one_or_none() | |
77 | + | |
78 | + @staticmethod | |
79 | + def _row_to_cfg(row: CameraStream) -> Dict[str, Any]: | |
80 | + try: | |
81 | + source = CameraRegistry.normalize_source(row.source) | |
82 | + except ValueError: | |
83 | + source = row.source | |
84 | + return { | |
85 | + "camera_id": row.id, | |
86 | + "name": row.name, | |
87 | + "source": source, | |
88 | + "type": row.type, | |
89 | + "resolution": row.resolution, | |
90 | + "fps_limit": row.fps_limit, | |
91 | + "active": bool(row.active), | |
92 | + } | |
93 | + | |
94 | + def _baseline(self) -> Dict[str, Dict[str, Any]]: | |
95 | + """The default catalog from settings/env, before any overrides.""" | |
96 | + out = {} | |
97 | + for cam_id, cfg in config.CAMERA_STREAMS.items(): | |
98 | + out[cam_id] = { | |
99 | + "camera_id": cam_id, | |
100 | + "name": cfg.get("name", cam_id), | |
101 | + "source": self.normalize_source(cfg.get("source", 0)), | |
102 | + "type": cfg.get("type", "gate"), | |
103 | + "resolution": None, | |
104 | + "fps_limit": None, | |
105 | + "active": True, | |
106 | + } | |
107 | + return out | |
108 | + | |
109 | + # ------------------------------------------------------------------ | |
110 | + # public API | |
111 | + # ------------------------------------------------------------------ | |
112 | + def configs(self) -> Dict[str, Dict[str, Any]]: | |
113 | + """Merged, effective camera catalog (baseline + persistent overrides).""" | |
114 | + with self._lock: | |
115 | + merged = self._baseline() | |
116 | + db = SessionLocal() | |
117 | + try: | |
118 | + rows = db.execute(select(CameraStream).order_by(CameraStream.id)).scalars().all() | |
119 | + for row in rows: | |
120 | + merged[row.id] = self._row_to_cfg(row) | |
121 | + finally: | |
122 | + db.close() | |
123 | + return merged | |
124 | + | |
125 | + def get(self, camera_id: str) -> Optional[Dict[str, Any]]: | |
126 | + return self.configs().get(camera_id) | |
127 | + | |
128 | + def exists(self, camera_id: str) -> bool: | |
129 | + with self._lock: | |
130 | + db = SessionLocal() | |
131 | + try: | |
132 | + return self._get_row(db, camera_id) is not None | |
133 | + finally: | |
134 | + db.close() | |
135 | + | |
136 | + def add( | |
137 | + self, | |
138 | + camera_id: str, | |
139 | + name: str, | |
140 | + source: Any, | |
141 | + cam_type: str = "gate", | |
142 | + resolution: Optional[str] = None, | |
143 | + fps_limit: Optional[int] = None, | |
144 | + ) -> Dict[str, Any]: | |
145 | + """Insert a brand-new camera. Rejects ids that already exist as rows.""" | |
146 | + normalized = self.normalize_source(source) | |
147 | + camera_id = camera_id.strip() | |
148 | + with self._lock: | |
149 | + db = SessionLocal() | |
150 | + try: | |
151 | + if self._get_row(db, camera_id) is not None: | |
152 | + raise ValueError(f"camera {camera_id!r} already registered") | |
153 | + db.add(CameraStream( | |
154 | + id=camera_id, | |
155 | + name=name.strip(), | |
156 | + source=str(normalized), | |
157 | + type=cam_type, | |
158 | + resolution=resolution, | |
159 | + fps_limit=fps_limit, | |
160 | + active=True, | |
161 | + )) | |
162 | + db.commit() | |
163 | + except ValueError: | |
164 | + db.rollback() | |
165 | + raise | |
166 | + except Exception: | |
167 | + db.rollback() | |
168 | + raise | |
169 | + finally: | |
170 | + db.close() | |
171 | + return self.get(camera_id) | |
172 | + | |
173 | + def update(self, camera_id: str, **fields) -> Optional[Dict[str, Any]]: | |
174 | + """Update persisted fields (and/or flips `active`). Returns None if unknown id.""" | |
175 | + allowed = {"name", "source", "cam_type", "resolution", "fps_limit", "active"} | |
176 | + unknown = set(fields) - allowed | |
177 | + if unknown: | |
178 | + raise ValueError(f"unsupported camera fields: {', '.join(sorted(unknown))}") | |
179 | + if "source" in fields and fields["source"] is not None: | |
180 | + fields["source"] = str(self.normalize_source(fields["source"])) | |
181 | + if "cam_type" in fields: | |
182 | + fields["type"] = fields.pop("cam_type") | |
183 | + with self._lock: | |
184 | + db = SessionLocal() | |
185 | + try: | |
186 | + row = self._get_row(db, camera_id) | |
187 | + if row is None: | |
188 | + base = self._baseline().get(camera_id) | |
189 | + if base is None: | |
190 | + return None | |
191 | + row = CameraStream( | |
192 | + id=camera_id, | |
193 | + name=base["name"], | |
194 | + source=str(base["source"]), | |
195 | + type=base["type"], | |
196 | + active=base["active"], | |
197 | + ) | |
198 | + db.add(row) | |
199 | + for key, value in fields.items(): | |
200 | + if value is None: | |
201 | + continue | |
202 | + if hasattr(row, key): | |
203 | + setattr(row, key, value) | |
204 | + db.commit() | |
205 | + except Exception: | |
206 | + db.rollback() | |
207 | + raise | |
208 | + finally: | |
209 | + db.close() | |
210 | + return self.get(camera_id) | |
211 | + | |
212 | + def set_active(self, camera_id: str, active: bool) -> Optional[Dict[str, Any]]: | |
213 | + return self.update(camera_id, active=bool(active)) | |
214 | + | |
215 | + def delete(self, camera_id: str) -> bool: | |
216 | + """Physically remove the override row (baseline may take over again).""" | |
217 | + with self._lock: | |
218 | + db = SessionLocal() | |
219 | + try: | |
220 | + row = self._get_row(db, camera_id) | |
221 | + if row is None: | |
222 | + return False | |
223 | + db.delete(row) | |
224 | + db.commit() | |
225 | + return True | |
226 | + except Exception: | |
227 | + db.rollback() | |
228 | + raise | |
229 | + finally: | |
230 | + db.close() | |
231 | + | |
232 | + | |
233 | +camera_registry = CameraRegistry() | |
0 | 234 | |
▼backend/app/services/face_backend.py
+144-0
| @@ -0,0 +1,144 @@ | ||
1 | +"""Recognition backend: lightweight SCRFD+ArcFace (insightface/ONNX) with fallback. | |
2 | + | |
3 | +`get_backend()` returns a singleton chosen from `FACE_BACKEND`: | |
4 | + | |
5 | +- `insightface` / `auto`: SCRFD detector + ArcFace embedder (aligned 512-d) from | |
6 | + the insightface model zoo, running on `onnxruntime` CPU/GPU execution | |
7 | + providers. This is the lightweight-and-accurate replacement for dlib. | |
8 | +- `legacy`: the original dlib `face_recognition` hog detector + OpenFace | |
9 | + 128-d embedder (or a Haar cascade fallback). | |
10 | + | |
11 | +Both expose the same `detect(rgb, gray)` -> [FaceHit] contract and a | |
12 | +`embed_photo(rgb)` helper used at enrollment/sync time, so the rest of the | |
13 | +pipeline never cares which backend it is. | |
14 | +""" | |
15 | + | |
16 | +import threading | |
17 | +from dataclasses import dataclass | |
18 | +from typing import List, Optional, Tuple | |
19 | + | |
20 | +import numpy as np | |
21 | + | |
22 | +from app.config import config | |
23 | + | |
24 | + | |
25 | +@dataclass | |
26 | +class FaceHit: | |
27 | + box: Tuple[int, int, int, int] # (top, right, bottom, left) | |
28 | + embedding: Optional[List[float]] = None | |
29 | + keypoints: Optional[np.ndarray] = None # (5, 2) aligned landmarks | |
30 | + | |
31 | + | |
32 | +class LegacyBackend: | |
33 | + name = "legacy" | |
34 | + dim = 128 # dlib OpenFace | |
35 | + embeds_in_detect = False | |
36 | + | |
37 | + def __init__(self): | |
38 | + self.haar_cascade = None | |
39 | + try: | |
40 | + import cv2 # type: ignore | |
41 | + self.haar_cascade = cv2.CascadeClassifier( | |
42 | + cv2.data.haarcascades + "haarcascade_frontalface_default.xml" | |
43 | + ) | |
44 | + except Exception: | |
45 | + pass | |
46 | + | |
47 | + def detect(self, rgb: np.ndarray, gray: Optional[np.ndarray] = None) -> List[FaceHit]: | |
48 | + try: | |
49 | + import face_recognition | |
50 | + | |
51 | + locs = face_recognition.face_locations(rgb, model="hog") | |
52 | + return [FaceHit(box=(t, r, b, l)) for (t, r, b, l) in locs] | |
53 | + except Exception: | |
54 | + if self.haar_cascade is None or gray is None: | |
55 | + return [] | |
56 | + faces = self.haar_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)) | |
57 | + return [FaceHit(box=(y, x + w, y + h, x)) for (x, y, w, h) in faces] | |
58 | + | |
59 | + def embed_photo(self, rgb: np.ndarray) -> Optional[List[float]]: | |
60 | + try: | |
61 | + import face_recognition | |
62 | + | |
63 | + locs = face_recognition.face_locations(rgb) | |
64 | + if not locs: | |
65 | + return None | |
66 | + encs = face_recognition.face_encodings(rgb, known_face_locations=locs) | |
67 | + if not encs: | |
68 | + return None | |
69 | + largest = max(range(len(locs)), key=lambda i: (locs[i][2] - locs[i][0]) * (locs[i][1] - locs[i][3])) | |
70 | + return encs[largest].tolist() | |
71 | + except Exception: | |
72 | + return None | |
73 | + | |
74 | + | |
75 | +class InsightFaceBackend: | |
76 | + name = "insightface" | |
77 | + dim = 512 # ArcFace (w600k) aligned embedding | |
78 | + embeds_in_detect = True # each detected face already carries its embedding | |
79 | + | |
80 | + def __init__(self): | |
81 | + self._app = None | |
82 | + self._lock = threading.Lock() | |
83 | + | |
84 | + def _ensure_app(self): | |
85 | + if self._app is not None: | |
86 | + return self._app | |
87 | + with self._lock: | |
88 | + if self._app is not None: | |
89 | + return self._app | |
90 | + from insightface.app import FaceAnalysis # deferred: heavy import | |
91 | + | |
92 | + providers = ["CUDAExecutionProvider", "CPUExecutionProvider"] if config.FACE_USE_GPU else ["CPUExecutionProvider"] | |
93 | + self._app = FaceAnalysis(name=config.FACE_INSIGHTFACE_MODEL, providers=providers) | |
94 | + self._app.prepare(ctx_id=0 if config.FACE_USE_GPU else -1, det_size=(config.FACE_DET_SIZE, config.FACE_DET_SIZE)) | |
95 | + return self._app | |
96 | + | |
97 | + def detect(self, rgb: np.ndarray, gray: Optional[np.ndarray] = None) -> List[FaceHit]: | |
98 | + app = self._ensure_app() | |
99 | + faces = app.get(rgb) | |
100 | + hits = [] | |
101 | + for f in faces: | |
102 | + x1, y1, x2, y2 = (int(v) for v in f.bbox) | |
103 | + embedding = None | |
104 | + if getattr(f, "embedding", None) is not None: | |
105 | + embedding = [float(v) for v in f.embedding] | |
106 | + keypoints = np.asarray(getattr(f, "kps", None)) if getattr(f, "kps", None) is not None else None | |
107 | + hits.append(FaceHit(box=(y1, x2, y2, x1), embedding=embedding, keypoints=keypoints)) | |
108 | + return hits | |
109 | + | |
110 | + def embed_photo(self, rgb: np.ndarray) -> Optional[List[float]]: | |
111 | + hits = self.detect(rgb) | |
112 | + if not hits: | |
113 | + return None | |
114 | + best = max(hits, key=lambda h: (h.box[2] - h.box[0]) * (h.box[3] - h.box[1])) | |
115 | + return best.embedding | |
116 | + | |
117 | + | |
118 | +def _build_backend(): | |
119 | + want = (config.FACE_BACKEND or "auto").lower() | |
120 | + if want == "legacy": | |
121 | + print("[FaceBackend] using legacy dlib pipeline") | |
122 | + return LegacyBackend() | |
123 | + try: | |
124 | + backend = InsightFaceBackend() | |
125 | + backend.detect(np.zeros((64, 64, 3), dtype=np.uint8)) # probe load (models + wiring) | |
126 | + print(f"[FaceBackend] using insightface/{config.FACE_INSIGHTFACE_MODEL} (ONNX, lightweight)") | |
127 | + return backend | |
128 | + except Exception as e: | |
129 | + print(f"[FaceBackend] insightface unavailable ({e}); falling back to legacy dlib pipeline") | |
130 | + return LegacyBackend() | |
131 | + | |
132 | + | |
133 | +_backend_lock = threading.Lock() | |
134 | +_backend = None | |
135 | + | |
136 | + | |
137 | +def get_backend(): | |
138 | + """Return the shared recognition backend (chosen once, thread-safe).""" | |
139 | + global _backend | |
140 | + if _backend is None: | |
141 | + with _backend_lock: | |
142 | + if _backend is None: | |
143 | + _backend = _build_backend() | |
144 | + return _backend | |
0 | 145 | |
▼backend/app/services/face_engine.py
+236-0
| @@ -0,0 +1,236 @@ | ||
1 | +import time | |
2 | +import cv2 | |
3 | +import numpy as np | |
4 | +from typing import List, Dict, Any, Tuple, Optional | |
5 | +from app.config import config | |
6 | +from app.db.vector_db import vector_db | |
7 | +from app.services.face_backend import get_backend | |
8 | + | |
9 | + | |
10 | +class Track: | |
11 | + """Single-face identity state maintained across frames.""" | |
12 | + | |
13 | + __slots__ = ( | |
14 | + "tid", "bbox", "label", "person_id", "role", "confidence", | |
15 | + "distance", "is_live", "last_seen", "last_embed", "seen", | |
16 | + ) | |
17 | + | |
18 | + def __init__(self, tid: int, bbox: Tuple[int, int, int, int]): | |
19 | + self.tid = tid | |
20 | + self.bbox = bbox | |
21 | + self.label = None | |
22 | + self.person_id = None | |
23 | + self.role = "Employee" | |
24 | + self.confidence = 0.0 | |
25 | + self.distance = 1.0 | |
26 | + self.is_live = False | |
27 | + self.last_seen = time.time() | |
28 | + self.last_embed = 0.0 | |
29 | + self.seen = 1 | |
30 | + | |
31 | + | |
32 | +def _iou(a, b) -> float: | |
33 | + """IoU between two (top, right, bottom, left) boxes.""" | |
34 | + ax1, ay1, ax2, ay2 = a | |
35 | + bx1, by1, bx2, by2 = b | |
36 | + iw = min(ax2, bx2) - max(ax1, bx1) | |
37 | + ih = min(ay2, by2) - max(ay1, by1) | |
38 | + if iw <= 0 or ih <= 0: | |
39 | + return 0.0 | |
40 | + inter = iw * ih | |
41 | + area_a = (ax2 - ax1) * (ay2 - ay1) | |
42 | + area_b = (bx2 - bx1) * (by2 - by1) | |
43 | + return inter / (area_a + area_b - inter) | |
44 | + | |
45 | + | |
46 | +class FaceRecognitionEngine: | |
47 | + """ | |
48 | + Detection → trajectory tracking → (throttled) embedding + ChromaDB match → UNKNOWN tagging. | |
49 | + | |
50 | + Tracking lets a stable face reuse its label for several frames, so expensive | |
51 | + embedding and vector queries run at `TRACK_REEMBED_INTERVAL_SEC` instead of | |
52 | + every frame — this is what keeps crowded scenes fast. | |
53 | + """ | |
54 | + | |
55 | + def __init__(self): | |
56 | + self.distance_threshold = config.FACE_DISTANCE_THRESHOLD | |
57 | + self.anti_spoof_threshold = config.ANTI_SPOOF_THRESHOLD | |
58 | + self.backend = get_backend() | |
59 | + self._tracks: Dict[int, Track] = {} | |
60 | + self._next_tid = 0 | |
61 | + | |
62 | + # ---- liveness ---- | |
63 | + def check_liveness(self, face_crop: np.ndarray) -> Tuple[bool, float]: | |
64 | + """Texture-based anti-spoofing score (kept as cheap fallback; CNN swap possible in Phase 1).""" | |
65 | + if face_crop.size == 0: | |
66 | + return False, 0.0 | |
67 | + gray = cv2.cvtColor(face_crop, cv2.COLOR_BGR2GRAY) | |
68 | + laplacian_var = cv2.Laplacian(gray, cv2.CV_64F).var() | |
69 | + liveness_score = min(1.0, laplacian_var / 350.0) | |
70 | + return liveness_score >= 0.25, round(liveness_score, 2) | |
71 | + | |
72 | + # ---- detection + embedding (backend-agnostic) ---- | |
73 | + def _detect(self, rgb_small: np.ndarray, gray_small: np.ndarray): | |
74 | + """Run the chosen backend; returns FaceHits when available else bare boxes.""" | |
75 | + try: | |
76 | + return self.backend.detect(rgb_small, gray_small) | |
77 | + except Exception as e: | |
78 | + print(f"[FaceEngine] backend detect error: {e}") | |
79 | + return [] | |
80 | + | |
81 | + def _embed(self, rgb_small: np.ndarray, to_embed_locs: list, hits_by_box: dict) -> List[Optional[List[float]]]: | |
82 | + """Embeddings for tracks whose re-embed window elapsed.""" | |
83 | + if not to_embed_locs: | |
84 | + return [] | |
85 | + if self.backend.embeds_in_detect: | |
86 | + return [hits_by_box.get(loc) for loc in to_embed_locs] | |
87 | + return self._legacy_embed_locations(rgb_small, to_embed_locs) | |
88 | + | |
89 | + def _extract_embedding_fallback(self, rgb_image: np.ndarray, face_location: Tuple[int, int, int, int]) -> Optional[List[float]]: | |
90 | + top, right, bottom, left = face_location | |
91 | + crop = rgb_image[top:bottom, left:right] | |
92 | + if crop.size > 0: | |
93 | + resized = cv2.resize(crop, (32, 32)) | |
94 | + hist = cv2.calcHist([resized], [0, 1, 2], None, [4, 4, 8], [0, 256, 0, 256, 0, 256]) | |
95 | + vec = cv2.normalize(hist, hist).flatten().tolist() | |
96 | + if len(vec) < 128: | |
97 | + vec.extend([0.0] * (128 - len(vec))) | |
98 | + return vec[:128] | |
99 | + return None | |
100 | + | |
101 | + def _legacy_embed_locations(self, rgb_small: np.ndarray, locations: list) -> List[Optional[List[float]]]: | |
102 | + """128-d embeddings for the legacy dlib path only.""" | |
103 | + if not locations: | |
104 | + return [] | |
105 | + try: | |
106 | + import face_recognition | |
107 | + | |
108 | + return face_recognition.face_encodings(rgb_small, known_face_locations=list(locations)) | |
109 | + except Exception: | |
110 | + return [self._extract_embedding_fallback(rgb_small, loc) for loc in locations] | |
111 | + | |
112 | + # ---- tracker ---- | |
113 | + def _assign_tracks(self, boxes: List[Tuple[int, int, int, int]], now: float) -> List[Tuple[int, Tuple[int, int, int, int]]]: | |
114 | + assignments = [] | |
115 | + used = set() | |
116 | + for loc in boxes: | |
117 | + best_tid, best_iou = None, config.TRACK_IOU_MIN | |
118 | + for tid, t in self._tracks.items(): | |
119 | + if tid in used: | |
120 | + continue | |
121 | + iou = _iou(t.bbox, loc) | |
122 | + if iou > best_iou: | |
123 | + best_tid, best_iou = tid, iou | |
124 | + if best_tid is None: | |
125 | + best_tid = self._next_tid | |
126 | + self._next_tid += 1 | |
127 | + self._tracks[best_tid] = Track(best_tid, loc) | |
128 | + used.add(best_tid) | |
129 | + t = self._tracks[best_tid] | |
130 | + t.bbox, t.last_seen, t.seen = loc, now, t.seen + 1 | |
131 | + assignments.append((best_tid, loc)) | |
132 | + return assignments | |
133 | + | |
134 | + def _expire_tracks(self, now: float): | |
135 | + stale = [tid for tid, t in self._tracks.items() if now - t.last_seen > config.TRACK_KEEP_SECONDS] | |
136 | + for tid in stale: | |
137 | + del self._tracks[tid] | |
138 | + | |
139 | + # ---- main pipeline ---- | |
140 | + def process_frame(self, frame: np.ndarray) -> Tuple[np.ndarray, List[Dict[str, Any]]]: | |
141 | + detections: List[Dict[str, Any]] = [] | |
142 | + if frame is None or frame.size == 0: | |
143 | + return frame, detections | |
144 | + | |
145 | + h, w, _ = frame.shape | |
146 | + small = cv2.resize(frame, (w // 2, h // 2)) | |
147 | + rgb_small = cv2.cvtColor(small, cv2.COLOR_BGR2RGB) | |
148 | + gray_small = cv2.cvtColor(small, cv2.COLOR_BGR2GRAY) | |
149 | + now = time.time() | |
150 | + | |
151 | + hits = self._detect(rgb_small, gray_small) | |
152 | + boxes = [h.box for h in hits] | |
153 | + if not boxes: | |
154 | + self._expire_tracks(now) | |
155 | + return frame, detections | |
156 | + | |
157 | + hits_by_box = {} | |
158 | + if self.backend.embeds_in_detect: | |
159 | + hits_by_box = {h.box: h.embedding for h in hits} | |
160 | + | |
161 | + assignments = self._assign_tracks(boxes, now) | |
162 | + | |
163 | + # Only embed/mismatch tracks whose re-embed window elapsed (or that are brand new). | |
164 | + to_embed = [] | |
165 | + to_embed_tracks = [] | |
166 | + for tid, loc in assignments: | |
167 | + t = self._tracks[tid] | |
168 | + if t.person_id is None or (now - t.last_embed) >= config.TRACK_REEMBED_INTERVAL_SEC: | |
169 | + t.last_embed = now | |
170 | + to_embed.append(loc) | |
171 | + to_embed_tracks.append(t) | |
172 | + | |
173 | + encodings = self._embed(rgb_small, to_embed, hits_by_box) | |
174 | + for track, encoding in zip(to_embed_tracks, encodings): | |
175 | + if encoding is None: | |
176 | + continue | |
177 | + result = vector_db.query_face(encoding) | |
178 | + distance = result.get("distance", 1.0) | |
179 | + meta = result.get("metadata", {}) | |
180 | + if result.get("match_id") and distance <= self.distance_threshold: | |
181 | + track.person_id = result["match_id"] | |
182 | + track.label = meta.get("name", result["match_id"]) | |
183 | + track.role = meta.get("role", "Employee") | |
184 | + track.distance = distance | |
185 | + track.confidence = round((1.0 - distance) * 100, 1) | |
186 | + else: | |
187 | + track.person_id = "UNKNOWN" | |
188 | + track.label = "UNKNOWN" | |
189 | + track.role = "Visitor/Unverified" | |
190 | + track.distance = distance | |
191 | + track.confidence = 0.0 | |
192 | + | |
193 | + self._expire_tracks(now) | |
194 | + | |
195 | + SCALE = 2 | |
196 | + for tid, loc in assignments: | |
197 | + t = self._tracks[tid] | |
198 | + top, right, bottom, left = loc | |
199 | + y1, x2, y2, x1 = top * SCALE, right * SCALE, bottom * SCALE, left * SCALE | |
200 | + y1, y2 = max(0, y1), min(h, y2) | |
201 | + x1, x2 = max(0, x1), min(w, x2) | |
202 | + | |
203 | + face_crop = frame[y1:y2, x1:x2] | |
204 | + is_live, liveness_score = self.check_liveness(face_crop) | |
205 | + t.is_live = is_live | |
206 | + | |
207 | + if t.person_id == "UNKNOWN": | |
208 | + name, role = "UNKNOWN", "Visitor/Unverified" | |
209 | + confidence = 0.0 | |
210 | + status_label = "UNKNOWN (SPOOF SUSPECT)" if not is_live else "UNKNOWN FACE" | |
211 | + box_color = (0, 69, 255) | |
212 | + else: | |
213 | + name, role = t.label, t.role | |
214 | + confidence = t.confidence | |
215 | + status_label = f"{name} ({confidence}%)" | |
216 | + box_color = (0, 255, 120) | |
217 | + | |
218 | + detections.append({ | |
219 | + "person_id": t.person_id, | |
220 | + "name": name, | |
221 | + "role": role, | |
222 | + "confidence": confidence, | |
223 | + "distance": t.distance, | |
224 | + "is_live": is_live, | |
225 | + "liveness_score": liveness_score, | |
226 | + "bbox": [x1, y1, x2, y2], | |
227 | + }) | |
228 | + | |
229 | + cv2.rectangle(frame, (x1, y1), (x2, y2), box_color, 2) | |
230 | + cv2.rectangle(frame, (x1, y2 - 30), (x2, y2), box_color, cv2.FILLED) | |
231 | + cv2.putText(frame, status_label, (x1 + 6, y2 - 8), cv2.FONT_HERSHEY_SIMPLEX, 0.55, (255, 255, 255), 2) | |
232 | + | |
233 | + return frame, detections | |
234 | + | |
235 | + | |
236 | +face_engine = FaceRecognitionEngine() | |
0 | 237 | |
▼backend/app/services/genai_engine.py
+85-0
| @@ -0,0 +1,85 @@ | ||
1 | +import os | |
2 | +from typing import Dict, Any, List | |
3 | +from google import genai | |
4 | +from app.config import config | |
5 | +from app.db.sqlite_db import SessionLocal | |
6 | +from app.db.models import Personnel, AttendanceLog, IncidentAlert, ProductivityLog, HealthLog, AssetLog | |
7 | + | |
8 | +class GeminiDecisionEngine: | |
9 | + def __init__(self): | |
10 | + self.api_key = config.GEMINI_API_KEY | |
11 | + self.client = None | |
12 | + if self.api_key: | |
13 | + try: | |
14 | + self.client = genai.Client(api_key=self.api_key) | |
15 | + except Exception as e: | |
16 | + print(f"[GeminiEngine Warning] Failed to initialize Gemini API client: {e}") | |
17 | + | |
18 | + def query_copilot(self, prompt: str) -> str: | |
19 | + """ | |
20 | + Answers natural language queries using system DB context & Gemini model. | |
21 | + e.g., 'Who entered the building today?', 'Summarize all security alerts', 'Is there any theft suspect?' | |
22 | + """ | |
23 | + db = SessionLocal() | |
24 | + try: | |
25 | + # Fetch contextual stats from database | |
26 | + personnel_count = db.query(Personnel).count() | |
27 | + recent_attendance = db.query(AttendanceLog).order_by(AttendanceLog.timestamp.desc()).limit(10).all() | |
28 | + unresolved_alerts = db.query(IncidentAlert).filter(IncidentAlert.is_resolved == False).all() | |
29 | + assets = db.query(AssetLog).all() | |
30 | + | |
31 | + att_summary = [f"- {a.name} ({a.person_id}) at {a.timestamp.strftime('%H:%M:%S')} via {a.camera_id}" for a in recent_attendance] | |
32 | + alert_summary = [f"- [{a.severity}] {a.alert_type}: {a.description} at {a.timestamp.strftime('%H:%M:%S')}" for a in unresolved_alerts] | |
33 | + asset_summary = [f"- {ast.asset_name} (Owner: {ast.owner_person_id}, Status: {ast.status})" for ast in assets] | |
34 | + | |
35 | + context = f""" | |
36 | + SYSTEM SURVEILLANCE CONTEXT: | |
37 | + Total Enrolled Personnel: {personnel_count} | |
38 | + Recent Attendance Events: | |
39 | + {chr(10).join(att_summary) if att_summary else 'No recent attendance logs.'} | |
40 | + | |
41 | + Unresolved Security Alerts: | |
42 | + {chr(10).join(alert_summary) if alert_summary else 'No active unresolved alerts.'} | |
43 | + | |
44 | + Monitored Assets: | |
45 | + {chr(10).join(asset_summary) if asset_summary else 'No assets registered.'} | |
46 | + """ | |
47 | + | |
48 | + if self.client: | |
49 | + full_prompt = f""" | |
50 | + You are AegisVision AI Security Copilot, an enterprise autonomous decision engine for modern surveillance. | |
51 | + Use the following real-time database context to answer the user's question accurately, concisely, and professionally. | |
52 | + | |
53 | + {context} | |
54 | + | |
55 | + USER QUESTION: {prompt} | |
56 | + """ | |
57 | + response = self.client.models.generate_content( | |
58 | + model="gemini-2.5-flash", | |
59 | + contents=full_prompt | |
60 | + ) | |
61 | + return response.text.strip() | |
62 | + else: | |
63 | + # Rule-based fallback response if API key is not configured | |
64 | + prompt_lower = prompt.lower() | |
65 | + if "alert" in prompt_lower or "theft" in prompt_lower or "unresolved" in prompt_lower: | |
66 | + if alert_summary: | |
67 | + return "Active Unresolved Alerts:\n" + "\n".join(alert_summary) | |
68 | + return "No active unresolved alerts at this time. All systems operating normally." | |
69 | + elif "who" in prompt_lower or "attendance" in prompt_lower or "entered" in prompt_lower: | |
70 | + if att_summary: | |
71 | + return "Recent Attendance Logs:\n" + "\n".join(att_summary) | |
72 | + return f"Total registered personnel: {personnel_count}. No recent entrance logs recorded." | |
73 | + elif "asset" in prompt_lower or "laptop" in prompt_lower: | |
74 | + if asset_summary: | |
75 | + return "Monitored Assets Status:\n" + "\n".join(asset_summary) | |
76 | + return "No registered assets." | |
77 | + else: | |
78 | + return f"AegisVision AI Copilot Active. Total Registered Personnel: {personnel_count}. Active Alerts: {len(unresolved_alerts)}." | |
79 | + | |
80 | + except Exception as e: | |
81 | + return f"Error processing query: {str(e)}" | |
82 | + finally: | |
83 | + db.close() | |
84 | + | |
85 | +genai_engine = GeminiDecisionEngine() | |
0 | 86 | |
▼backend/app/services/health_sentinel.py
+116-0
| @@ -0,0 +1,116 @@ | ||
1 | +import cv2 | |
2 | +import numpy as np | |
3 | +from typing import Dict, Any, List | |
4 | +from app.db.sqlite_db import SessionLocal | |
5 | +from app.db.models import HealthLog | |
6 | +from app.services.alert_service import emit_alert | |
7 | +from app.config import config | |
8 | + | |
9 | +class HealthSentinelEngine: | |
10 | + def __init__(self): | |
11 | + self.drowsiness_tracker: Dict[str, int] = {} | |
12 | + | |
13 | + def calculate_eye_aspect_ratio(self, eye_points: np.ndarray) -> float: | |
14 | + """ | |
15 | + Computes Eye Aspect Ratio (EAR). | |
16 | + EAR drops significantly when eyes close or droop (drowsiness indicator). | |
17 | + """ | |
18 | + if len(eye_points) < 6: | |
19 | + return 0.30 | |
20 | + | |
21 | + # Vertical eye landmarks | |
22 | + A = np.linalg.norm(eye_points[1] - eye_points[5]) | |
23 | + B = np.linalg.norm(eye_points[2] - eye_points[4]) | |
24 | + # Horizontal eye landmark | |
25 | + C = np.linalg.norm(eye_points[0] - eye_points[3]) | |
26 | + | |
27 | + ear = (A + B) / (2.0 * C) if C > 0 else 0.30 | |
28 | + return float(ear) | |
29 | + | |
30 | + def analyze_health_and_posture(self, person_id: str, frame: np.ndarray, face_bbox: List[int], camera_id: str = "cam_workspace") -> Dict[str, Any]: | |
31 | + """ | |
32 | + Analyzes employee/student ergonomics, eye closure fatigue, and posture. | |
33 | + Logs telemetry to HealthLog in SQLite and triggers alerts on fatigue. | |
34 | + """ | |
35 | + if person_id == "UNKNOWN" or not face_bbox: | |
36 | + return {"drowsiness": False, "posture": "UNKNOWN"} | |
37 | + | |
38 | + x1, y1, x2, y2 = face_bbox | |
39 | + face_w = x2 - x1 | |
40 | + face_h = y2 - y1 | |
41 | + | |
42 | + # Posture check: head aspect ratio & vertical position relative to frame height | |
43 | + posture_status = "GOOD" | |
44 | + if face_h > 0: | |
45 | + aspect = float(face_w) / face_h | |
46 | + if aspect > 1.2 or y2 > frame.shape[0] * 0.85: | |
47 | + posture_status = "SLOUCHING" | |
48 | + | |
49 | + # Eye aspect ratio & drowsiness simulation based on eye region intensity variance | |
50 | + h, w, _ = frame.shape | |
51 | + eye_region_y1 = max(0, y1 + int(face_h * 0.2)) | |
52 | + eye_region_y2 = min(h, y1 + int(face_h * 0.55)) | |
53 | + eye_crop = frame[eye_region_y1:eye_region_y2, x1:x2] | |
54 | + | |
55 | + ear_val = 0.28 | |
56 | + is_drowsy = False | |
57 | + | |
58 | + if eye_crop.size > 0: | |
59 | + gray_eye = cv2.cvtColor(eye_crop, cv2.COLOR_BGR2GRAY) | |
60 | + # Low contrast variance in eye region correlates with closed eyelids | |
61 | + eye_std = np.std(gray_eye) | |
62 | + if eye_std < 18.0: | |
63 | + ear_val = 0.16 | |
64 | + is_drowsy = True | |
65 | + | |
66 | + count = self.drowsiness_tracker.get(person_id, 0) | |
67 | + if is_drowsy: | |
68 | + count += 1 | |
69 | + self.drowsiness_tracker[person_id] = count | |
70 | + else: | |
71 | + self.drowsiness_tracker[person_id] = max(0, count - 1) | |
72 | + | |
73 | + fatigue_score = min(1.0, count / 10.0) | |
74 | + drowsiness_alert = count >= 8 | |
75 | + | |
76 | + # Log health metrics | |
77 | + db = SessionLocal() | |
78 | + drowsiness_alert_fired = False | |
79 | + try: | |
80 | + log = HealthLog( | |
81 | + person_id=person_id, | |
82 | + drowsiness_detected=drowsiness_alert, | |
83 | + eye_aspect_ratio=round(ear_val, 2), | |
84 | + posture_status=posture_status, | |
85 | + fatigue_score=round(fatigue_score, 2), | |
86 | + camera_id=camera_id | |
87 | + ) | |
88 | + db.add(log) | |
89 | + | |
90 | + if drowsiness_alert and count == 8: | |
91 | + drowsiness_alert_fired = True | |
92 | + | |
93 | + db.commit() | |
94 | + except Exception as e: | |
95 | + db.rollback() | |
96 | + print(f"[HealthSentinel Error] {e}") | |
97 | + finally: | |
98 | + db.close() | |
99 | + | |
100 | + if drowsiness_alert_fired: | |
101 | + emit_alert( | |
102 | + alert_type="DROWSINESS_WARNING", | |
103 | + severity="WARNING", | |
104 | + description=f"Fatigue warning: Person {person_id} exhibits persistent eye closure / drowsiness.", | |
105 | + camera_id=camera_id, | |
106 | + person_id=person_id, | |
107 | + ) | |
108 | + | |
109 | + return { | |
110 | + "drowsiness_alert": drowsiness_alert, | |
111 | + "ear": round(ear_val, 2), | |
112 | + "posture": posture_status, | |
113 | + "fatigue_score": round(fatigue_score, 2) | |
114 | + } | |
115 | + | |
116 | +health_sentinel = HealthSentinelEngine() | |
0 | 117 | |
▼backend/app/services/ingestion_service.py
+155-0
| @@ -0,0 +1,155 @@ | ||
1 | +import os | |
2 | +import json | |
3 | +import cv2 | |
4 | +from watchdog.observers import Observer | |
5 | +from watchdog.events import FileSystemEventHandler | |
6 | +from app.config import config | |
7 | +from app.db.vector_db import vector_db | |
8 | +from app.db.sqlite_db import SessionLocal | |
9 | +from app.db.models import Personnel | |
10 | +from app.services.face_backend import get_backend | |
11 | + | |
12 | +class PlugAndPlayIngestionService: | |
13 | + def __init__(self): | |
14 | + self.known_persons_dir = config.KNOWN_PERSONS_DIR | |
15 | + | |
16 | + def register_person_from_folder(self, person_folder_path: str): | |
17 | + """Processes a single person's directory containing photo(s) and metadata info.json.""" | |
18 | + if not os.path.isdir(person_folder_path): | |
19 | + return | |
20 | + | |
21 | + folder_name = os.path.basename(person_folder_path) | |
22 | + person_id = folder_name.split("_")[0] if "_" in folder_name else folder_name | |
23 | + | |
24 | + metadata = { | |
25 | + "name": folder_name.replace("_", " ").title(), | |
26 | + "role": "Employee", | |
27 | + "department": "General", | |
28 | + "email": f"{person_id.lower()}@organization.com", | |
29 | + "phone": "" | |
30 | + } | |
31 | + | |
32 | + # Check for info.json inside folder | |
33 | + info_json_path = os.path.join(person_folder_path, "info.json") | |
34 | + if os.path.exists(info_json_path): | |
35 | + try: | |
36 | + with open(info_json_path, "r") as f: | |
37 | + user_json = json.load(f) | |
38 | + metadata.update(user_json) | |
39 | + except Exception as e: | |
40 | + print(f"[Ingestion] Error reading info.json in {folder_name}: {e}") | |
41 | + | |
42 | + # Scan for image files (.jpg, .png, .jpeg) | |
43 | + image_files = [ | |
44 | + f for f in os.listdir(person_folder_path) | |
45 | + if f.lower().endswith((".jpg", ".png", ".jpeg")) | |
46 | + ] | |
47 | + | |
48 | + if not image_files: | |
49 | + return | |
50 | + | |
51 | + for img_name in image_files: | |
52 | + img_path = os.path.join(person_folder_path, img_name) | |
53 | + self._process_single_image(person_id, img_path, metadata) | |
54 | + | |
55 | + def register_single_file(self, file_path: str): | |
56 | + """Registers a person directly from a file like 'EMP102_Alice.jpg'.""" | |
57 | + if not os.path.isfile(file_path) or not file_path.lower().endswith((".jpg", ".png", ".jpeg")): | |
58 | + return | |
59 | + | |
60 | + filename = os.path.splitext(os.path.basename(file_path))[0] | |
61 | + person_id = filename.split("_")[0] if "_" in filename else filename | |
62 | + name = filename.replace("_", " ").title() | |
63 | + | |
64 | + metadata = { | |
65 | + "name": name, | |
66 | + "role": "Employee", | |
67 | + "department": "General", | |
68 | + "email": f"{person_id.lower()}@organization.com", | |
69 | + "phone": "" | |
70 | + } | |
71 | + | |
72 | + self._process_single_image(person_id, file_path, metadata) | |
73 | + | |
74 | + def _process_single_image(self, person_id: str, img_path: str, metadata: dict): | |
75 | + try: | |
76 | + backend = get_backend() | |
77 | + image = cv2.imread(img_path) | |
78 | + if image is None: | |
79 | + print(f"[Ingestion Warning] Could not read image {img_path}") | |
80 | + return | |
81 | + rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) | |
82 | + embedding = backend.embed_photo(rgb) | |
83 | + | |
84 | + if not embedding: | |
85 | + print(f"[Ingestion Warning] No face detected in {img_path}") | |
86 | + return | |
87 | + | |
88 | + embedding_id = f"{person_id}::{os.path.basename(img_path)}" | |
89 | + | |
90 | + # 1. Upsert into ChromaDB Vector Index (one vector per photo, so | |
91 | + # multiple photos of the same person coexist instead of overwriting). | |
92 | + vector_db.add_face_embedding(embedding_id, person_id, embedding, metadata) | |
93 | + | |
94 | + # 2. Upsert into SQLite Relational Database | |
95 | + db = SessionLocal() | |
96 | + try: | |
97 | + person = db.query(Personnel).filter(Personnel.person_id == person_id).first() | |
98 | + if not person: | |
99 | + person = Personnel( | |
100 | + person_id=person_id, | |
101 | + name=metadata.get("name", person_id), | |
102 | + role=metadata.get("role", "Employee"), | |
103 | + department=metadata.get("department", "General"), | |
104 | + email=metadata.get("email"), | |
105 | + phone=metadata.get("phone"), | |
106 | + metadata_json=json.dumps(metadata) | |
107 | + ) | |
108 | + db.add(person) | |
109 | + else: | |
110 | + person.name = metadata.get("name", person.name) | |
111 | + person.role = metadata.get("role", person.role) | |
112 | + person.department = metadata.get("department", person.department) | |
113 | + person.metadata_json = json.dumps(metadata) | |
114 | + | |
115 | + db.commit() | |
116 | + print(f"[Ingestion Success] Registered/Updated {person_id} ({metadata.get('name')}) into VectorDB & SQLite") | |
117 | + finally: | |
118 | + db.close() | |
119 | + | |
120 | + except Exception as e: | |
121 | + print(f"[Ingestion Error] Failed to process {img_path}: {e}") | |
122 | + | |
123 | + def sync_all(self): | |
124 | + """Scans the entire known_persons folder for plug-and-play enrollment.""" | |
125 | + if not os.path.exists(self.known_persons_dir): | |
126 | + os.makedirs(self.known_persons_dir, exist_ok=True) | |
127 | + return | |
128 | + | |
129 | + items = os.listdir(self.known_persons_dir) | |
130 | + for item in items: | |
131 | + full_path = os.path.join(self.known_persons_dir, item) | |
132 | + if os.path.isdir(full_path): | |
133 | + self.register_person_from_folder(full_path) | |
134 | + elif os.path.isfile(full_path): | |
135 | + self.register_single_file(full_path) | |
136 | + | |
137 | +class DirectoryWatchHandler(FileSystemEventHandler): | |
138 | + def __init__(self, service: PlugAndPlayIngestionService): | |
139 | + self.service = service | |
140 | + | |
141 | + def on_created(self, event): | |
142 | + if event.is_directory: | |
143 | + self.service.register_person_from_folder(event.src_path) | |
144 | + else: | |
145 | + self.service.register_single_file(event.src_path) | |
146 | + | |
147 | +ingestion_service = PlugAndPlayIngestionService() | |
148 | + | |
149 | +def start_directory_watcher(): | |
150 | + event_handler = DirectoryWatchHandler(ingestion_service) | |
151 | + observer = Observer() | |
152 | + observer.schedule(event_handler, path=config.KNOWN_PERSONS_DIR, recursive=True) | |
153 | + observer.start() | |
154 | + print(f"[PlugAndPlay Watcher] Monitoring {config.KNOWN_PERSONS_DIR} for instant folder/JSON ingestion...") | |
155 | + return observer | |
0 | 156 | |
▼backend/app/services/logging_service.py
+51-0
| @@ -0,0 +1,51 @@ | ||
1 | +import logging | |
2 | +from app.utils.timeutil import utcnow | |
3 | +import logging.handlers | |
4 | +import sys | |
5 | +from datetime import datetime | |
6 | +from typing import Optional | |
7 | + | |
8 | +from app.config import config | |
9 | + | |
10 | +_fmt = logging.Formatter( | |
11 | + "%(asctime)s | %(levelname)-8s | %(name)s | %(message)s", | |
12 | + datefmt="%Y-%m-%d %H:%M:%S", | |
13 | +) | |
14 | + | |
15 | + | |
16 | +def setup_logging(level: Optional[str] = None) -> None: | |
17 | + """Configure the root logger: rotating file under data/logs + console handler.""" | |
18 | + level = level or config.LOG_LEVEL | |
19 | + root = logging.getLogger() | |
20 | + root.setLevel(level.upper()) | |
21 | + | |
22 | + if root.handlers: | |
23 | + return | |
24 | + | |
25 | + log_dir = config.LOGS_DIR | |
26 | + log_dir.mkdir(parents=True, exist_ok=True) | |
27 | + | |
28 | + file_handler = logging.handlers.RotatingFileHandler( | |
29 | + log_dir / "aegisvision.log", | |
30 | + maxBytes=5 * 1024 * 1024, | |
31 | + backupCount=3, | |
32 | + encoding="utf-8", | |
33 | + ) | |
34 | + file_handler.setFormatter(_fmt) | |
35 | + file_handler.setLevel(level.upper()) | |
36 | + root.addHandler(file_handler) | |
37 | + | |
38 | + console = logging.StreamHandler(sys.stdout) | |
39 | + console.setFormatter(_fmt) | |
40 | + console.setLevel(level.upper()) | |
41 | + root.addHandler(console) | |
42 | + | |
43 | + | |
44 | +def get_logger(name: str) -> logging.Logger: | |
45 | + """Return a module-scoped logger (root logger already configured on startup).""" | |
46 | + return logging.getLogger(name) | |
47 | + | |
48 | + | |
49 | +def log_context() -> dict: | |
50 | + """Standard structured fields for charting/analytics logging queues.""" | |
51 | + return {"ts": utcnow().isoformat()} | |
0 | 52 | |
▼backend/app/services/notify.py
+78-0
| @@ -0,0 +1,78 @@ | ||
1 | +import json | |
2 | +import urllib.parse | |
3 | +import urllib.request | |
4 | + | |
5 | +from app.config import config | |
6 | + | |
7 | +_SEVERITIES = ["INFO", "WARNING", "CRITICAL"] | |
8 | + | |
9 | + | |
10 | +class PushNotifier: | |
11 | + """Config-gated external event push: Telegram bot + generic webhook. | |
12 | + | |
13 | + Each provider is a no-op when its config (token / url) is empty. Delivery is | |
14 | + best-effort and never raises — alerting must never crash the pipeline. | |
15 | + """ | |
16 | + | |
17 | + def _enabled(self) -> bool: | |
18 | + min_sev = (config.NOTIFY_MIN_SEVERITY or "WARNING").upper() | |
19 | + target = (self._severity or "").upper() or "INFO" | |
20 | + return _SEVERITIES.index(target) >= _SEVERITIES.index(min_sev) | |
21 | + | |
22 | + def __init__(self, severity: str = "WARNING"): | |
23 | + self._severity = severity | |
24 | + self._enabled_flag = self._enabled() | |
25 | + | |
26 | + def should_publish(self, severity: str) -> bool: | |
27 | + if not self._enabled_flag: | |
28 | + return False | |
29 | + target = (severity or "INFO").upper() | |
30 | + min_sev = (config.NOTIFY_MIN_SEVERITY or "WARNING").upper() | |
31 | + return _SEVERITIES.index(target) >= _SEVERITIES.index(min_sev) | |
32 | + | |
33 | + def _post_form(self, url: str, payload: dict) -> None: | |
34 | + data = urllib.parse.urlencode(payload).encode() | |
35 | + req = urllib.request.Request(url, data=data, method="POST") | |
36 | + with urllib.request.urlopen(req, timeout=5): | |
37 | + pass | |
38 | + | |
39 | + def _post_json(self, url: str, payload: dict) -> None: | |
40 | + req = urllib.request.Request( | |
41 | + url, | |
42 | + data=json.dumps(payload).encode(), | |
43 | + headers={"Content-Type": "application/json"}, | |
44 | + method="POST", | |
45 | + ) | |
46 | + with urllib.request.urlopen(req, timeout=5): | |
47 | + pass | |
48 | + | |
49 | + def notify(self, event: dict) -> None: | |
50 | + if not self.should_publish(str(event.get("severity", "INFO"))): | |
51 | + return | |
52 | + | |
53 | + description = event.get("description") or event.get("alert_type") or "AegisVision alert" | |
54 | + prefix = f"{event.get('severity', 'INFO')} | {event.get('alert_type', 'ALERT')}" | |
55 | + text = f"[AegisVision] {prefix}\n{description}" | |
56 | + if event.get("camera_id"): | |
57 | + text += f"\nCamera: {event['camera_id']}" | |
58 | + if event.get("person_id"): | |
59 | + text += f"\nPerson: {event['person_id']}" | |
60 | + | |
61 | + if config.TELEGRAM_BOT_TOKEN and config.TELEGRAM_CHAT_ID: | |
62 | + try: | |
63 | + self._post_form( | |
64 | + f"https://api.telegram.org/bot{config.TELEGRAM_BOT_TOKEN}/sendMessage", | |
65 | + {"chat_id": config.TELEGRAM_CHAT_ID, "text": text}, | |
66 | + ) | |
67 | + except Exception: | |
68 | + pass | |
69 | + | |
70 | + if config.NOTIFY_WEBHOOK_URL: | |
71 | + try: | |
72 | + self._post_json(config.NOTIFY_WEBHOOK_URL, event) | |
73 | + except Exception: | |
74 | + pass | |
75 | + | |
76 | + | |
77 | +def push_alert(event: dict) -> None: | |
78 | + PushNotifier(severity=str(event.get("severity", "INFO"))).notify(event) | |
0 | 79 | |
▼backend/app/services/productivity_engine.py
+140-0
| @@ -0,0 +1,140 @@ | ||
1 | +import time | |
2 | +import cv2 | |
3 | +import numpy as np | |
4 | +from datetime import datetime | |
5 | +from typing import Dict, Any, List | |
6 | +from app.db.sqlite_db import SessionLocal | |
7 | +from app.db.models import ProductivityLog | |
8 | +from app.services.alert_service import emit_alert | |
9 | +from app.config import config | |
10 | + | |
11 | +class ProductivityEngine: | |
12 | + def __init__(self): | |
13 | + self.presence_tracker: Dict[str, Dict[str, Any]] = {} | |
14 | + | |
15 | + def detect_phone_in_hand_or_face(self, frame: np.ndarray, face_bbox: List[int]) -> bool: | |
16 | + """ | |
17 | + Detects smartphone usage near the face area using color & edge analysis. | |
18 | + Mobile screens/chassis exhibit rectangular contours and high screen contrast near hands/face. | |
19 | + """ | |
20 | + if frame is None or not face_bbox or len(face_bbox) < 4: | |
21 | + return False | |
22 | + | |
23 | + x1, y1, x2, y2 = face_bbox | |
24 | + h, w, _ = frame.shape | |
25 | + | |
26 | + # Define search region around face (hands holding phone near chin/cheek) | |
27 | + roi_y1 = max(0, y1 - 20) | |
28 | + roi_y2 = min(h, y2 + 100) | |
29 | + roi_x1 = max(0, x1 - 60) | |
30 | + roi_x2 = min(w, x2 + 60) | |
31 | + | |
32 | + roi = frame[roi_y1:roi_y2, roi_x1:roi_x2] | |
33 | + if roi.size == 0: | |
34 | + return False | |
35 | + | |
36 | + gray = cv2.cvtColor(roi, cv2.COLOR_BGR2GRAY) | |
37 | + edges = cv2.Canny(gray, 75, 200) | |
38 | + | |
39 | + # Look for rectangular contours matching standard phone aspect ratio (approx 16:9 or 2:1) | |
40 | + contours, _ = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) | |
41 | + for cnt in contours: | |
42 | + area = cv2.contourArea(cnt) | |
43 | + if 800 < area < 15000: | |
44 | + peri = cv2.arcLength(cnt, True) | |
45 | + approx = cv2.approxPolyDP(cnt, 0.04 * peri, True) | |
46 | + if len(approx) == 4: | |
47 | + rx, ry, rw, rh = cv2.boundingRect(cnt) | |
48 | + aspect_ratio = float(rh) / rw if rw > 0 else 0 | |
49 | + if 1.4 < aspect_ratio < 2.5 or 0.4 < aspect_ratio < 0.7: | |
50 | + return True | |
51 | + return False | |
52 | + | |
53 | + def update_presence(self, person_id: str, is_present: bool, camera_id: str, frame: np.ndarray = None, face_bbox: List[int] = None) -> Dict[str, Any]: | |
54 | + """ | |
55 | + Updates employee/student workstation presence state. | |
56 | + Tracks active desk time, idle time, bunking, and phone usage. | |
57 | + """ | |
58 | + if person_id == "UNKNOWN": | |
59 | + return {"status": "UNKNOWN_VISITOR"} | |
60 | + | |
61 | + now = time.time() | |
62 | + if person_id not in self.presence_tracker: | |
63 | + self.presence_tracker[person_id] = { | |
64 | + "first_seen": now, | |
65 | + "last_seen": now, | |
66 | + "active_time": 0, | |
67 | + "phone_time": 0, | |
68 | + "status": "PRODUCTIVE", | |
69 | + "phone_counter": 0 | |
70 | + } | |
71 | + | |
72 | + tracker = self.presence_tracker[person_id] | |
73 | + | |
74 | + if is_present: | |
75 | + elapsed = now - tracker["last_seen"] | |
76 | + tracker["active_time"] += elapsed if elapsed < 10 else 1 | |
77 | + tracker["last_seen"] = now | |
78 | + | |
79 | + # Check phone usage | |
80 | + has_phone = False | |
81 | + if frame is not None and face_bbox: | |
82 | + has_phone = self.detect_phone_in_hand_or_face(frame, face_bbox) | |
83 | + | |
84 | + if has_phone: | |
85 | + tracker["phone_counter"] += 1 | |
86 | + tracker["phone_time"] += 1 | |
87 | + if tracker["phone_counter"] > 5: | |
88 | + tracker["status"] = "PHONE_DISTRACTED" | |
89 | + else: | |
90 | + tracker["phone_counter"] = max(0, tracker["phone_counter"] - 1) | |
91 | + if tracker["phone_counter"] == 0: | |
92 | + tracker["status"] = "PRODUCTIVE" | |
93 | + | |
94 | + else: | |
95 | + idle_duration = now - tracker["last_seen"] | |
96 | + if idle_duration > config.IDLE_TIMEOUT_SECONDS: | |
97 | + tracker["status"] = "BUNKING" | |
98 | + elif idle_duration > 60: | |
99 | + tracker["status"] = "LAZY_IDLE" | |
100 | + | |
101 | + # Log session status to SQLite | |
102 | + db = SessionLocal() | |
103 | + alert_trigger = None | |
104 | + try: | |
105 | + log = ProductivityLog( | |
106 | + person_id=person_id, | |
107 | + status=tracker["status"], | |
108 | + duration_seconds=int(tracker["active_time"]), | |
109 | + camera_id=camera_id, | |
110 | + notes=f"Phone time: {int(tracker['phone_time'])}s" | |
111 | + ) | |
112 | + db.add(log) | |
113 | + | |
114 | + # Trigger alert for excessive phone use or bunking | |
115 | + if tracker["status"] in ["PHONE_DISTRACTED", "BUNKING"] and tracker["phone_counter"] == 6: | |
116 | + alert_trigger = ( | |
117 | + "PHONE_OVERUSE" if tracker["status"] == "PHONE_DISTRACTED" else "BUNKING_EXCEEDED", | |
118 | + tracker["status"], | |
119 | + ) | |
120 | + | |
121 | + db.commit() | |
122 | + except Exception as e: | |
123 | + db.rollback() | |
124 | + print(f"[ProductivityEngine Error] {e}") | |
125 | + finally: | |
126 | + db.close() | |
127 | + | |
128 | + if alert_trigger: | |
129 | + alert_type, status = alert_trigger | |
130 | + emit_alert( | |
131 | + alert_type=alert_type, | |
132 | + severity="WARNING", | |
133 | + description=f"Person {person_id} flagged for {status} at workstation.", | |
134 | + camera_id=camera_id, | |
135 | + person_id=person_id, | |
136 | + ) | |
137 | + | |
138 | + return tracker | |
139 | + | |
140 | +productivity_engine = ProductivityEngine() | |
0 | 141 | |
▼backend/app/services/rule_engine.py
+121-0
| @@ -0,0 +1,121 @@ | ||
1 | +import asyncio | |
2 | +from app.utils.timeutil import utcnow | |
3 | +import logging | |
4 | +from collections import defaultdict | |
5 | +from datetime import datetime, timedelta | |
6 | + | |
7 | +from app.config import config | |
8 | +from app.db.sqlite_db import SessionLocal | |
9 | +from app.db.models import AttendanceLog, HealthLog, ProductivityLog | |
10 | +from app.services.alert_service import emit_alert | |
11 | + | |
12 | +logger = logging.getLogger("aegisvision.rule_engine") | |
13 | + | |
14 | + | |
15 | +class RuleEngine: | |
16 | + """Background rule-based escalation over recent telemetry. | |
17 | + | |
18 | + Runs a periodic sweep (RULE_SWEEP_INTERVAL_SEC) that looks for repeated signals | |
19 | + inside the RULE_WINDOW_MIN window and emits severity-ranked IncidentAlerts | |
20 | + (which are WS-pushed and forwarded to external providers by alert_service). | |
21 | + Per-signal dedupe prevents alert storms. | |
22 | + """ | |
23 | + | |
24 | + def __init__(self): | |
25 | + self._last_emitted: dict[str, datetime] = {} | |
26 | + self._running = False | |
27 | + | |
28 | + def _dedupe(self, key: str) -> bool: | |
29 | + now = utcnow() | |
30 | + last = self._last_emitted.get(key) | |
31 | + if last and (now - last).total_seconds() < config.ESCALATION_DEDUPE_SEC: | |
32 | + return False | |
33 | + self._last_emitted[key] = now | |
34 | + return True | |
35 | + | |
36 | + def sweep(self) -> int: | |
37 | + """Evaluate rules once; returns number of alerts emitted.""" | |
38 | + db = SessionLocal() | |
39 | + emitted = 0 | |
40 | + try: | |
41 | + since = utcnow() - timedelta(minutes=config.RULE_WINDOW_MIN) | |
42 | + window_desc = f"last {config.RULE_WINDOW_MIN} min" | |
43 | + | |
44 | + unknown_count = ( | |
45 | + db.query(AttendanceLog) | |
46 | + .filter(AttendanceLog.timestamp >= since, AttendanceLog.name == "UNKNOWN") | |
47 | + .count() | |
48 | + ) | |
49 | + if unknown_count >= config.UNKNOWN_GATE_COUNT and self._dedupe("UNKNOWN_GATE_REPEATED"): | |
50 | + emit_alert( | |
51 | + alert_type="UNKNOWN_REPEATED", | |
52 | + severity="CRITICAL", | |
53 | + description=f"{unknown_count} UNKNOWN individuals registered at the gate in the {window_desc}. Consider reviewing footage.", | |
54 | + camera_id="cam_entrance", | |
55 | + ) | |
56 | + emitted += 1 | |
57 | + | |
58 | + per_person = defaultdict(int) | |
59 | + for row in ( | |
60 | + db.query(ProductivityLog) | |
61 | + .filter(ProductivityLog.timestamp >= since, ProductivityLog.status == "PHONE_DISTRACTED") | |
62 | + .all() | |
63 | + ): | |
64 | + if row.person_id: | |
65 | + per_person[row.person_id] += 1 | |
66 | + for person_id, cnt in per_person.items(): | |
67 | + if cnt >= config.PHONE_OVERUSE_COUNT and self._dedupe(f"PHONE_OVERUSE_{person_id}"): | |
68 | + emit_alert( | |
69 | + alert_type="PHONE_OVERUSE", | |
70 | + severity="WARNING", | |
71 | + description=f"{person_id} was phone-distracted {cnt} times in the {window_desc}.", | |
72 | + camera_id="cam_workspace", | |
73 | + person_id=person_id, | |
74 | + ) | |
75 | + emitted += 1 | |
76 | + | |
77 | + drowsy = defaultdict(int) | |
78 | + for row in ( | |
79 | + db.query(HealthLog) | |
80 | + .filter(HealthLog.timestamp >= since, HealthLog.drowsiness_detected == True) | |
81 | + .all() | |
82 | + ): | |
83 | + if row.person_id: | |
84 | + drowsy[row.person_id] += 1 | |
85 | + for person_id, cnt in drowsy.items(): | |
86 | + if cnt >= config.DROWSINESS_REPEAT_COUNT and self._dedupe(f"DROWSINESS_{person_id}"): | |
87 | + emit_alert( | |
88 | + alert_type="DROWSINESS_WARNING", | |
89 | + severity="WARNING", | |
90 | + description=f"Repeated drowsiness for {person_id} ({cnt} detections in the {window_desc}). Fatigue risk.", | |
91 | + camera_id="cam_workspace", | |
92 | + person_id=person_id, | |
93 | + ) | |
94 | + emitted += 1 | |
95 | + | |
96 | + return emitted | |
97 | + except Exception: | |
98 | + logger.exception("Rule sweep failed") | |
99 | + return emitted | |
100 | + finally: | |
101 | + db.close() | |
102 | + | |
103 | + async def run(self) -> None: | |
104 | + self._running = True | |
105 | + while self._running: | |
106 | + try: | |
107 | + await asyncio.sleep(config.RULE_SWEEP_INTERVAL_SEC) | |
108 | + emitted = self.sweep() | |
109 | + if emitted: | |
110 | + logger.info("Rule engine emitted %s escalation alert(s)", emitted) | |
111 | + except asyncio.CancelledError: | |
112 | + break | |
113 | + except Exception: | |
114 | + logger.exception("Rule engine sweep crashed") | |
115 | + self._running = False | |
116 | + | |
117 | + def stop(self) -> None: | |
118 | + self._running = False | |
119 | + | |
120 | + | |
121 | +rule_engine = RuleEngine() | |
0 | 122 | |
▼backend/app/services/spatial_memory.py
+193-0
| @@ -0,0 +1,193 @@ | ||
1 | +import json | |
2 | +from app.utils.timeutil import utcnow | |
3 | +import time | |
4 | +import cv2 | |
5 | +import numpy as np | |
6 | +from datetime import datetime | |
7 | +from typing import Dict, Any, List, Optional, Tuple | |
8 | +from app.db.sqlite_db import SessionLocal | |
9 | +from app.db.models import AssetLog | |
10 | +from app.services.alert_service import emit_alert | |
11 | +from app.config import config | |
12 | + | |
13 | + | |
14 | +def _overlap(a: List[int], b: List[int]) -> bool: | |
15 | + ax1, ay1, ax2, ay2 = a | |
16 | + bx1, by1, bx2, by2 = b | |
17 | + return not (ax2 < bx1 or bx2 < ax1 or ay2 < by1 or by2 < ay1) | |
18 | + | |
19 | + | |
20 | +class SpatialMemoryEngine: | |
21 | + """Spatial Memory & Anti-Theft Asset Tracking. | |
22 | + | |
23 | + Remembers registered asset regions per camera. On every frame it compares the | |
24 | + current region against a slowly-adapted background patch. Sustained motion in a | |
25 | + registered region (without the owner present) raises a displacement alert; an | |
26 | + UNKNOWN person near the asset escalates to a CRITICAL theft attempt. | |
27 | + """ | |
28 | + | |
29 | + def __init__(self): | |
30 | + self.tracked_assets: Dict[str, Dict[str, Any]] = {} | |
31 | + self._refs: Dict[str, np.ndarray] = {} | |
32 | + self._motion_state: Dict[str, int] = {} | |
33 | + self._last_alert: Dict[str, float] = {} | |
34 | + | |
35 | + # ---- API / registration ---- | |
36 | + def register_or_update_asset(self, asset_name: str, owner_id: str, bbox: List[int], camera_id: str = "cam_workspace"): | |
37 | + db = SessionLocal() | |
38 | + try: | |
39 | + asset = db.query(AssetLog).filter(AssetLog.asset_name == asset_name).first() | |
40 | + bbox_str = json.dumps(bbox) | |
41 | + if not asset: | |
42 | + asset = AssetLog( | |
43 | + asset_name=asset_name, | |
44 | + owner_person_id=owner_id, | |
45 | + last_seen_bbox=bbox_str, | |
46 | + camera_id=camera_id, | |
47 | + status="NORMAL", | |
48 | + ) | |
49 | + db.add(asset) | |
50 | + else: | |
51 | + asset.last_seen_bbox = bbox_str | |
52 | + asset.camera_id = camera_id | |
53 | + asset.status = "NORMAL" | |
54 | + asset.updated_at = utcnow() | |
55 | + db.commit() | |
56 | + self.tracked_assets[asset_name] = { | |
57 | + "owner_id": owner_id, | |
58 | + "bbox": [int(v) for v in bbox], | |
59 | + "camera_id": camera_id, | |
60 | + "status": "NORMAL", | |
61 | + } | |
62 | + self._refs.pop(asset_name, None) | |
63 | + self._motion_state.pop(asset_name, None) | |
64 | + except Exception as e: | |
65 | + db.rollback() | |
66 | + print(f"[SpatialMemory Error] {e}") | |
67 | + finally: | |
68 | + db.close() | |
69 | + | |
70 | + def delete_asset(self, asset_name: str) -> bool: | |
71 | + db = SessionLocal() | |
72 | + try: | |
73 | + asset = db.query(AssetLog).filter(AssetLog.asset_name == asset_name).first() | |
74 | + if not asset: | |
75 | + return False | |
76 | + db.delete(asset) | |
77 | + db.commit() | |
78 | + self.tracked_assets.pop(asset_name, None) | |
79 | + self._refs.pop(asset_name, None) | |
80 | + self._motion_state.pop(asset_name, None) | |
81 | + return True | |
82 | + finally: | |
83 | + db.close() | |
84 | + | |
85 | + # ---- live surveillance ---- | |
86 | + def monitor(self, camera_id: str, frame: np.ndarray, detections: List[Dict[str, Any]]) -> List[Dict[str, Any]]: | |
87 | + """Per-frame region-diff surveillance against registered assets on this camera.""" | |
88 | + if frame is None or frame.size == 0: | |
89 | + return [] | |
90 | + now = time.time() | |
91 | + alerts_generated = [] | |
92 | + h, w, _ = frame.shape | |
93 | + | |
94 | + db = SessionLocal() | |
95 | + try: | |
96 | + assets = db.query(AssetLog).filter(AssetLog.camera_id == camera_id).all() | |
97 | + for asset in assets: | |
98 | + stored = json.loads(asset.last_seen_bbox) if asset.last_seen_bbox else [] | |
99 | + if len(stored) < 4: | |
100 | + continue | |
101 | + x1, y1, x2, y2 = (max(0, int(v)) for v in stored) | |
102 | + x1, y1 = min(x1, w - 2), min(y1, h - 2) | |
103 | + x2, y2 = max(x1 + 1, min(x2, w)), max(y1 + 1, min(y2, h)) | |
104 | + if (x2 - x1) < 4 or (y2 - y1) < 4: | |
105 | + continue | |
106 | + | |
107 | + crop = frame[y1:y2, x1:x2] | |
108 | + gray = cv2.cvtColor(crop, cv2.COLOR_BGR2GRAY) | |
109 | + gray = cv2.resize(gray, (64, 48)).astype(np.float32) | |
110 | + | |
111 | + ref = self._refs.get(asset.asset_name) | |
112 | + if ref is None: | |
113 | + self._refs[asset.asset_name] = gray | |
114 | + self._motion_state[asset.asset_name] = 0 | |
115 | + continue | |
116 | + | |
117 | + diff = float(np.mean(np.abs(gray - ref))) | |
118 | + movement = diff > config.ASSET_MOTION_DIFF_THRESHOLD | |
119 | + | |
120 | + if not movement: | |
121 | + self._motion_state[asset.asset_name] = 0 | |
122 | + self._refs[asset.asset_name] = ref * 0.9 + gray * 0.1 | |
123 | + continue | |
124 | + | |
125 | + owner_near = False | |
126 | + stranger_near = False | |
127 | + asset_bbox = [x1, y1, x2, y2] | |
128 | + for det in detections: | |
129 | + if not _overlap(asset_bbox, det.get("bbox", [])): | |
130 | + continue | |
131 | + if det.get("person_id") == asset.owner_person_id: | |
132 | + owner_near = True | |
133 | + elif det.get("person_id") is not None: | |
134 | + stranger_near = True | |
135 | + | |
136 | + if owner_near: | |
137 | + self._motion_state[asset.asset_name] = 0 | |
138 | + self._refs[asset.asset_name] = gray | |
139 | + continue | |
140 | + | |
141 | + state = self._motion_state.get(asset.asset_name, 0) + 1 | |
142 | + self._motion_state[asset.asset_name] = state | |
143 | + if state < config.ASSET_MOTION_CONFIRM_FRAMES: | |
144 | + continue | |
145 | + | |
146 | + self._motion_state[asset.asset_name] = 0 | |
147 | + if now - self._last_alert.get(asset.asset_name, 0) < config.ASSET_ALERT_COOLDOWN_SECONDS: | |
148 | + continue | |
149 | + self._last_alert[asset.asset_name] = now | |
150 | + | |
151 | + asset.status = "THEFT_SUSPECTED" if stranger_near else "DISPLACED_MISSING" | |
152 | + db.add(asset) | |
153 | + | |
154 | + if stranger_near: | |
155 | + emit_alert( | |
156 | + alert_type="THEFT_ATTEMPT", | |
157 | + severity="CRITICAL", | |
158 | + description=f"CRITICAL THEFT ALERT: UNKNOWN individual near registered asset '{asset.asset_name}' (Owner: {asset.owner_person_id}).", | |
159 | + camera_id=camera_id, | |
160 | + person_id="UNKNOWN", | |
161 | + ) | |
162 | + alerts_generated.append({ | |
163 | + "asset_name": asset.asset_name, | |
164 | + "status": "THEFT_SUSPECTED", | |
165 | + "owner_id": asset.owner_person_id, | |
166 | + }) | |
167 | + else: | |
168 | + emit_alert( | |
169 | + alert_type="ASSET_DISPLACED", | |
170 | + severity="WARNING", | |
171 | + description=f"Asset '{asset.asset_name}' was moved/detected at a new location (Owner: {asset.owner_person_id}).", | |
172 | + camera_id=camera_id, | |
173 | + person_id=asset.owner_person_id, | |
174 | + ) | |
175 | + alerts_generated.append({ | |
176 | + "asset_name": asset.asset_name, | |
177 | + "status": "DISPLACED_MISSING", | |
178 | + "owner_id": asset.owner_person_id, | |
179 | + }) | |
180 | + | |
181 | + self._refs[asset.asset_name] = gray | |
182 | + | |
183 | + db.commit() | |
184 | + except Exception as e: | |
185 | + db.rollback() | |
186 | + print(f"[SpatialMemory Error] {e}") | |
187 | + finally: | |
188 | + db.close() | |
189 | + | |
190 | + return alerts_generated | |
191 | + | |
192 | + | |
193 | +spatial_memory = SpatialMemoryEngine() | |
0 | 194 | |
▼backend/app/utils/timeutil.py
+7-0
| @@ -0,0 +1,7 @@ | ||
1 | +from datetime import datetime, timezone | |
2 | + | |
3 | + | |
4 | +def utcnow() -> datetime: | |
5 | + """Naive UTC clock (matches the SQLite `DateTime` columns) without the | |
6 | + deprecated `datetime.utcnow()` call.""" | |
7 | + return datetime.now(timezone.utc).replace(tzinfo=None) | |
0 | 8 | |
▼backend/funnytest.py
+0-28
| @@ -1,28 +0,0 @@ | ||
1 | -# import os | |
2 | - | |
3 | - | |
4 | -# filepath=os.listdir("/home/swadhin/GitHubproject/liveattendence_with_anti_cheat/backend/app/functions/pictures") | |
5 | -# ids=[] | |
6 | -# for file in filepath: | |
7 | - | |
8 | -# id=file.split(".")[0] | |
9 | -# ids.append(id) | |
10 | - | |
11 | - | |
12 | -# print(ids) | |
13 | - | |
14 | - | |
15 | -def hello(): | |
16 | - y=3 | |
17 | - x=4 | |
18 | - yield y | |
19 | - return x | |
20 | - | |
21 | - | |
22 | -def hello2(): | |
23 | - u=8 | |
24 | - x=hello() | |
25 | - print(x) | |
26 | - | |
27 | -print(hello2()) | |
28 | - | |
29 | 0 | |
▼backend/main.py
+110-24
| @@ -1,15 +1,88 @@ | ||
1 | - | |
1 | +import asyncio | |
2 | +import logging | |
3 | +import os | |
4 | +from contextlib import asynccontextmanager | |
5 | +from fastapi import FastAPI | |
2 | 6 | from fastapi.middleware.cors import CORSMiddleware |
3 | -from fastapi import FastAPI, APIRouter | |
4 | -from app.routes.routesadd import router | |
7 | +from fastapi.staticfiles import StaticFiles | |
8 | + | |
9 | +from app.config import config | |
10 | +from app.db.sqlite_db import init_sqlite_db | |
11 | +from app.services.logging_service import setup_logging | |
12 | +from app.errors import add_exception_handlers | |
13 | +from app.services.ingestion_service import ingestion_service, start_directory_watcher | |
14 | +from app.services.camera_manager import camera_manager | |
15 | +from app.services.camera_ops_service import camera_ops_service | |
16 | +from app.services.alert_dispatcher import alert_dispatcher | |
17 | +from app.services.rule_engine import rule_engine | |
18 | +from app.routes.stream import router as stream_router | |
19 | +from app.routes.personnel import router as personnel_router | |
20 | +from app.routes.analytics import router as analytics_router | |
21 | +from app.routes.alerts import router as alerts_router | |
22 | +from app.routes.copilot import router as copilot_router | |
23 | +from app.routes.assets import router as assets_router | |
24 | +from app.routes.auth import router as auth_router | |
25 | +from app.routes.system import router as system_router | |
26 | +from app.routes.metrics import router as metrics_router | |
27 | + | |
28 | +logger = logging.getLogger("aegisvision.main") | |
29 | + | |
30 | +@asynccontextmanager | |
31 | +async def lifespan(app: FastAPI): | |
32 | + # 0. Structured logging (rotating file + console). | |
33 | + setup_logging() | |
34 | + | |
35 | + # 1. Capture the main event loop for threadsafe WS alert broadcast. | |
36 | + alert_dispatcher.bind_loop(asyncio.get_running_loop()) | |
37 | + | |
38 | + # 2. Initialize SQLite Database Tables | |
39 | + logger.info("Initializing SQLite Relational Database...") | |
40 | + init_sqlite_db() | |
41 | + | |
42 | + # 3. Sync Plug-and-Play folder/JSON enrolments | |
43 | + logger.info("Syncing plug-and-play enrolments from data/known_persons...") | |
44 | + ingestion_service.sync_all() | |
45 | + | |
46 | + # 4. Start directory watcher | |
47 | + observer = start_directory_watcher() | |
48 | + | |
49 | + # 5. Start camera workers (one thread per configured stream) | |
50 | + logger.info("Starting %s camera workers...", len(config.CAMERA_STREAMS)) | |
51 | + camera_manager.start_all() | |
52 | + | |
53 | + # 5b. Start the fleet housekeeper (feed health, activity flush, retention). | |
54 | + camera_ops_service.start() | |
55 | + | |
56 | + # 6. Start the rule-escalation background sweep. | |
57 | + rule_task = asyncio.create_task(rule_engine.run()) | |
5 | 58 | |
6 | -# from app.models.studentmodel import StudentModel | |
7 | -from app.settings.connectdb import Firebase | |
8 | -origins=[ | |
9 | - "*" | |
10 | -] | |
59 | + yield | |
11 | 60 | |
12 | -app=FastAPI() | |
61 | + # Cleanup on shutdown | |
62 | + logger.info("Stopping fleet housekeeper...") | |
63 | + camera_ops_service.stop() | |
64 | + logger.info("Stopping camera workers...") | |
65 | + camera_manager.stop_all() | |
66 | + logger.info("Stopping file system observer...") | |
67 | + observer.stop() | |
68 | + observer.join() | |
69 | + rule_task.cancel() | |
70 | + try: | |
71 | + await rule_task | |
72 | + except asyncio.CancelledError: | |
73 | + pass | |
74 | + | |
75 | +app = FastAPI( | |
76 | + title=config.PROJECT_NAME, | |
77 | + version=config.VERSION, | |
78 | + description="Enterprise Multi-Camera AI Surveillance, Analytics & Decision Platform", | |
79 | + lifespan=lifespan | |
80 | +) | |
81 | + | |
82 | +add_exception_handlers(app) | |
83 | + | |
84 | +# CORS Configuration | |
85 | +origins = ["*"] | |
13 | 86 | app.add_middleware( |
14 | 87 | CORSMiddleware, |
15 | 88 | allow_origins=origins, |
| @@ -18,23 +91,36 @@ app.add_middleware( | ||
18 | 91 | allow_headers=["*"], |
19 | 92 | ) |
20 | 93 | |
21 | -app.include_router(router) | |
22 | - | |
94 | +# Mount Routers | |
95 | +app.include_router(stream_router) | |
96 | +app.include_router(personnel_router) | |
97 | +app.include_router(analytics_router) | |
98 | +app.include_router(alerts_router) | |
99 | +app.include_router(copilot_router) | |
100 | +app.include_router(assets_router) | |
101 | +app.include_router(auth_router) | |
102 | +app.include_router(system_router) | |
103 | +app.include_router(metrics_router) | |
23 | 104 | |
24 | 105 | @app.get("/") |
25 | -async def home(): | |
26 | - return {"message":"Welcome to the Live Attendance System"} | |
106 | +async def root(): | |
107 | + return { | |
108 | + "status": "online", | |
109 | + "system": config.PROJECT_NAME, | |
110 | + "version": config.VERSION, | |
111 | + "documentation": "/docs" | |
112 | + } | |
27 | 113 | |
114 | +if config.SERVE_FRONTEND and config.FRONTEND_DIST.exists(): | |
115 | + app.mount("/", StaticFiles(directory=str(config.FRONTEND_DIST), html=True), name="frontend") | |
116 | +async def root(): | |
117 | + return { | |
118 | + "status": "online", | |
119 | + "system": config.PROJECT_NAME, | |
120 | + "version": config.VERSION, | |
121 | + "documentation": "/docs" | |
122 | + } | |
28 | 123 | |
29 | - | |
30 | - | |
31 | - | |
32 | - | |
33 | -if __name__ == '__main__': | |
124 | +if __name__ == "__main__": | |
34 | 125 | import uvicorn |
35 | - | |
36 | - uvicorn.run(app = "main:app", host="localhost", port=8000, reload=True) | |
37 | - | |
38 | - | |
39 | - | |
40 | - | |
126 | + uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True) | |
41 | 127 | |
▼backend/path.py
+0-1
| @@ -1 +0,0 @@ | ||
1 | -from app.functions.encodeGenarator import EndcodeGenarator | |
2 | 0 | |
▼backend/requirements.txt
+33-0
| @@ -0,0 +1,33 @@ | ||
1 | +# AegisVision backend dependencies (Python 3.12) | |
2 | +# Also install pandoc-free dlib build deps: cmake, build-essential, libopenblas-dev | |
3 | + | |
4 | +# --- Web / API --- | |
5 | +fastapi>=0.141,<1.0 | |
6 | +uvicorn[standard]>=0.52 | |
7 | +python-multipart>=0.0.32 | |
8 | +httpx>=0.28 | |
9 | + | |
10 | +# --- Data --- | |
11 | +sqlalchemy>=2.0,<3.0 | |
12 | +pydantic>=2.13 | |
13 | +pydantic-settings>=2.15 | |
14 | +chromadb>=1.5,<2.0 | |
15 | + | |
16 | +# --- Vision / ML --- | |
17 | +numpy>=2.5,<3.0 | |
18 | +opencv-python-headless>=5.0 | |
19 | +face-recognition>=1.3.0 | |
20 | +dlib>=20.0 | |
21 | +onnxruntime>=1.28 | |
22 | +pillow>=12.3 | |
23 | +# Lightweight+accurate recognition backend (SCRFD detect + ArcFace embed). | |
24 | +# Optional: without it the app automatically falls back to the dlib path above. | |
25 | +insightface>=1.0.1 | |
26 | + | |
27 | +# --- Ops --- | |
28 | +watchdog>=6.0 | |
29 | +google-genai>=2.17 | |
30 | + | |
31 | +# --- Dev / test --- | |
32 | +pytest>=8.0 | |
33 | +pytest-asyncio>=0.26 | |
0 | 34 | |
▼docker-compose.yml
+25-0
| @@ -0,0 +1,25 @@ | ||
1 | +services: | |
2 | + backend: | |
3 | + build: | |
4 | + context: . | |
5 | + dockerfile: backend/Dockerfile | |
6 | + container_name: aegisvision-backend | |
7 | + env_file: | |
8 | + - ./backend/.env.example | |
9 | + environment: | |
10 | + - DATA_DIR=/data | |
11 | + - VECTOR_DB_DIR=/data/vector_db | |
12 | + - KNOWN_PERSONS_DIR=/data/known_persons | |
13 | + - LOGS_DIR=/data/logs | |
14 | + - SNAPSHOTS_DIR=/data/snapshots | |
15 | + - SQLITE_URL=sqlite:////data/surveillance.db | |
16 | + - SERVE_FRONTEND=true | |
17 | + - FRONTEND_DIST=/frontend | |
18 | + volumes: | |
19 | + - aegisvision_data:/data | |
20 | + ports: | |
21 | + - "8000:8000" | |
22 | + restart: unless-stopped | |
23 | + | |
24 | +volumes: | |
25 | + aegisvision_data: | |
0 | 26 | |
▼docs/diagrams/architecture.svg
+201-0
| @@ -0,0 +1,201 @@ | ||
1 | +<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="920" viewBox="0 0 1440 920" font-family="'Segoe UI', system-ui, -apple-system, Arial, sans-serif"> | |
2 | + <defs> | |
3 | + <linearGradient id="bg" x1="0" y1="0" x2="0" y2="1"> | |
4 | + <stop offset="0" stop-color="#0b1019"/> | |
5 | + <stop offset="1" stop-color="#070b12"/> | |
6 | + </linearGradient> | |
7 | + <marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"> | |
8 | + <path d="M0 0 L10 5 L0 10 z" fill="#64748b"/> | |
9 | + </marker> | |
10 | + <marker id="arrA" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"> | |
11 | + <path d="M0 0 L10 5 L0 10 z" fill="#22d3ee"/> | |
12 | + </marker> | |
13 | + </defs> | |
14 | + | |
15 | + <rect width="1440" height="920" fill="url(#bg)"/> | |
16 | + | |
17 | + <!-- Title --> | |
18 | + <text x="720" y="40" text-anchor="middle" font-size="24" font-weight="700" fill="#f8fafc">AegisVision — System Architecture</text> | |
19 | + <text x="720" y="64" text-anchor="middle" font-size="12.5" fill="#64748b">Plug-and-play, multi-camera AI surveillance & behavioral-monitoring platform</text> | |
20 | + | |
21 | + <!-- ================= ROW 1 : SOURCES ================= --> | |
22 | + <g> | |
23 | + <rect x="40" y="96" width="380" height="176" rx="12" fill="#0e1522" stroke="#334155"/> | |
24 | + <text x="60" y="122" font-size="14" font-weight="700" fill="#f8fafc">Edge / CCTV / Webcam Sources</text> | |
25 | + <g font-size="11.5" fill="#94a3b8"> | |
26 | + <text x="60" y="146">• RTSP IP cameras · hardware encoders</text> | |
27 | + <text x="60" y="166">• MJPEG / HLS / USB webcams</text> | |
28 | + <text x="60" y="186">• config-driven · auto-reconnect</text> | |
29 | + </g> | |
30 | + <rect x="60" y="200" width="340" height="52" rx="8" fill="#141d2e" stroke="#22d3ee" stroke-dasharray="4 3"/> | |
31 | + <text x="230" y="219" text-anchor="middle" font-size="11" fill="#67e8f9">cam_entrance · cam_workspace · cam_restricted</text> | |
32 | + <text x="230" y="235" text-anchor="middle" font-size="10.5" fill="#475569">gate · productivity · restricted</text> | |
33 | + </g> | |
34 | + | |
35 | + <line x1="420" y1="184" x2="496" y2="184" stroke="#64748b" stroke-width="2" marker-end="url(#arr)"/> | |
36 | + | |
37 | + <!-- ================= ROW 1 : WORKERS ================= --> | |
38 | + <g> | |
39 | + <rect x="502" y="96" width="330" height="176" rx="12" fill="#0e1522" stroke="#334155"/> | |
40 | + <text x="667" y="122" text-anchor="middle" font-size="14" font-weight="700" fill="#f8fafc">Camera Worker Pool</text> | |
41 | + <g font-size="11.5" fill="#94a3b8"> | |
42 | + <text x="520" y="146">One background capture thread per source</text> | |
43 | + <text x="520" y="166">latest-frame sharing · back-pressure</text> | |
44 | + </g> | |
45 | + <rect x="520" y="184" width="294" height="52" rx="6" fill="#141d2e" stroke="#818cf8"/> | |
46 | + <text x="667" y="204" text-anchor="middle" font-size="11.5" fill="#c7d2fe">/api/stream/video_feed · snapshot</text> | |
47 | + <text x="667" y="224" text-anchor="middle" font-size="11.5" fill="#c7d2fe">/api/stream/ws · fleet · clips</text> | |
48 | + </g> | |
49 | + | |
50 | + <line x1="832" y1="184" x2="906" y2="184" stroke="#64748b" stroke-width="2" marker-end="url(#arr)"/> | |
51 | + | |
52 | + <!-- ================= ROW 1 : PIPELINE ================= --> | |
53 | + <g> | |
54 | + <rect x="912" y="88" width="488" height="192" rx="12" fill="#0e1522" stroke="#334155"/> | |
55 | + <text x="1156" y="114" text-anchor="middle" font-size="14" font-weight="700" fill="#f8fafc">Frame Pipeline (per camera)</text> | |
56 | + <g> | |
57 | + <rect x="928" y="128" width="88" height="40" rx="6" fill="#0b1019" stroke="#22d3ee"/> | |
58 | + <text x="971" y="147" text-anchor="middle" font-size="11.5" fill="#67e8f9">Detect</text> | |
59 | + <text x="971" y="163" text-anchor="middle" font-size="9.5" fill="#475569">SCRFD</text> | |
60 | + <rect x="1024" y="128" width="88" height="40" rx="6" fill="#0b1019" stroke="#22d3ee"/> | |
61 | + <text x="1068" y="147" text-anchor="middle" font-size="11.5" fill="#67e8f9">Track</text> | |
62 | + <text x="1068" y="163" text-anchor="middle" font-size="9.5" fill="#475569">IoU</text> | |
63 | + <rect x="1122" y="128" width="88" height="40" rx="6" fill="#0b1019" stroke="#34d399"/> | |
64 | + <text x="1166" y="147" text-anchor="middle" font-size="11.5" fill="#6ee7b7">Anti-spoof</text> | |
65 | + <text x="1166" y="163" text-anchor="middle" font-size="9.5" fill="#475569">liveness</text> | |
66 | + <rect x="1220" y="128" width="84" height="40" rx="6" fill="#0b1019" stroke="#818cf8"/> | |
67 | + <text x="1262" y="147" text-anchor="middle" font-size="11.5" fill="#c7d2fe">Embed</text> | |
68 | + <text x="1262" y="163" text-anchor="middle" font-size="9.5" fill="#475569">ArcFace 512-d</text> | |
69 | + <rect x="1314" y="128" width="72" height="40" rx="6" fill="#0b1019" stroke="#f59e0b"/> | |
70 | + <text x="1350" y="147" text-anchor="middle" font-size="11.5" fill="#fcd34d">Match</text> | |
71 | + <text x="1350" y="163" text-anchor="middle" font-size="9.5" fill="#475569">top-N</text> | |
72 | + </g> | |
73 | + <g font-size="10.5" fill="#64748b"> | |
74 | + <text x="926" y="194">backend: <tspan fill="#67e8f9">insightface/ONNX</tspan> (auto) · <tspan fill="#94a3b8">dlib</tspan> fallback</text> | |
75 | + <text x="926" y="210">embed dims <tspan fill="#818cf8">512</tspan> vs <tspan fill="#94a3b8">128</tspan> → vector index self-heals</text> | |
76 | + <text x="926" y="226">UNKNOWN ⇒ <tspan fill="#fb7185">no file written</tspan>, red on-frame tag</text> | |
77 | + </g> | |
78 | + <rect x="926" y="236" width="460" height="28" rx="6" fill="#101720" stroke="#1e293b"/> | |
79 | + <text x="1156" y="255" text-anchor="middle" font-size="10.5" fill="#64748b">identity + liveness + events → behavioral engines</text> | |
80 | + </g> | |
81 | + | |
82 | + <!-- arrows row1 -> row2 --> | |
83 | + <line x1="400" y1="280" x2="400" y2="318" stroke="#64748b" stroke-width="2" marker-end="url(#arr)"/> | |
84 | + | |
85 | + <!-- ================= ROW 2 : ENGINES ================= --> | |
86 | + <g> | |
87 | + <rect x="40" y="320" width="560" height="300" rx="12" fill="#0e1522" stroke="#334155"/> | |
88 | + <text x="60" y="348" font-size="14" font-weight="700" fill="#f8fafc">Behavioral Engines</text> | |
89 | + <rect x="60" y="362" width="520" height="56" rx="8" fill="#141d2e" stroke="#34d399"/> | |
90 | + <text x="76" y="382" font-size="12.5" font-weight="700" fill="#6ee7b7">Attendance & Gate Zones</text> | |
91 | + <text x="76" y="400" font-size="11" fill="#94a3b8">in/out line-crossing · live occupancy · 30 s cooldown per person per camera</text> | |
92 | + <rect x="60" y="428" width="520" height="56" rx="8" fill="#141d2e" stroke="#f59e0b"/> | |
93 | + <text x="76" y="448" font-size="12.5" font-weight="700" fill="#fcd34d">Productivity State Machine</text> | |
94 | + <text x="76" y="466" font-size="11" fill="#94a3b8">active · idle/lazy · bunking · phone-distraction windows</text> | |
95 | + <rect x="60" y="494" width="520" height="56" rx="8" fill="#141d2e" stroke="#fb7185"/> | |
96 | + <text x="76" y="514" font-size="12.5" font-weight="700" fill="#fda4af">Health & Safety Sentinel</text> | |
97 | + <text x="76" y="532" font-size="11" fill="#94a3b8">drowsiness · fatigue · posture · fall risk</text> | |
98 | + <rect x="60" y="560" width="520" height="56" rx="8" fill="#141d2e" stroke="#a78bfa"/> | |
99 | + <text x="76" y="580" font-size="12.5" font-weight="700" fill="#c4b5fd">Spatial / Anti-Theft Memory</text> | |
100 | + <text x="76" y="598" font-size="11" fill="#94a3b8">asset registry · displacement vs owner · UNKNOWN near asset ⇒ THEFT_SUSPECTED</text> | |
101 | + </g> | |
102 | + | |
103 | + <!-- ================= ROW 2 : DATA ================= --> | |
104 | + <g> | |
105 | + <rect x="640" y="320" width="360" height="300" rx="12" fill="#0e1522" stroke="#334155"/> | |
106 | + <text x="660" y="348" font-size="14" font-weight="700" fill="#f8fafc">Single Source of Truth</text> | |
107 | + <rect x="660" y="368" width="320" height="116" rx="8" fill="#101720" stroke="#22d3ee"/> | |
108 | + <text x="676" y="390" font-size="12.5" font-weight="700" fill="#67e8f9">ChromaDB · vector index (n-dim cosine)</text> | |
109 | + <g font-size="11" fill="#94a3b8"> | |
110 | + <text x="676" y="412">face_embeddings — one vector per photo</text> | |
111 | + <text x="676" y="432">top-N nearest · distance threshold gating</text> | |
112 | + <text x="676" y="452">self-healing rebuild on backend dimension change</text> | |
113 | + </g> | |
114 | + <rect x="660" y="496" width="320" height="118" rx="8" fill="#101720" stroke="#34d399"/> | |
115 | + <text x="676" y="518" font-size="12.5" font-weight="700" fill="#6ee7b7">SQLite · relational</text> | |
116 | + <g font-size="11" fill="#94a3b8"> | |
117 | + <text x="676" y="540">personnel · attendance_log · productivity_log</text> | |
118 | + <text x="676" y="560">health_log · asset_log · incident_alerts</text> | |
119 | + <text x="676" y="580">kept in sync with the vector index</text> | |
120 | + </g> | |
121 | + </g> | |
122 | + | |
123 | + <!-- ================= ROW 2 : RULES + PUSH ================= --> | |
124 | + <g> | |
125 | + <rect x="1040" y="322" width="360" height="326" rx="12" fill="#0e1522" stroke="#334155"/> | |
126 | + <text x="1200" y="350" text-anchor="middle" font-size="14" font-weight="700" fill="#f8fafc">Alerts & Push</text> | |
127 | + <rect x="1056" y="366" width="288" height="58" rx="8" fill="#141d2e" stroke="#fb7185"/> | |
128 | + <text x="1200" y="388" text-anchor="middle" font-size="12" fill="#fda4af">Rule Engine</text> | |
129 | + <text x="1200" y="406" text-anchor="middle" font-size="10.5" fill="#64748b">UNKNOWN×3 · phone ×3 · drowsy</text> | |
130 | + <rect x="1056" y="436" width="288" height="58" rx="8" fill="#141d2e" stroke="#f59e0b"/> | |
131 | + <text x="1200" y="458" text-anchor="middle" font-size="12" fill="#fcd34d">Alert Dispatcher</text> | |
132 | + <text x="1200" y="476" text-anchor="middle" font-size="10.5" fill="#64748b">WebSocket broadcast</text> | |
133 | + <rect x="1056" y="506" width="288" height="58" rx="8" fill="#141d2e" stroke="#818cf8"/> | |
134 | + <text x="1200" y="528" text-anchor="middle" font-size="12" fill="#c7d2fe">Push Providers</text> | |
135 | + <text x="1200" y="546" text-anchor="middle" font-size="10.5" fill="#64748b">Telegram bot · JSON webhook</text> | |
136 | + <rect x="1056" y="576" width="288" height="58" rx="8" fill="#141d2e" stroke="#a78bfa"/> | |
137 | + <text x="1200" y="598" text-anchor="middle" font-size="12" fill="#c4b5fd">GenAI Copilot</text> | |
138 | + <text x="1200" y="616" text-anchor="middle" font-size="10.5" fill="#64748b">natural-language · rule fallback</text> | |
139 | + </g> | |
140 | + | |
141 | + <!-- engines -> data --> | |
142 | + <path d="M 320 620 L 780 640" stroke="#64748b" stroke-width="2" marker-end="url(#arr)"/> | |
143 | + <!-- engines -> rules --> | |
144 | + <path d="M 600 560 L 1040 460" stroke="#64748b" stroke-width="2" marker-end="url(#arr)" stroke-dasharray="5 4"/> | |
145 | + | |
146 | + <!-- ================= ROW 3 : API ================= --> | |
147 | + <g> | |
148 | + <rect x="40" y="660" width="760" height="200" rx="12" fill="#0e1522" stroke="#334155"/> | |
149 | + <text x="60" y="688" font-size="14" font-weight="700" fill="#f8fafc">FastAPI Backend Layer</text> | |
150 | + <g font-size="11" fill="#94a3b8"> | |
151 | + <text x="60" y="712">• REST /api groups: personnel · analytics · alerts · assets</text> | |
152 | + <text x="60" y="730">• copilot · system · auth (JWT) · metrics</text> | |
153 | + <text x="60" y="748">• MJPEG video feeds + WebSocket event stream</text> | |
154 | + <text x="60" y="766">• Pydantic-typed request/response schemas</text> | |
155 | + </g> | |
156 | + <rect x="60" y="780" width="220" height="34" rx="6" fill="#101720" stroke="#22d3ee"/> | |
157 | + <text x="170" y="802" text-anchor="middle" font-size="11" fill="#67e8f9">REST /api/*</text> | |
158 | + <rect x="300" y="780" width="220" height="34" rx="6" fill="#101720" stroke="#34d399"/> | |
159 | + <text x="410" y="802" text-anchor="middle" font-size="11" fill="#6ee7b7">WS /api/stream/ws</text> | |
160 | + <rect x="540" y="780" width="240" height="34" rx="6" fill="#101720" stroke="#818cf8"/> | |
161 | + <text x="660" y="802" text-anchor="middle" font-size="11" fill="#c7d2fe">Auth · RBAC · health · logs</text> | |
162 | + </g> | |
163 | + | |
164 | + <!-- ================= ROW 3 : REACT ================= --> | |
165 | + <g> | |
166 | + <rect x="820" y="660" width="580" height="224" rx="12" fill="#0e1522" stroke="#334155"/> | |
167 | + <text x="1060" y="688" text-anchor="middle" font-size="14" font-weight="700" fill="#f8fafc">Web Command Center</text> | |
168 | + <rect x="844" y="704" width="152" height="44" rx="8" fill="#141d2e" stroke="#22d3ee"/> | |
169 | + <text x="920" y="724" text-anchor="middle" font-size="11" fill="#67e8f9">Live grid</text> | |
170 | + <text x="920" y="740" text-anchor="middle" font-size="10" fill="#475569">Multi-camera</text> | |
171 | + <rect x="1012" y="704" width="152" height="44" rx="8" fill="#141d2e" stroke="#34d399"/> | |
172 | + <text x="1088" y="724" text-anchor="middle" font-size="11" fill="#6ee7b7">Personnel</text> | |
173 | + <text x="1088" y="740" text-anchor="middle" font-size="10" fill="#475569">Roster · assign</text> | |
174 | + <rect x="1180" y="704" width="124" height="44" rx="8" fill="#141d2e" stroke="#f59e0b"/> | |
175 | + <text x="1242" y="724" text-anchor="middle" font-size="11" fill="#fcd34d">Analytics</text> | |
176 | + <text x="1242" y="740" text-anchor="middle" font-size="10" fill="#475569">Productivity</text> | |
177 | + <rect x="844" y="764" width="152" height="44" rx="8" fill="#141d2e" stroke="#fb7185"/> | |
178 | + <text x="920" y="784" text-anchor="middle" font-size="11" fill="#fda4af">Incidents</text> | |
179 | + <text x="920" y="800" text-anchor="middle" font-size="10" fill="#475569">Live alerts</text> | |
180 | + <rect x="1012" y="764" width="152" height="44" rx="8" fill="#141d2e" stroke="#a78bfa"/> | |
181 | + <text x="1088" y="784" text-anchor="middle" font-size="11" fill="#c4b5fd">Health</text> | |
182 | + <text x="1088" y="800" text-anchor="middle" font-size="10" fill="#475569">Sentinel</text> | |
183 | + <rect x="1180" y="764" width="124" height="44" rx="8" fill="#141d2e" stroke="#22d3ee"/> | |
184 | + <text x="1242" y="784" text-anchor="middle" font-size="11" fill="#67e8f9">Spatial</text> | |
185 | + <text x="1242" y="800" text-anchor="middle" font-size="10" fill="#475569">Assets</text> | |
186 | + <rect x="844" y="824" width="460" height="40" rx="8" fill="#141d2e" stroke="#818cf8"/> | |
187 | + <text x="1074" y="849" text-anchor="middle" font-size="11.5" fill="#c7d2fe">Copilot · attendance view · system</text> | |
188 | + <g font-size="10.5" fill="#64748b"> | |
189 | + <text x="844" y="886">5 s polling + WebSocket alert feed</text> | |
190 | + <text x="844" y="904">VITE_API_BASE env · no hardcoded hosts</text> | |
191 | + </g> | |
192 | + </g> | |
193 | + | |
194 | + <line x1="800" y1="780" x2="838" y2="780" stroke="#64748b" stroke-width="2" marker-end="url(#arr)"/> | |
195 | + | |
196 | + <!-- bottom legend --> | |
197 | + <g transform="translate(40,896)"> | |
198 | + <rect width="1360" height="3" rx="1.5" fill="url(#accA)" opacity="0.4"/> | |
199 | + <text x="720" y="18" text-anchor="middle" font-size="10.5" fill="#475569">FastAPI + ChromaDB + SQLite · OpenCV + insightface/ONNX · Tailwind · WebSocket · Telegram/Webhook notifications</text> | |
200 | + </g> | |
201 | +</svg> | |
0 | 202 | |
▼docs/diagrams/data-model.svg
+113-0
| @@ -0,0 +1,113 @@ | ||
1 | +<svg xmlns="http://www.w3.org/2000/svg" width="1260" height="720" viewBox="0 0 1260 720" font-family="'Segoe UI', system-ui, -apple-system, Arial, sans-serif"> | |
2 | + <defs> | |
3 | + <linearGradient id="bg" x1="0" y1="0" x2="0" y2="1"> | |
4 | + <stop offset="0" stop-color="#0b1019"/> | |
5 | + <stop offset="1" stop-color="#070b12"/> | |
6 | + </linearGradient> | |
7 | + <marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"> | |
8 | + <path d="M0 0 L10 5 L0 10 z" fill="#64748b"/> | |
9 | + </marker> | |
10 | + </defs> | |
11 | + | |
12 | + <rect width="1260" height="720" fill="url(#bg)"/> | |
13 | + <text x="630" y="40" text-anchor="middle" font-size="23" font-weight="700" fill="#f8fafc">Data Model</text> | |
14 | + <text x="630" y="64" text-anchor="middle" font-size="12.5" fill="#64748b">SQLite relational store + ChromaDB vector index — kept in sync on every enroll / delete</text> | |
15 | + | |
16 | + <!-- personnel --> | |
17 | + <g> | |
18 | + <rect x="40" y="110" width="300" height="210" rx="10" fill="#0e1522" stroke="#34d399"/> | |
19 | + <rect x="40" y="110" width="300" height="34" rx="10" fill="#0e1522" stroke="none"/> | |
20 | + <rect x="40" y="124" width="300" height="20" rx="0" fill="none"/> | |
21 | + <text x="190" y="136" text-anchor="middle" font-size="13" font-weight="700" fill="#6ee7b7">personnel</text> | |
22 | + <g font-size="11" fill="#cbd5e1" font-family="monospace"> | |
23 | + <text x="56" y="170">person_id <tspan fill="#64748b">PK</tspan></text> | |
24 | + <text x="56" y="190">name · role · employment_type</text> | |
25 | + <text x="56" y="210">department · email · phone</text> | |
26 | + <text x="56" y="230">enrolled_at TIMESTAMP</text> | |
27 | + <text x="56" y="250">metadata_json</text> | |
28 | + <text x="56" y="270">is_active BOOLEAN</text> | |
29 | + </g> | |
30 | + </g> | |
31 | + | |
32 | + <!-- attendance --> | |
33 | + <g> | |
34 | + <rect x="392" y="110" width="300" height="190" rx="10" fill="#0e1522" stroke="#22d3ee"/> | |
35 | + <text x="542" y="136" text-anchor="middle" font-size="13" font-weight="700" fill="#67e8f9">attendance_log</text> | |
36 | + <g font-size="11" fill="#cbd3e1" font-family="monospace"> | |
37 | + <text x="408" y="156">log_id PK</text> | |
38 | + <text x="408" y="176">person_id FK → personnel</text> | |
39 | + <text x="408" y="196">camera_id (gate)</text> | |
40 | + <text x="408" y="216">event_type: IN / OUT</text> | |
41 | + <text x="408" y="236">timestamp · 30 s cooldown</text> | |
42 | + <text x="408" y="256">confidence · liveness</text> | |
43 | + </g> | |
44 | + </g> | |
45 | + | |
46 | + <!-- productivity --> | |
47 | + <g> | |
48 | + <rect x="380" y="370" width="270" height="170" rx="10" fill="#0e1522" stroke="#f59e0b"/> | |
49 | + <text x="515" y="396" text-anchor="middle" font-size="13" font-weight="700" fill="#fcd34d">productivity_log</text> | |
50 | + <g font-size="11" fill="#cbd3e1" font-family="monospace"> | |
51 | + <text x="396" y="416">log_id PK · person_id</text> | |
52 | + <text x="396" y="436">camera_id · state: active</text> | |
53 | + <text x="396" y="456"> / idle_lazy / bunking</text> | |
54 | + <text x="396" y="476"> / phone_distraction</text> | |
55 | + <text x="396" y="496">timestamp · duration_s</text> | |
56 | + </g> | |
57 | + </g> | |
58 | + | |
59 | + <!-- health --> | |
60 | + <g> | |
61 | + <rect x="40" y="380" width="300" height="180" rx="10" fill="#0e1522" stroke="#fb7185"/> | |
62 | + <text x="190" y="406" text-anchor="middle" font-size="13" font-weight="700" fill="#fda4af">health_log</text> | |
63 | + <g font-size="11" fill="#cbd3e1" font-family="monospace"> | |
64 | + <text x="56" y="430">log_id PK · person_id</text> | |
65 | + <text x="56" y="450">eye_stdev (drowsiness)</text> | |
66 | + <text x="56" y="470">fatigue_score</text> | |
67 | + <text x="56" y="490">posture: GOOD/SLOUCH</text> | |
68 | + <text x="56" y="510">say_event: WARNING</text> | |
69 | + <text x="56" y="530">timestamp</text> | |
70 | + </g> | |
71 | + </g> | |
72 | + | |
73 | + <!-- assets --> | |
74 | + <g> | |
75 | + <rect x="712" y="110" width="300" height="190" rx="10" fill="#0e1522" stroke="#a78bfa"/> | |
76 | + <text x="862" y="136" text-anchor="middle" font-size="13" font-weight="700" fill="#c4b5fd">asset_log</text> | |
77 | + <g font-size="11" fill="#cbd3e1" font-family="monospace"> | |
78 | + <text x="728" y="156">asset_id PK · name</text> | |
79 | + <text x="728" y="176">owner person_id</text> | |
80 | + <text x="728" y="196">region bbox (zone)</text> | |
81 | + <text x="728" y="216">status: NORMAL /</text> | |
82 | + <text x="728" y="236"> DISPLACED_MISSING</text> | |
83 | + <text x="728" y="256">last_seen</text> | |
84 | + </g> | |
85 | + </g> | |
86 | + | |
87 | + <!-- incidents --> | |
88 | + <g> | |
89 | + <rect x="60" y="570" width="324" height="120" rx="10" fill="#0e1522" stroke="#fb7185"/> | |
90 | + <text x="222" y="594" text-anchor="middle" font-size="13" font-weight="700" fill="#fda4af">incident_alerts</text> | |
91 | + <g font-size="11" fill="#cbd3e1" font-family="monospace"> | |
92 | + <text x="78" y="616">alert_id PK · person_id / camera_id.</text> | |
93 | + <text x="78" y="636">type UNKNOWN_FACE · THEFT_SUSPECTED</text> | |
94 | + <text x="78" y="656">severity · message</text> | |
95 | + <text x="78" y="676">created_at · resolved_at</text> | |
96 | + </g> | |
97 | + </g> | |
98 | + | |
99 | + <!-- face embeddings (chroma) --> | |
100 | + <g transform="translate(740,600)"> | |
101 | + <rect width="480" height="98" rx="10" fill="#101720" stroke="#22d3ee"/> | |
102 | + <text x="16" y="24" font-size="13" font-weight="700" fill="#67e8f9">ChromaDB · face_embeddings (vector)</text> | |
103 | + <g font-size="11" fill="#cbd3e1" font-family="monospace"> | |
104 | + <text x="24" y="46">embedding_idPK e.g. "EMP01::photo.jpg"</text> | |
105 | + <text x="24" y="66">person_id → groups multiple photos</text> | |
106 | + <text x="24" y="86">vector[n] · cosine space · metadata</text> | |
107 | + </g> | |
108 | + </g> | |
109 | + | |
110 | + <!-- arrows --> | |
111 | + <line x1="340" y1="200" x2="398" y2="200" stroke="#64748b" stroke-width="2" marker-end="url(#arr)"/> | |
112 | + <line x1="500" y1="300" x2="500" y2="368" stroke="#64748b" stroke-width="2" marker-end="url(#arr)"/> | |
113 | +</svg> | |
0 | 114 | |
▼docs/diagrams/deployment.svg
+89-0
| @@ -0,0 +1,89 @@ | ||
1 | +<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="660" viewBox="0 0 1200 660" font-family="'Segoe UI', system-ui, -apple-system, Arial, sans-serif"> | |
2 | + <defs> | |
3 | + <linearGradient id="bg" x1="0" y1="0" x2="0" y2="1"> | |
4 | + <stop offset="0" stop-color="#0b1019"/> | |
5 | + <stop offset="1" stop-color="#070b12"/> | |
6 | + </linearGradient> | |
7 | + <marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"> | |
8 | + <path d="M0 0 L10 5 L0 10 z" fill="#64748b"/> | |
9 | + </marker> | |
10 | + <marker id="arrV" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"> | |
11 | + <path d="M0 0 L10 5 L0 10 z" fill="#a78bfa"/> | |
12 | + </marker> | |
13 | + </defs> | |
14 | + | |
15 | + <rect width="1200" height="660" fill="url(#bg)"/> | |
16 | + <text x="600" y="40" text-anchor="middle" font-size="23" font-weight="700" fill="#f8fafc">Deployment Topology</text> | |
17 | + <text x="600" y="64" text-anchor="middle" font-size="12.5" fill="#64748b">Runs as a single Docker Compose stack — one box is enough for a small office</text> | |
18 | + | |
19 | + <!-- Clients --> | |
20 | + <g> | |
21 | + <rect x="40" y="100" width="320" height="120" rx="12" fill="#0e1522" stroke="#334155"/> | |
22 | + <text x="60" y="128" font-size="13" font-weight="700" fill="#f8fafc">Clients</text> | |
23 | + <g font-size="11" fill="#94a3b8"> | |
24 | + <text x="60" y="152">Browser dashboard (Tailwind SPA)</text> | |
25 | + <text x="60" y="172">operator · admin · HR</text> | |
26 | + <text x="60" y="192">Telegram chat · JSON webhook</text> | |
27 | + </g> | |
28 | + </g> | |
29 | + <line x1="360" y1="160" x2="462" y2="160" stroke="#64748b" stroke-width="2" marker-end="url(#arr)"/> | |
30 | + <text x="411" y="152" text-anchor="middle" font-size="10" fill="#475569">HTTP / WS</text> | |
31 | + | |
32 | + <!-- Host --> | |
33 | + <g> | |
34 | + <rect x="466" y="78" width="330" height="500" rx="14" fill="#101720" stroke="#1e293b"/> | |
35 | + <rect x="466" y="78" width="330" height="36" rx="14" fill="#101720"/> | |
36 | + <text x="631" y="101" text-anchor="middle" font-size="13" font-weight="700" fill="#f8fafc">AegisVision host · single box</text> | |
37 | + | |
38 | + <rect x="488" y="128" width="286" height="54" rx="8" fill="#141d2e" stroke="#22d3ee"/> | |
39 | + <text x="506" y="148" font-size="11.5" fill="#67e8f9">NGINX · TLS reverse proxy</text> | |
40 | + <text x="506" y="166" font-size="10.5" fill="#64748b">static files + /api routing</text> | |
41 | + | |
42 | + <rect x="496" y="196" width="270" height="70" rx="8" fill="#141d2e" stroke="#818cf8"/> | |
43 | + <text x="514" y="218" font-size="11.5" font-weight="700" fill="#c7d2fe">uvicorn</text> | |
44 | + <text x="514" y="232" font-size="10.5" fill="#64748b">main worker + match workers</text> | |
45 | + <text x="514" y="248" font-size="10.5" fill="#64748b">REST + WS + MJPEG stream</text> | |
46 | + | |
47 | + <rect x="496" y="282" width="270" height="94" rx="8" fill="#141d2e" stroke="#34d399"/> | |
48 | + <text x="514" y="304" font-size="11.5" font-weight="700" fill="#6ee7b7">Persistent volumes</text> | |
49 | + <g font-size="10.5" fill="#94a3b8"> | |
50 | + <text x="514" y="324">data/surveillance.db (SQLite)</text> | |
51 | + <text x="514" y="340">data/vector_db (ChromaDB)</text> | |
52 | + <text x="514" y="356">data/known_persons (roster)</text> | |
53 | + <text x="514" y="372">data/logs (rotating)</text> | |
54 | + </g> | |
55 | + | |
56 | + <rect x="496" y="392" width="270" height="56" rx="8" fill="#141d2e" stroke="#a78bfa"/> | |
57 | + <text x="514" y="414" font-size="11.5" fill="#c4b5fd">ONNX models (~/.insightface)</text> | |
58 | + <text x="514" y="431" font-size="10.5" fill="#64748b">SCRFD detect + ArcFace embed</text> | |
59 | + </g> | |
60 | + | |
61 | + <!-- Edge --> | |
62 | + <g> | |
63 | + <rect x="850" y="78" width="310" height="190" rx="12" fill="#0e1522" stroke="#334155"/> | |
64 | + <text x="1005" y="106" text-anchor="middle" font-size="13" font-weight="700" fill="#f8fafc">Edge inputs</text> | |
65 | + <g font-size="10.5" fill="#94a3b8"> | |
66 | + <text x="870" y="132">RTSP IP cameras</text> | |
67 | + <text x="870" y="152">USB / MJPEG webcams</text> | |
68 | + <text x="870" y="172">hardware encoders</text> | |
69 | + </g> | |
70 | + <rect x="870" y="196" width="268" height="50" rx="8" fill="#141d2e" stroke="#22d3ee"/> | |
71 | + <text x="1004" y="218" text-anchor="middle" font-size="11" fill="#67e8f9">streams in</text> | |
72 | + <text x="1004" y="234" text-anchor="middle" font-size="10" fill="#475569">RTSP / MJPEG / HLS</text> | |
73 | + </g> | |
74 | + <path d="M850 160 L798 160" stroke="#64748b" stroke-width="2" marker-end="url(#arr)"/> | |
75 | + <text x="820" y="152" text-anchor="middle" font-size="10" fill="#475569">feed</text> | |
76 | + | |
77 | + <!-- External APIs --> | |
78 | + <g> | |
79 | + <rect x="850" y="330" width="310" height="150" rx="12" fill="#0e1522" stroke="#334155"/> | |
80 | + <text x="1005" y="358" text-anchor="middle" font-size="13" font-weight="700" fill="#f8fafc">External APIs</text> | |
81 | + <g font-size="11" fill="#94a3b8"> | |
82 | + <text x="870" y="384">Google Generative AI (Gemini)</text> | |
83 | + <text x="870" y="406">Telegram Bot API</text> | |
84 | + <text x="870" y="428">JSON push webhooks</text> | |
85 | + </g> | |
86 | + </g> | |
87 | + <path d="M631 578 L631 332" stroke="#a78bfa" stroke-width="2" marker-end="url(#arrV)"/> | |
88 | + <text x="641" y="470" font-size="10" fill="#7c3aed">copilot + push</text> | |
89 | +</svg> | |
0 | 90 | |
▼docs/diagrams/recognition-pipeline.svg
+125-0
| @@ -0,0 +1,125 @@ | ||
1 | +<svg xmlns="http://www.w3.org/2000/svg" width="1440" height="560" viewBox="0 0 1440 560" font-family="'Segoe UI', system-ui, -apple-system, Arial, sans-serif"> | |
2 | + <defs> | |
3 | + <linearGradient id="bg" x1="0" y1="0" x2="0" y2="1"> | |
4 | + <stop offset="0" stop-color="#0b1019"/> | |
5 | + <stop offset="1" stop-color="#070b12"/> | |
6 | + </linearGradient> | |
7 | + <marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"> | |
8 | + <path d="M0 0 L10 5 L0 10 z" fill="#64748b"/> | |
9 | + </marker> | |
10 | + </defs> | |
11 | + | |
12 | + <rect width="1440" height="560" fill="url(#bg)"/> | |
13 | + <text x="720" y="40" text-anchor="middle" font-size="23" font-weight="700" fill="#f8fafc">Face Recognition Pipeline</text> | |
14 | + <text x="720" y="64" text-anchor="middle" font-size="12.5" fill="#64748b">Frame in → identity + liveness out · UNKNOWN faces are tagged, never saved</text> | |
15 | + | |
16 | + <!-- stages --> | |
17 | + <g> | |
18 | + <!-- 1 capture --> | |
19 | + <rect x="40" y="120" width="196" height="150" rx="10" fill="#0e1522" stroke="#22d3ee"/> | |
20 | + <text x="138" y="146" text-anchor="middle" font-size="13" font-weight="700" fill="#67e8f9">1 · Capture</text> | |
21 | + <g font-size="10.5" fill="#94a3b8"> | |
22 | + <text x="54" y="170">decode RTSP / MJPEG / webcam</text> | |
23 | + <text x="54" y="188">latest-frame share</text> | |
24 | + <text x="54" y="234" fill="#64748b">↓ BGR frame</text> | |
25 | + </g> | |
26 | + | |
27 | + <!-- 2 detect --> | |
28 | + <rect x="296" y="120" width="196" height="150" rx="10" fill="#0e1522" stroke="#22d3ee"/> | |
29 | + <text x="394" y="146" text-anchor="middle" font-size="13" font-weight="700" fill="#67e8f9">2 · Detect Faces</text> | |
30 | + <g font-size="10.5" fill="#94a3b8"> | |
31 | + <text x="310" y="170">SCRFD-500M (ONNX)</text> | |
32 | + <text x="310" y="188">multi-scale · fast</text> | |
33 | + <text x="310" y="206">dlib-HOG fallback</text> | |
34 | + <text x="310" y="234" fill="#64748b">boxes + landmarks</text> | |
35 | + </g> | |
36 | + | |
37 | + <!-- 3 track --> | |
38 | + <rect x="552" y="120" width="196" height="150" rx="10" fill="#0e1522" stroke="#22d3ee"/> | |
39 | + <text x="650" y="146" text-anchor="middle" font-size="13" font-weight="700" fill="#67e8f9">3 · Track</text> | |
40 | + <g font-size="10.5" fill="#94a3b8"> | |
41 | + <text x="566" y="170">IoU frame-to-frame</text> | |
42 | + <text x="566" y="188">persistent per-person</text> | |
43 | + <text x="566" y="206">re-embed on timer or new</text> | |
44 | + <text x="566" y="234" fill="#67e8f9">stable identity over time</text> | |
45 | + </g> | |
46 | + | |
47 | + <!-- 4 anti-spoof --> | |
48 | + <rect x="808" y="120" width="196" height="150" rx="10" fill="#0e1522" stroke="#34d399"/> | |
49 | + <text x="906" y="146" text-anchor="middle" font-size="13" font-weight="700" fill="#6ee7b7">4 · Anti-Spoof</text> | |
50 | + <g font-size="10.5" fill="#94a3b8"> | |
51 | + <text x="822" y="170">liveness score from</text> | |
52 | + <text x="822" y="188">face texture / Laplacian</text> | |
53 | + <text x="822" y="206">CNN model roadmap</text> | |
54 | + <text x="822" y="234" fill="#34d399">live / spoof flag</text> | |
55 | + </g> | |
56 | + | |
57 | + <!-- 5 embed --> | |
58 | + <rect x="1064" y="120" width="196" height="150" rx="10" fill="#0e1522" stroke="#818cf8"/> | |
59 | + <text x="1162" y="146" text-anchor="middle" font-size="13" font-weight="700" fill="#c7d2fe">5 · Embed</text> | |
60 | + <g font-size="10.5" fill="#94a3b8"> | |
61 | + <text x="1078" y="170">ArcFace alignment</text> | |
62 | + <text x="1078" y="188">w600k_mbf (512-d)</text> | |
63 | + <text x="1078" y="206">dlib OpenFace (128-d)</text> | |
64 | + <text x="1078" y="234" fill="#818cf8">normalized vector</text> | |
65 | + </g> | |
66 | + | |
67 | + <!-- 6 match --> | |
68 | + <rect x="1308" y="120" width="96" height="150" rx="10" fill="#0e1522" stroke="#f59e0b"/> | |
69 | + <text x="1356" y="146" text-anchor="middle" font-size="12" font-weight="700" fill="#fcd34d">6 · Match</text> | |
70 | + <g font-size="10.5" fill="#94a3b8"> | |
71 | + <text x="1326" y="170">Chroma</text> | |
72 | + <text x="1326" y="188">top-N</text> | |
73 | + <text x="1326" y="206">cosine</text> | |
74 | + <text x="1326" y="234" fill="#f59e0b">≤ thr</text> | |
75 | + </g> | |
76 | + </g> | |
77 | + | |
78 | + <!-- arrows between stages --> | |
79 | + <line x1="236" y1="195" x2="294" y2="195" stroke="#64748b" stroke-width="2" marker-end="url(#arr)"/> | |
80 | + <line x1="492" y1="195" x2="550" y2="195" stroke="#64748b" stroke-width="2" marker-end="url(#arr)"/> | |
81 | + <line x1="748" y1="195" x2="806" y2="195" stroke="#64748b" stroke-width="2" marker-end="url(#arr)"/> | |
82 | + <line x1="1004" y1="195" x2="1062" y2="195" stroke="#64748b" stroke-width="2" marker-end="url(#arr)"/> | |
83 | + <line x1="260" y1="270" x2="1355" y2="270" stroke="#64748b" stroke-width="1" stroke-dasharray="4 4" opacity="0.5"/> | |
84 | + | |
85 | + <!-- decision lane --> | |
86 | + <g> | |
87 | + <rect x="40" y="300" width="1380" height="120" rx="12" fill="#0e1522" stroke="#334155"/> | |
88 | + <text x="60" y="328" font-size="13.5" font-weight="700" fill="#f8fafc">Decision & Label</text> | |
89 | + <rect x="60" y="344" width="320" height="58" rx="8" fill="#141d2e" stroke="#34d399"/> | |
90 | + <text x="80" y="366" font-size="11.5" fill="#6ee7b7">distance ≤ FACE_DISTANCE_THRESHOLD (0.60)</text> | |
91 | + <text x="80" y="384" font-size="10.5" fill="#64748b">KNOWN : name · confidence = (1 − d)×100</text> | |
92 | + <rect x="400" y="344" width="320" height="58" rx="8" fill="#141d2e" stroke="#f59e0b"/> | |
93 | + <text x="420" y="366" font-size="11.5" fill="#fcd34d">distance > threshold</text> | |
94 | + <text x="420" y="384" font-size="10.5" fill="#64748b">UNKNOWN : tagged on-frame, never stored</text> | |
95 | + <rect x="740" y="344" width="320" height="58" rx="8" fill="#141d2e" stroke="#fb7185"/> | |
96 | + <text x="760" y="366" font-size="11.5" fill="#fda4af">liveness < ANTI_SPOOF_THRESHOLD</text> | |
97 | + <text x="760" y="384" font-size="10.5" fill="#64748b">SPOOF SUSPECT : shown in red</text> | |
98 | + <rect x="1080" y="344" width="340" height="58" rx="8" fill="#141d2e" stroke="#818cf8"/> | |
99 | + <text x="1096" y="366" font-size="11.5" fill="#c7d2fe">matched KNOWN + live</text> | |
100 | + <text x="1096" y="384" font-size="10.5" fill="#64748b">gate in/out · occupancy · behavior logging</text> | |
101 | + </g> | |
102 | + | |
103 | + <!-- persistence row --> | |
104 | + <g> | |
105 | + <rect x="60" y="444" width="200" height="34" rx="6" fill="#101720" stroke="#22d3ee"/> | |
106 | + <text x="160" y="466" text-anchor="middle" font-size="11" fill="#67e8f9">Chroma: face_embeddings</text> | |
107 | + <rect x="280" y="444" width="200" height="34" rx="6" fill="#101720" stroke="#34d399"/> | |
108 | + <text x="380" y="466" text-anchor="middle" font-size="11" fill="#6ee7b7">SQLite: attendance_log</text> | |
109 | + <rect x="500" y="444" width="200" height="34" rx="6" fill="#101720" stroke="#f59e0b"/> | |
110 | + <text x="600" y="466" text-anchor="middle" font-size="11" fill="#fcd34d">productivity · health</text> | |
111 | + <rect x="720" y="444" width="220" height="34" rx="6" fill="#101720" stroke="#fb7185"/> | |
112 | + <text x="830" y="466" text-anchor="middle" font-size="11" fill="#fda4af">incident_alerts (UNKNOWN×…)</text> | |
113 | + <rect x="960" y="444" width="220" height="34" rx="6" fill="#101720" stroke="#a78bfa"/> | |
114 | + <text x="1070" y="466" text-anchor="middle" font-size="11" fill="#c4b5fd">spatial asset memory</text> | |
115 | + <rect x="1200" y="444" width="220" height="34" rx="6" fill="#101720" stroke="#818cf8"/> | |
116 | + <text x="1310" y="466" text-anchor="middle" font-size="11" fill="#c7d2fe">WS broadcast + push</text> | |
117 | + </g> | |
118 | + | |
119 | + <!-- key metrics --> | |
120 | + <g font-size="11" fill="#64748b"> | |
121 | + <rect x="40" y="500" width="1360" height="3" rx="1.5" fill="#1e293b"/> | |
122 | + <text x="720" y="522" text-anchor="middle" font-size="10.5">SCRFD detect → IoU track → liveness gate → ArcFace embed → top-N cosine match</text> | |
123 | + <text x="720" y="542" text-anchor="middle" font-size="10.5">Recognition backend switchable via FACE_BACKEND (auto / insightface / legacy)</text> | |
124 | + </g> | |
125 | +</svg> | |
0 | 126 | |
▼frontend/css/app.css
+30-0
| @@ -0,0 +1,30 @@ | ||
1 | +/* AegisVision console — custom styles layered on Tailwind CDN */ | |
2 | + | |
3 | +* { scrollbar-width: thin; scrollbar-color: rgba(148,163,184,0.25) transparent; } | |
4 | +::-webkit-scrollbar { width: 8px; height: 8px; } | |
5 | +::-webkit-scrollbar-thumb { background: rgba(148,163,184,0.22); border-radius: 8px; } | |
6 | +::-webkit-scrollbar-track { background: transparent; } | |
7 | + | |
8 | +body { font-family: 'Inter', ui-sans-serif, system-ui; } | |
9 | + | |
10 | +.glass-card { | |
11 | + background: rgba(14,21,34,0.6); | |
12 | + border: 1px solid rgba(255,255,255,0.06); | |
13 | + backdrop-filter: blur(8px); | |
14 | +} | |
15 | + | |
16 | +.nav-link { color: rgb(156 163 175); } | |
17 | +.nav-link:hover { background: rgba(255,255,255,0.05); color: rgb(229 231 235); } | |
18 | +.nav-link.active { background: linear-gradient(90deg, rgba(34,211,238,0.12), rgba(99,102,241,0.12)); color: #fff; border: 1px solid rgba(34,211,238,0.25); } | |
19 | +.nav-link.active .nav-icon { color: #22d3ee; } | |
20 | + | |
21 | +@keyframes toast-in { from { transform: translateY(12px); opacity: 0 } to { transform: translateY(0); opacity: 1 } } | |
22 | +.toast { animation: toast-in 0.2s ease-out; } | |
23 | + | |
24 | +@keyframes publish { 0% { box-shadow: 0 0 0 0 rgba(244,63,94,0.5) } 100% { box-shadow: 0 0 0 12px rgba(244,63,94,0) } } | |
25 | +.alert-pulse { animation: publish-pulse 1.2s ease-out infinite; } | |
26 | + | |
27 | +.dots-loading::after { | |
28 | + content: '…'; animation: dots 1.2s steps(4, end) infinite; | |
29 | +} | |
30 | +@keyframes dots { 0% { content: '' } 25% { content: '.' } 50% { content: '..' } 75% { content: '...' } } | |
0 | 31 | |
▼frontend/face_app/.eslintrc.cjs
+0-21
| @@ -1,21 +0,0 @@ | ||
1 | -module.exports = { | |
2 | - root: true, | |
3 | - env: { browser: true, es2020: true }, | |
4 | - extends: [ | |
5 | - 'eslint:recommended', | |
6 | - 'plugin:react/recommended', | |
7 | - 'plugin:react/jsx-runtime', | |
8 | - 'plugin:react-hooks/recommended', | |
9 | - ], | |
10 | - ignorePatterns: ['dist', '.eslintrc.cjs'], | |
11 | - parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, | |
12 | - settings: { react: { version: '18.2' } }, | |
13 | - plugins: ['react-refresh'], | |
14 | - rules: { | |
15 | - 'react/jsx-no-target-blank': 'off', | |
16 | - 'react-refresh/only-export-components': [ | |
17 | - 'warn', | |
18 | - { allowConstantExport: true }, | |
19 | - ], | |
20 | - }, | |
21 | -} | |
22 | 0 | |
▼frontend/face_app/.gitignore
+0-24
| @@ -1,24 +0,0 @@ | ||
1 | -# Logs | |
2 | -logs | |
3 | -*.log | |
4 | -npm-debug.log* | |
5 | -yarn-debug.log* | |
6 | -yarn-error.log* | |
7 | -pnpm-debug.log* | |
8 | -lerna-debug.log* | |
9 | -node_modules/ | |
10 | - | |
11 | -dist | |
12 | -dist-ssr | |
13 | -*.local | |
14 | - | |
15 | -# Editor directories and files | |
16 | -.vscode/* | |
17 | -!.vscode/extensions.json | |
18 | -.idea | |
19 | -.DS_Store | |
20 | -*.suo | |
21 | -*.ntvs* | |
22 | -*.njsproj | |
23 | -*.sln | |
24 | -*.sw? | |
25 | 0 | |
▼frontend/face_app/README.md
+0-8
| @@ -1,8 +0,0 @@ | ||
1 | -# React + Vite | |
2 | - | |
3 | -This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. | |
4 | - | |
5 | -Currently, two official plugins are available: | |
6 | - | |
7 | -- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh | |
8 | -- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh | |
9 | 0 | |
▼frontend/face_app/index.html
+0-13
| @@ -1,13 +0,0 @@ | ||
1 | -<!doctype html> | |
2 | -<html lang="en"> | |
3 | - <head> | |
4 | - <meta charset="UTF-8" /> | |
5 | - <link rel="icon" type="image/svg+xml" href="/face.svg" /> | |
6 | - <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
7 | - <title>FaceApp</title> | |
8 | - </head> | |
9 | - <body> | |
10 | - <div id="root"></div> | |
11 | - <script type="module" src="/src/main.jsx"></script> | |
12 | - </body> | |
13 | -</html> | |
14 | 0 | |
▼frontend/face_app/package-lock.json
+0-7702
| @@ -1,7702 +0,0 @@ | ||
1 | -{ | |
2 | - "name": "face_app", | |
3 | - "version": "0.0.0", | |
4 | - "lockfileVersion": 3, | |
5 | - "requires": true, | |
6 | - "packages": { | |
7 | - "": { | |
8 | - "name": "face_app", | |
9 | - "version": "0.0.0", | |
10 | - "dependencies": { | |
11 | - "@auth0/auth0-react": "^2.2.4", | |
12 | - "@emotion/react": "^11.11.4", | |
13 | - "@emotion/styled": "^11.11.5", | |
14 | - "@fontsource/roboto": "^5.0.13", | |
15 | - "@mui/icons-material": "^5.15.15", | |
16 | - "@mui/material": "^5.15.18", | |
17 | - "@mui/styled-engine-sc": "^6.0.0-alpha.18", | |
18 | - "@mui/x-data-grid": "^7.5.0", | |
19 | - "@mui/x-data-grid-generator": "^7.4.0", | |
20 | - "axios": "^1.6.8", | |
21 | - "mdb-react-ui-kit": "^8.0.0", | |
22 | - "react": "^18.2.0", | |
23 | - "react-dom": "^18.2.0", | |
24 | - "react-mui-sidebar": "^1.3.5", | |
25 | - "react-router-dom": "^6.23.0", | |
26 | - "styled-components": "^6.1.11" | |
27 | - }, | |
28 | - "devDependencies": { | |
29 | - "@types/react": "^18.2.66", | |
30 | - "@types/react-dom": "^18.2.22", | |
31 | - "@vitejs/plugin-react-swc": "^3.5.0", | |
32 | - "autoprefixer": "^10.4.19", | |
33 | - "eslint": "^8.57.0", | |
34 | - "eslint-plugin-react": "^7.34.1", | |
35 | - "eslint-plugin-react-hooks": "^4.6.0", | |
36 | - "eslint-plugin-react-refresh": "^0.4.6", | |
37 | - "postcss": "^8.4.38", | |
38 | - "tailwindcss": "^3.4.3", | |
39 | - "vite": "^5.2.0" | |
40 | - } | |
41 | - }, | |
42 | - "node_modules/@alloc/quick-lru": { | |
43 | - "version": "5.2.0", | |
44 | - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", | |
45 | - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", | |
46 | - "dev": true, | |
47 | - "engines": { | |
48 | - "node": ">=10" | |
49 | - }, | |
50 | - "funding": { | |
51 | - "url": "https://github.com/sponsors/sindresorhus" | |
52 | - } | |
53 | - }, | |
54 | - "node_modules/@auth0/auth0-react": { | |
55 | - "version": "2.2.4", | |
56 | - "resolved": "https://registry.npmjs.org/@auth0/auth0-react/-/auth0-react-2.2.4.tgz", | |
57 | - "integrity": "sha512-l29PQC0WdgkCoOc6WeMAY26gsy/yXJICW0jHfj0nz8rZZphYKrLNqTRWFFCMJY+sagza9tSgB1kG/UvQYgGh9A==", | |
58 | - "dependencies": { | |
59 | - "@auth0/auth0-spa-js": "^2.1.3" | |
60 | - }, | |
61 | - "peerDependencies": { | |
62 | - "react": "^16.11.0 || ^17 || ^18", | |
63 | - "react-dom": "^16.11.0 || ^17 || ^18" | |
64 | - } | |
65 | - }, | |
66 | - "node_modules/@auth0/auth0-spa-js": { | |
67 | - "version": "2.1.3", | |
68 | - "resolved": "https://registry.npmjs.org/@auth0/auth0-spa-js/-/auth0-spa-js-2.1.3.tgz", | |
69 | - "integrity": "sha512-NMTBNuuG4g3rame1aCnNS5qFYIzsTUV5qTFPRfTyYFS1feS6jsCBR+eTq9YkxCp1yuoM2UIcjunPaoPl77U9xQ==" | |
70 | - }, | |
71 | - "node_modules/@babel/code-frame": { | |
72 | - "version": "7.24.2", | |
73 | - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", | |
74 | - "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", | |
75 | - "dependencies": { | |
76 | - "@babel/highlight": "^7.24.2", | |
77 | - "picocolors": "^1.0.0" | |
78 | - }, | |
79 | - "engines": { | |
80 | - "node": ">=6.9.0" | |
81 | - } | |
82 | - }, | |
83 | - "node_modules/@babel/helper-module-imports": { | |
84 | - "version": "7.24.3", | |
85 | - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", | |
86 | - "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", | |
87 | - "dependencies": { | |
88 | - "@babel/types": "^7.24.0" | |
89 | - }, | |
90 | - "engines": { | |
91 | - "node": ">=6.9.0" | |
92 | - } | |
93 | - }, | |
94 | - "node_modules/@babel/helper-string-parser": { | |
95 | - "version": "7.24.1", | |
96 | - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", | |
97 | - "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", | |
98 | - "engines": { | |
99 | - "node": ">=6.9.0" | |
100 | - } | |
101 | - }, | |
102 | - "node_modules/@babel/helper-validator-identifier": { | |
103 | - "version": "7.24.5", | |
104 | - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", | |
105 | - "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", | |
106 | - "engines": { | |
107 | - "node": ">=6.9.0" | |
108 | - } | |
109 | - }, | |
110 | - "node_modules/@babel/highlight": { | |
111 | - "version": "7.24.5", | |
112 | - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", | |
113 | - "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", | |
114 | - "dependencies": { | |
115 | - "@babel/helper-validator-identifier": "^7.24.5", | |
116 | - "chalk": "^2.4.2", | |
117 | - "js-tokens": "^4.0.0", | |
118 | - "picocolors": "^1.0.0" | |
119 | - }, | |
120 | - "engines": { | |
121 | - "node": ">=6.9.0" | |
122 | - } | |
123 | - }, | |
124 | - "node_modules/@babel/highlight/node_modules/ansi-styles": { | |
125 | - "version": "3.2.1", | |
126 | - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", | |
127 | - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", | |
128 | - "dependencies": { | |
129 | - "color-convert": "^1.9.0" | |
130 | - }, | |
131 | - "engines": { | |
132 | - "node": ">=4" | |
133 | - } | |
134 | - }, | |
135 | - "node_modules/@babel/highlight/node_modules/chalk": { | |
136 | - "version": "2.4.2", | |
137 | - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", | |
138 | - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", | |
139 | - "dependencies": { | |
140 | - "ansi-styles": "^3.2.1", | |
141 | - "escape-string-regexp": "^1.0.5", | |
142 | - "supports-color": "^5.3.0" | |
143 | - }, | |
144 | - "engines": { | |
145 | - "node": ">=4" | |
146 | - } | |
147 | - }, | |
148 | - "node_modules/@babel/highlight/node_modules/color-convert": { | |
149 | - "version": "1.9.3", | |
150 | - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", | |
151 | - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", | |
152 | - "dependencies": { | |
153 | - "color-name": "1.1.3" | |
154 | - } | |
155 | - }, | |
156 | - "node_modules/@babel/highlight/node_modules/color-name": { | |
157 | - "version": "1.1.3", | |
158 | - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", | |
159 | - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" | |
160 | - }, | |
161 | - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { | |
162 | - "version": "1.0.5", | |
163 | - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", | |
164 | - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", | |
165 | - "engines": { | |
166 | - "node": ">=0.8.0" | |
167 | - } | |
168 | - }, | |
169 | - "node_modules/@babel/highlight/node_modules/has-flag": { | |
170 | - "version": "3.0.0", | |
171 | - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", | |
172 | - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", | |
173 | - "engines": { | |
174 | - "node": ">=4" | |
175 | - } | |
176 | - }, | |
177 | - "node_modules/@babel/highlight/node_modules/supports-color": { | |
178 | - "version": "5.5.0", | |
179 | - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", | |
180 | - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", | |
181 | - "dependencies": { | |
182 | - "has-flag": "^3.0.0" | |
183 | - }, | |
184 | - "engines": { | |
185 | - "node": ">=4" | |
186 | - } | |
187 | - }, | |
188 | - "node_modules/@babel/parser": { | |
189 | - "version": "7.24.5", | |
190 | - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", | |
191 | - "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", | |
192 | - "peer": true, | |
193 | - "bin": { | |
194 | - "parser": "bin/babel-parser.js" | |
195 | - }, | |
196 | - "engines": { | |
197 | - "node": ">=6.0.0" | |
198 | - } | |
199 | - }, | |
200 | - "node_modules/@babel/runtime": { | |
201 | - "version": "7.24.5", | |
202 | - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.5.tgz", | |
203 | - "integrity": "sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==", | |
204 | - "dependencies": { | |
205 | - "regenerator-runtime": "^0.14.0" | |
206 | - }, | |
207 | - "engines": { | |
208 | - "node": ">=6.9.0" | |
209 | - } | |
210 | - }, | |
211 | - "node_modules/@babel/types": { | |
212 | - "version": "7.24.5", | |
213 | - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", | |
214 | - "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", | |
215 | - "dependencies": { | |
216 | - "@babel/helper-string-parser": "^7.24.1", | |
217 | - "@babel/helper-validator-identifier": "^7.24.5", | |
218 | - "to-fast-properties": "^2.0.0" | |
219 | - }, | |
220 | - "engines": { | |
221 | - "node": ">=6.9.0" | |
222 | - } | |
223 | - }, | |
224 | - "node_modules/@emotion/babel-plugin": { | |
225 | - "version": "11.11.0", | |
226 | - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", | |
227 | - "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", | |
228 | - "dependencies": { | |
229 | - "@babel/helper-module-imports": "^7.16.7", | |
230 | - "@babel/runtime": "^7.18.3", | |
231 | - "@emotion/hash": "^0.9.1", | |
232 | - "@emotion/memoize": "^0.8.1", | |
233 | - "@emotion/serialize": "^1.1.2", | |
234 | - "babel-plugin-macros": "^3.1.0", | |
235 | - "convert-source-map": "^1.5.0", | |
236 | - "escape-string-regexp": "^4.0.0", | |
237 | - "find-root": "^1.1.0", | |
238 | - "source-map": "^0.5.7", | |
239 | - "stylis": "4.2.0" | |
240 | - } | |
241 | - }, | |
242 | - "node_modules/@emotion/babel-plugin/node_modules/source-map": { | |
243 | - "version": "0.5.7", | |
244 | - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", | |
245 | - "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", | |
246 | - "engines": { | |
247 | - "node": ">=0.10.0" | |
248 | - } | |
249 | - }, | |
250 | - "node_modules/@emotion/cache": { | |
251 | - "version": "11.11.0", | |
252 | - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", | |
253 | - "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", | |
254 | - "dependencies": { | |
255 | - "@emotion/memoize": "^0.8.1", | |
256 | - "@emotion/sheet": "^1.2.2", | |
257 | - "@emotion/utils": "^1.2.1", | |
258 | - "@emotion/weak-memoize": "^0.3.1", | |
259 | - "stylis": "4.2.0" | |
260 | - } | |
261 | - }, | |
262 | - "node_modules/@emotion/hash": { | |
263 | - "version": "0.9.1", | |
264 | - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", | |
265 | - "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" | |
266 | - }, | |
267 | - "node_modules/@emotion/is-prop-valid": { | |
268 | - "version": "1.2.2", | |
269 | - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", | |
270 | - "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", | |
271 | - "dependencies": { | |
272 | - "@emotion/memoize": "^0.8.1" | |
273 | - } | |
274 | - }, | |
275 | - "node_modules/@emotion/memoize": { | |
276 | - "version": "0.8.1", | |
277 | - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", | |
278 | - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" | |
279 | - }, | |
280 | - "node_modules/@emotion/react": { | |
281 | - "version": "11.11.4", | |
282 | - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz", | |
283 | - "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==", | |
284 | - "dependencies": { | |
285 | - "@babel/runtime": "^7.18.3", | |
286 | - "@emotion/babel-plugin": "^11.11.0", | |
287 | - "@emotion/cache": "^11.11.0", | |
288 | - "@emotion/serialize": "^1.1.3", | |
289 | - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", | |
290 | - "@emotion/utils": "^1.2.1", | |
291 | - "@emotion/weak-memoize": "^0.3.1", | |
292 | - "hoist-non-react-statics": "^3.3.1" | |
293 | - }, | |
294 | - "peerDependencies": { | |
295 | - "react": ">=16.8.0" | |
296 | - }, | |
297 | - "peerDependenciesMeta": { | |
298 | - "@types/react": { | |
299 | - "optional": true | |
300 | - } | |
301 | - } | |
302 | - }, | |
303 | - "node_modules/@emotion/serialize": { | |
304 | - "version": "1.1.4", | |
305 | - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz", | |
306 | - "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==", | |
307 | - "dependencies": { | |
308 | - "@emotion/hash": "^0.9.1", | |
309 | - "@emotion/memoize": "^0.8.1", | |
310 | - "@emotion/unitless": "^0.8.1", | |
311 | - "@emotion/utils": "^1.2.1", | |
312 | - "csstype": "^3.0.2" | |
313 | - } | |
314 | - }, | |
315 | - "node_modules/@emotion/sheet": { | |
316 | - "version": "1.2.2", | |
317 | - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", | |
318 | - "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" | |
319 | - }, | |
320 | - "node_modules/@emotion/styled": { | |
321 | - "version": "11.11.5", | |
322 | - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz", | |
323 | - "integrity": "sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==", | |
324 | - "dependencies": { | |
325 | - "@babel/runtime": "^7.18.3", | |
326 | - "@emotion/babel-plugin": "^11.11.0", | |
327 | - "@emotion/is-prop-valid": "^1.2.2", | |
328 | - "@emotion/serialize": "^1.1.4", | |
329 | - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", | |
330 | - "@emotion/utils": "^1.2.1" | |
331 | - }, | |
332 | - "peerDependencies": { | |
333 | - "@emotion/react": "^11.0.0-rc.0", | |
334 | - "react": ">=16.8.0" | |
335 | - }, | |
336 | - "peerDependenciesMeta": { | |
337 | - "@types/react": { | |
338 | - "optional": true | |
339 | - } | |
340 | - } | |
341 | - }, | |
342 | - "node_modules/@emotion/unitless": { | |
343 | - "version": "0.8.1", | |
344 | - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", | |
345 | - "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" | |
346 | - }, | |
347 | - "node_modules/@emotion/use-insertion-effect-with-fallbacks": { | |
348 | - "version": "1.0.1", | |
349 | - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", | |
350 | - "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", | |
351 | - "peerDependencies": { | |
352 | - "react": ">=16.8.0" | |
353 | - } | |
354 | - }, | |
355 | - "node_modules/@emotion/utils": { | |
356 | - "version": "1.2.1", | |
357 | - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", | |
358 | - "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" | |
359 | - }, | |
360 | - "node_modules/@emotion/weak-memoize": { | |
361 | - "version": "0.3.1", | |
362 | - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", | |
363 | - "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" | |
364 | - }, | |
365 | - "node_modules/@esbuild/aix-ppc64": { | |
366 | - "version": "0.20.2", | |
367 | - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", | |
368 | - "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", | |
369 | - "cpu": [ | |
370 | - "ppc64" | |
371 | - ], | |
372 | - "optional": true, | |
373 | - "os": [ | |
374 | - "aix" | |
375 | - ], | |
376 | - "engines": { | |
377 | - "node": ">=12" | |
378 | - } | |
379 | - }, | |
380 | - "node_modules/@esbuild/android-arm": { | |
381 | - "version": "0.20.2", | |
382 | - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", | |
383 | - "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", | |
384 | - "cpu": [ | |
385 | - "arm" | |
386 | - ], | |
387 | - "optional": true, | |
388 | - "os": [ | |
389 | - "android" | |
390 | - ], | |
391 | - "engines": { | |
392 | - "node": ">=12" | |
393 | - } | |
394 | - }, | |
395 | - "node_modules/@esbuild/android-arm64": { | |
396 | - "version": "0.20.2", | |
397 | - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", | |
398 | - "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", | |
399 | - "cpu": [ | |
400 | - "arm64" | |
401 | - ], | |
402 | - "optional": true, | |
403 | - "os": [ | |
404 | - "android" | |
405 | - ], | |
406 | - "engines": { | |
407 | - "node": ">=12" | |
408 | - } | |
409 | - }, | |
410 | - "node_modules/@esbuild/android-x64": { | |
411 | - "version": "0.20.2", | |
412 | - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", | |
413 | - "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", | |
414 | - "cpu": [ | |
415 | - "x64" | |
416 | - ], | |
417 | - "optional": true, | |
418 | - "os": [ | |
419 | - "android" | |
420 | - ], | |
421 | - "engines": { | |
422 | - "node": ">=12" | |
423 | - } | |
424 | - }, | |
425 | - "node_modules/@esbuild/darwin-arm64": { | |
426 | - "version": "0.20.2", | |
427 | - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", | |
428 | - "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", | |
429 | - "cpu": [ | |
430 | - "arm64" | |
431 | - ], | |
432 | - "optional": true, | |
433 | - "os": [ | |
434 | - "darwin" | |
435 | - ], | |
436 | - "engines": { | |
437 | - "node": ">=12" | |
438 | - } | |
439 | - }, | |
440 | - "node_modules/@esbuild/darwin-x64": { | |
441 | - "version": "0.20.2", | |
442 | - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", | |
443 | - "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", | |
444 | - "cpu": [ | |
445 | - "x64" | |
446 | - ], | |
447 | - "optional": true, | |
448 | - "os": [ | |
449 | - "darwin" | |
450 | - ], | |
451 | - "engines": { | |
452 | - "node": ">=12" | |
453 | - } | |
454 | - }, | |
455 | - "node_modules/@esbuild/freebsd-arm64": { | |
456 | - "version": "0.20.2", | |
457 | - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", | |
458 | - "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", | |
459 | - "cpu": [ | |
460 | - "arm64" | |
461 | - ], | |
462 | - "optional": true, | |
463 | - "os": [ | |
464 | - "freebsd" | |
465 | - ], | |
466 | - "engines": { | |
467 | - "node": ">=12" | |
468 | - } | |
469 | - }, | |
470 | - "node_modules/@esbuild/freebsd-x64": { | |
471 | - "version": "0.20.2", | |
472 | - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", | |
473 | - "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", | |
474 | - "cpu": [ | |
475 | - "x64" | |
476 | - ], | |
477 | - "optional": true, | |
478 | - "os": [ | |
479 | - "freebsd" | |
480 | - ], | |
481 | - "engines": { | |
482 | - "node": ">=12" | |
483 | - } | |
484 | - }, | |
485 | - "node_modules/@esbuild/linux-arm": { | |
486 | - "version": "0.20.2", | |
487 | - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", | |
488 | - "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", | |
489 | - "cpu": [ | |
490 | - "arm" | |
491 | - ], | |
492 | - "optional": true, | |
493 | - "os": [ | |
494 | - "linux" | |
495 | - ], | |
496 | - "engines": { | |
497 | - "node": ">=12" | |
498 | - } | |
499 | - }, | |
500 | - "node_modules/@esbuild/linux-arm64": { | |
501 | - "version": "0.20.2", | |
502 | - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", | |
503 | - "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", | |
504 | - "cpu": [ | |
505 | - "arm64" | |
506 | - ], | |
507 | - "optional": true, | |
508 | - "os": [ | |
509 | - "linux" | |
510 | - ], | |
511 | - "engines": { | |
512 | - "node": ">=12" | |
513 | - } | |
514 | - }, | |
515 | - "node_modules/@esbuild/linux-ia32": { | |
516 | - "version": "0.20.2", | |
517 | - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", | |
518 | - "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", | |
519 | - "cpu": [ | |
520 | - "ia32" | |
521 | - ], | |
522 | - "optional": true, | |
523 | - "os": [ | |
524 | - "linux" | |
525 | - ], | |
526 | - "engines": { | |
527 | - "node": ">=12" | |
528 | - } | |
529 | - }, | |
530 | - "node_modules/@esbuild/linux-loong64": { | |
531 | - "version": "0.20.2", | |
532 | - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", | |
533 | - "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", | |
534 | - "cpu": [ | |
535 | - "loong64" | |
536 | - ], | |
537 | - "optional": true, | |
538 | - "os": [ | |
539 | - "linux" | |
540 | - ], | |
541 | - "engines": { | |
542 | - "node": ">=12" | |
543 | - } | |
544 | - }, | |
545 | - "node_modules/@esbuild/linux-mips64el": { | |
546 | - "version": "0.20.2", | |
547 | - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", | |
548 | - "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", | |
549 | - "cpu": [ | |
550 | - "mips64el" | |
551 | - ], | |
552 | - "optional": true, | |
553 | - "os": [ | |
554 | - "linux" | |
555 | - ], | |
556 | - "engines": { | |
557 | - "node": ">=12" | |
558 | - } | |
559 | - }, | |
560 | - "node_modules/@esbuild/linux-ppc64": { | |
561 | - "version": "0.20.2", | |
562 | - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", | |
563 | - "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", | |
564 | - "cpu": [ | |
565 | - "ppc64" | |
566 | - ], | |
567 | - "optional": true, | |
568 | - "os": [ | |
569 | - "linux" | |
570 | - ], | |
571 | - "engines": { | |
572 | - "node": ">=12" | |
573 | - } | |
574 | - }, | |
575 | - "node_modules/@esbuild/linux-riscv64": { | |
576 | - "version": "0.20.2", | |
577 | - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", | |
578 | - "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", | |
579 | - "cpu": [ | |
580 | - "riscv64" | |
581 | - ], | |
582 | - "optional": true, | |
583 | - "os": [ | |
584 | - "linux" | |
585 | - ], | |
586 | - "engines": { | |
587 | - "node": ">=12" | |
588 | - } | |
589 | - }, | |
590 | - "node_modules/@esbuild/linux-s390x": { | |
591 | - "version": "0.20.2", | |
592 | - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", | |
593 | - "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", | |
594 | - "cpu": [ | |
595 | - "s390x" | |
596 | - ], | |
597 | - "optional": true, | |
598 | - "os": [ | |
599 | - "linux" | |
600 | - ], | |
601 | - "engines": { | |
602 | - "node": ">=12" | |
603 | - } | |
604 | - }, | |
605 | - "node_modules/@esbuild/linux-x64": { | |
606 | - "version": "0.20.2", | |
607 | - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", | |
608 | - "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", | |
609 | - "cpu": [ | |
610 | - "x64" | |
611 | - ], | |
612 | - "optional": true, | |
613 | - "os": [ | |
614 | - "linux" | |
615 | - ], | |
616 | - "engines": { | |
617 | - "node": ">=12" | |
618 | - } | |
619 | - }, | |
620 | - "node_modules/@esbuild/netbsd-x64": { | |
621 | - "version": "0.20.2", | |
622 | - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", | |
623 | - "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", | |
624 | - "cpu": [ | |
625 | - "x64" | |
626 | - ], | |
627 | - "optional": true, | |
628 | - "os": [ | |
629 | - "netbsd" | |
630 | - ], | |
631 | - "engines": { | |
632 | - "node": ">=12" | |
633 | - } | |
634 | - }, | |
635 | - "node_modules/@esbuild/openbsd-x64": { | |
636 | - "version": "0.20.2", | |
637 | - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", | |
638 | - "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", | |
639 | - "cpu": [ | |
640 | - "x64" | |
641 | - ], | |
642 | - "optional": true, | |
643 | - "os": [ | |
644 | - "openbsd" | |
645 | - ], | |
646 | - "engines": { | |
647 | - "node": ">=12" | |
648 | - } | |
649 | - }, | |
650 | - "node_modules/@esbuild/sunos-x64": { | |
651 | - "version": "0.20.2", | |
652 | - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", | |
653 | - "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", | |
654 | - "cpu": [ | |
655 | - "x64" | |
656 | - ], | |
657 | - "optional": true, | |
658 | - "os": [ | |
659 | - "sunos" | |
660 | - ], | |
661 | - "engines": { | |
662 | - "node": ">=12" | |
663 | - } | |
664 | - }, | |
665 | - "node_modules/@esbuild/win32-arm64": { | |
666 | - "version": "0.20.2", | |
667 | - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", | |
668 | - "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", | |
669 | - "cpu": [ | |
670 | - "arm64" | |
671 | - ], | |
672 | - "optional": true, | |
673 | - "os": [ | |
674 | - "win32" | |
675 | - ], | |
676 | - "engines": { | |
677 | - "node": ">=12" | |
678 | - } | |
679 | - }, | |
680 | - "node_modules/@esbuild/win32-ia32": { | |
681 | - "version": "0.20.2", | |
682 | - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", | |
683 | - "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", | |
684 | - "cpu": [ | |
685 | - "ia32" | |
686 | - ], | |
687 | - "optional": true, | |
688 | - "os": [ | |
689 | - "win32" | |
690 | - ], | |
691 | - "engines": { | |
692 | - "node": ">=12" | |
693 | - } | |
694 | - }, | |
695 | - "node_modules/@esbuild/win32-x64": { | |
696 | - "version": "0.20.2", | |
697 | - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", | |
698 | - "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", | |
699 | - "cpu": [ | |
700 | - "x64" | |
701 | - ], | |
702 | - "optional": true, | |
703 | - "os": [ | |
704 | - "win32" | |
705 | - ], | |
706 | - "engines": { | |
707 | - "node": ">=12" | |
708 | - } | |
709 | - }, | |
710 | - "node_modules/@eslint-community/eslint-utils": { | |
711 | - "version": "4.4.0", | |
712 | - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", | |
713 | - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", | |
714 | - "dev": true, | |
715 | - "dependencies": { | |
716 | - "eslint-visitor-keys": "^3.3.0" | |
717 | - }, | |
718 | - "engines": { | |
719 | - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" | |
720 | - }, | |
721 | - "peerDependencies": { | |
722 | - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" | |
723 | - } | |
724 | - }, | |
725 | - "node_modules/@eslint-community/regexpp": { | |
726 | - "version": "4.10.0", | |
727 | - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", | |
728 | - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", | |
729 | - "dev": true, | |
730 | - "engines": { | |
731 | - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" | |
732 | - } | |
733 | - }, | |
734 | - "node_modules/@eslint/eslintrc": { | |
735 | - "version": "2.1.4", | |
736 | - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", | |
737 | - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", | |
738 | - "dev": true, | |
739 | - "dependencies": { | |
740 | - "ajv": "^6.12.4", | |
741 | - "debug": "^4.3.2", | |
742 | - "espree": "^9.6.0", | |
743 | - "globals": "^13.19.0", | |
744 | - "ignore": "^5.2.0", | |
745 | - "import-fresh": "^3.2.1", | |
746 | - "js-yaml": "^4.1.0", | |
747 | - "minimatch": "^3.1.2", | |
748 | - "strip-json-comments": "^3.1.1" | |
749 | - }, | |
750 | - "engines": { | |
751 | - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" | |
752 | - }, | |
753 | - "funding": { | |
754 | - "url": "https://opencollective.com/eslint" | |
755 | - } | |
756 | - }, | |
757 | - "node_modules/@eslint/js": { | |
758 | - "version": "8.57.0", | |
759 | - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", | |
760 | - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", | |
761 | - "dev": true, | |
762 | - "engines": { | |
763 | - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" | |
764 | - } | |
765 | - }, | |
766 | - "node_modules/@fast-csv/format": { | |
767 | - "version": "4.3.5", | |
768 | - "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz", | |
769 | - "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==", | |
770 | - "dependencies": { | |
771 | - "@types/node": "^14.0.1", | |
772 | - "lodash.escaperegexp": "^4.1.2", | |
773 | - "lodash.isboolean": "^3.0.3", | |
774 | - "lodash.isequal": "^4.5.0", | |
775 | - "lodash.isfunction": "^3.0.9", | |
776 | - "lodash.isnil": "^4.0.0" | |
777 | - } | |
778 | - }, | |
779 | - "node_modules/@fast-csv/format/node_modules/@types/node": { | |
780 | - "version": "14.18.63", | |
781 | - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", | |
782 | - "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==" | |
783 | - }, | |
784 | - "node_modules/@fast-csv/parse": { | |
785 | - "version": "4.3.6", | |
786 | - "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz", | |
787 | - "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==", | |
788 | - "dependencies": { | |
789 | - "@types/node": "^14.0.1", | |
790 | - "lodash.escaperegexp": "^4.1.2", | |
791 | - "lodash.groupby": "^4.6.0", | |
792 | - "lodash.isfunction": "^3.0.9", | |
793 | - "lodash.isnil": "^4.0.0", | |
794 | - "lodash.isundefined": "^3.0.1", | |
795 | - "lodash.uniq": "^4.5.0" | |
796 | - } | |
797 | - }, | |
798 | - "node_modules/@fast-csv/parse/node_modules/@types/node": { | |
799 | - "version": "14.18.63", | |
800 | - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz", | |
801 | - "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==" | |
802 | - }, | |
803 | - "node_modules/@floating-ui/core": { | |
804 | - "version": "1.6.1", | |
805 | - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.1.tgz", | |
806 | - "integrity": "sha512-42UH54oPZHPdRHdw6BgoBD6cg/eVTmVrFcgeRDM3jbO7uxSoipVcmcIGFcA5jmOHO5apcyvBhkSKES3fQJnu7A==", | |
807 | - "dependencies": { | |
808 | - "@floating-ui/utils": "^0.2.0" | |
809 | - } | |
810 | - }, | |
811 | - "node_modules/@floating-ui/dom": { | |
812 | - "version": "1.6.4", | |
813 | - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.4.tgz", | |
814 | - "integrity": "sha512-0G8R+zOvQsAG1pg2Q99P21jiqxqGBW1iRe/iXHsBRBxnpXKFI8QwbB4x5KmYLggNO5m34IQgOIu9SCRfR/WWiQ==", | |
815 | - "dependencies": { | |
816 | - "@floating-ui/core": "^1.0.0", | |
817 | - "@floating-ui/utils": "^0.2.0" | |
818 | - } | |
819 | - }, | |
820 | - "node_modules/@floating-ui/react-dom": { | |
821 | - "version": "2.0.9", | |
822 | - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.9.tgz", | |
823 | - "integrity": "sha512-q0umO0+LQK4+p6aGyvzASqKbKOJcAHJ7ycE9CuUvfx3s9zTHWmGJTPOIlM/hmSBfUfg/XfY5YhLBLR/LHwShQQ==", | |
824 | - "dependencies": { | |
825 | - "@floating-ui/dom": "^1.0.0" | |
826 | - }, | |
827 | - "peerDependencies": { | |
828 | - "react": ">=16.8.0", | |
829 | - "react-dom": ">=16.8.0" | |
830 | - } | |
831 | - }, | |
832 | - "node_modules/@floating-ui/utils": { | |
833 | - "version": "0.2.2", | |
834 | - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz", | |
835 | - "integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==" | |
836 | - }, | |
837 | - "node_modules/@fontsource/roboto": { | |
838 | - "version": "5.0.13", | |
839 | - "resolved": "https://registry.npmjs.org/@fontsource/roboto/-/roboto-5.0.13.tgz", | |
840 | - "integrity": "sha512-j61DHjsdUCKMXSdNLTOxcG701FWnF0jcqNNQi2iPCDxU8seN/sMxeh62dC++UiagCWq9ghTypX+Pcy7kX+QOeQ==" | |
841 | - }, | |
842 | - "node_modules/@humanwhocodes/config-array": { | |
843 | - "version": "0.11.14", | |
844 | - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", | |
845 | - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", | |
846 | - "dev": true, | |
847 | - "dependencies": { | |
848 | - "@humanwhocodes/object-schema": "^2.0.2", | |
849 | - "debug": "^4.3.1", | |
850 | - "minimatch": "^3.0.5" | |
851 | - }, | |
852 | - "engines": { | |
853 | - "node": ">=10.10.0" | |
854 | - } | |
855 | - }, | |
856 | - "node_modules/@humanwhocodes/module-importer": { | |
857 | - "version": "1.0.1", | |
858 | - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", | |
859 | - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", | |
860 | - "dev": true, | |
861 | - "engines": { | |
862 | - "node": ">=12.22" | |
863 | - }, | |
864 | - "funding": { | |
865 | - "type": "github", | |
866 | - "url": "https://github.com/sponsors/nzakas" | |
867 | - } | |
868 | - }, | |
869 | - "node_modules/@humanwhocodes/object-schema": { | |
870 | - "version": "2.0.3", | |
871 | - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", | |
872 | - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", | |
873 | - "dev": true | |
874 | - }, | |
875 | - "node_modules/@isaacs/cliui": { | |
876 | - "version": "8.0.2", | |
877 | - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", | |
878 | - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", | |
879 | - "dev": true, | |
880 | - "dependencies": { | |
881 | - "string-width": "^5.1.2", | |
882 | - "string-width-cjs": "npm:string-width@^4.2.0", | |
883 | - "strip-ansi": "^7.0.1", | |
884 | - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", | |
885 | - "wrap-ansi": "^8.1.0", | |
886 | - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" | |
887 | - }, | |
888 | - "engines": { | |
889 | - "node": ">=12" | |
890 | - } | |
891 | - }, | |
892 | - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { | |
893 | - "version": "6.0.1", | |
894 | - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", | |
895 | - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", | |
896 | - "dev": true, | |
897 | - "engines": { | |
898 | - "node": ">=12" | |
899 | - }, | |
900 | - "funding": { | |
901 | - "url": "https://github.com/chalk/ansi-regex?sponsor=1" | |
902 | - } | |
903 | - }, | |
904 | - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { | |
905 | - "version": "7.1.0", | |
906 | - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", | |
907 | - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", | |
908 | - "dev": true, | |
909 | - "dependencies": { | |
910 | - "ansi-regex": "^6.0.1" | |
911 | - }, | |
912 | - "engines": { | |
913 | - "node": ">=12" | |
914 | - }, | |
915 | - "funding": { | |
916 | - "url": "https://github.com/chalk/strip-ansi?sponsor=1" | |
917 | - } | |
918 | - }, | |
919 | - "node_modules/@jridgewell/gen-mapping": { | |
920 | - "version": "0.3.5", | |
921 | - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", | |
922 | - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", | |
923 | - "dev": true, | |
924 | - "dependencies": { | |
925 | - "@jridgewell/set-array": "^1.2.1", | |
926 | - "@jridgewell/sourcemap-codec": "^1.4.10", | |
927 | - "@jridgewell/trace-mapping": "^0.3.24" | |
928 | - }, | |
929 | - "engines": { | |
930 | - "node": ">=6.0.0" | |
931 | - } | |
932 | - }, | |
933 | - "node_modules/@jridgewell/resolve-uri": { | |
934 | - "version": "3.1.2", | |
935 | - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", | |
936 | - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", | |
937 | - "dev": true, | |
938 | - "engines": { | |
939 | - "node": ">=6.0.0" | |
940 | - } | |
941 | - }, | |
942 | - "node_modules/@jridgewell/set-array": { | |
943 | - "version": "1.2.1", | |
944 | - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", | |
945 | - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", | |
946 | - "dev": true, | |
947 | - "engines": { | |
948 | - "node": ">=6.0.0" | |
949 | - } | |
950 | - }, | |
951 | - "node_modules/@jridgewell/sourcemap-codec": { | |
952 | - "version": "1.4.15", | |
953 | - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", | |
954 | - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" | |
955 | - }, | |
956 | - "node_modules/@jridgewell/trace-mapping": { | |
957 | - "version": "0.3.25", | |
958 | - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", | |
959 | - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", | |
960 | - "dev": true, | |
961 | - "dependencies": { | |
962 | - "@jridgewell/resolve-uri": "^3.1.0", | |
963 | - "@jridgewell/sourcemap-codec": "^1.4.14" | |
964 | - } | |
965 | - }, | |
966 | - "node_modules/@microsoft/api-extractor": { | |
967 | - "version": "7.43.0", | |
968 | - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.43.0.tgz", | |
969 | - "integrity": "sha512-GFhTcJpB+MI6FhvXEI9b2K0snulNLWHqC/BbcJtyNYcKUiw7l3Lgis5ApsYncJ0leALX7/of4XfmXk+maT111w==", | |
970 | - "peer": true, | |
971 | - "dependencies": { | |
972 | - "@microsoft/api-extractor-model": "7.28.13", | |
973 | - "@microsoft/tsdoc": "0.14.2", | |
974 | - "@microsoft/tsdoc-config": "~0.16.1", | |
975 | - "@rushstack/node-core-library": "4.0.2", | |
976 | - "@rushstack/rig-package": "0.5.2", | |
977 | - "@rushstack/terminal": "0.10.0", | |
978 | - "@rushstack/ts-command-line": "4.19.1", | |
979 | - "lodash": "~4.17.15", | |
980 | - "minimatch": "~3.0.3", | |
981 | - "resolve": "~1.22.1", | |
982 | - "semver": "~7.5.4", | |
983 | - "source-map": "~0.6.1", | |
984 | - "typescript": "5.4.2" | |
985 | - }, | |
986 | - "bin": { | |
987 | - "api-extractor": "bin/api-extractor" | |
988 | - } | |
989 | - }, | |
990 | - "node_modules/@microsoft/api-extractor-model": { | |
991 | - "version": "7.28.13", | |
992 | - "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.28.13.tgz", | |
993 | - "integrity": "sha512-39v/JyldX4MS9uzHcdfmjjfS6cYGAoXV+io8B5a338pkHiSt+gy2eXQ0Q7cGFJ7quSa1VqqlMdlPrB6sLR/cAw==", | |
994 | - "peer": true, | |
995 | - "dependencies": { | |
996 | - "@microsoft/tsdoc": "0.14.2", | |
997 | - "@microsoft/tsdoc-config": "~0.16.1", | |
998 | - "@rushstack/node-core-library": "4.0.2" | |
999 | - } | |
1000 | - }, | |
1001 | - "node_modules/@microsoft/api-extractor/node_modules/minimatch": { | |
1002 | - "version": "3.0.8", | |
1003 | - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz", | |
1004 | - "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==", | |
1005 | - "peer": true, | |
1006 | - "dependencies": { | |
1007 | - "brace-expansion": "^1.1.7" | |
1008 | - }, | |
1009 | - "engines": { | |
1010 | - "node": "*" | |
1011 | - } | |
1012 | - }, | |
1013 | - "node_modules/@microsoft/api-extractor/node_modules/resolve": { | |
1014 | - "version": "1.22.8", | |
1015 | - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", | |
1016 | - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", | |
1017 | - "peer": true, | |
1018 | - "dependencies": { | |
1019 | - "is-core-module": "^2.13.0", | |
1020 | - "path-parse": "^1.0.7", | |
1021 | - "supports-preserve-symlinks-flag": "^1.0.0" | |
1022 | - }, | |
1023 | - "bin": { | |
1024 | - "resolve": "bin/resolve" | |
1025 | - }, | |
1026 | - "funding": { | |
1027 | - "url": "https://github.com/sponsors/ljharb" | |
1028 | - } | |
1029 | - }, | |
1030 | - "node_modules/@microsoft/api-extractor/node_modules/semver": { | |
1031 | - "version": "7.5.4", | |
1032 | - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", | |
1033 | - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", | |
1034 | - "peer": true, | |
1035 | - "dependencies": { | |
1036 | - "lru-cache": "^6.0.0" | |
1037 | - }, | |
1038 | - "bin": { | |
1039 | - "semver": "bin/semver.js" | |
1040 | - }, | |
1041 | - "engines": { | |
1042 | - "node": ">=10" | |
1043 | - } | |
1044 | - }, | |
1045 | - "node_modules/@microsoft/api-extractor/node_modules/typescript": { | |
1046 | - "version": "5.4.2", | |
1047 | - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", | |
1048 | - "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", | |
1049 | - "peer": true, | |
1050 | - "bin": { | |
1051 | - "tsc": "bin/tsc", | |
1052 | - "tsserver": "bin/tsserver" | |
1053 | - }, | |
1054 | - "engines": { | |
1055 | - "node": ">=14.17" | |
1056 | - } | |
1057 | - }, | |
1058 | - "node_modules/@microsoft/tsdoc": { | |
1059 | - "version": "0.14.2", | |
1060 | - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", | |
1061 | - "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", | |
1062 | - "peer": true | |
1063 | - }, | |
1064 | - "node_modules/@microsoft/tsdoc-config": { | |
1065 | - "version": "0.16.2", | |
1066 | - "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", | |
1067 | - "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", | |
1068 | - "peer": true, | |
1069 | - "dependencies": { | |
1070 | - "@microsoft/tsdoc": "0.14.2", | |
1071 | - "ajv": "~6.12.6", | |
1072 | - "jju": "~1.4.0", | |
1073 | - "resolve": "~1.19.0" | |
1074 | - } | |
1075 | - }, | |
1076 | - "node_modules/@microsoft/tsdoc-config/node_modules/resolve": { | |
1077 | - "version": "1.19.0", | |
1078 | - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", | |
1079 | - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", | |
1080 | - "peer": true, | |
1081 | - "dependencies": { | |
1082 | - "is-core-module": "^2.1.0", | |
1083 | - "path-parse": "^1.0.6" | |
1084 | - }, | |
1085 | - "funding": { | |
1086 | - "url": "https://github.com/sponsors/ljharb" | |
1087 | - } | |
1088 | - }, | |
1089 | - "node_modules/@mui/base": { | |
1090 | - "version": "5.0.0-beta.40", | |
1091 | - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", | |
1092 | - "integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==", | |
1093 | - "dependencies": { | |
1094 | - "@babel/runtime": "^7.23.9", | |
1095 | - "@floating-ui/react-dom": "^2.0.8", | |
1096 | - "@mui/types": "^7.2.14", | |
1097 | - "@mui/utils": "^5.15.14", | |
1098 | - "@popperjs/core": "^2.11.8", | |
1099 | - "clsx": "^2.1.0", | |
1100 | - "prop-types": "^15.8.1" | |
1101 | - }, | |
1102 | - "engines": { | |
1103 | - "node": ">=12.0.0" | |
1104 | - }, | |
1105 | - "funding": { | |
1106 | - "type": "opencollective", | |
1107 | - "url": "https://opencollective.com/mui-org" | |
1108 | - }, | |
1109 | - "peerDependencies": { | |
1110 | - "@types/react": "^17.0.0 || ^18.0.0", | |
1111 | - "react": "^17.0.0 || ^18.0.0", | |
1112 | - "react-dom": "^17.0.0 || ^18.0.0" | |
1113 | - }, | |
1114 | - "peerDependenciesMeta": { | |
1115 | - "@types/react": { | |
1116 | - "optional": true | |
1117 | - } | |
1118 | - } | |
1119 | - }, | |
1120 | - "node_modules/@mui/core-downloads-tracker": { | |
1121 | - "version": "5.15.18", | |
1122 | - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.18.tgz", | |
1123 | - "integrity": "sha512-/9pVk+Al8qxAjwFUADv4BRZgMpZM4m5E+2Q/20qhVPuIJWqKp4Ie4tGExac6zu93rgPTYVQGgu+1vjiT0E+cEw==", | |
1124 | - "funding": { | |
1125 | - "type": "opencollective", | |
1126 | - "url": "https://opencollective.com/mui-org" | |
1127 | - } | |
1128 | - }, | |
1129 | - "node_modules/@mui/icons-material": { | |
1130 | - "version": "5.15.15", | |
1131 | - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.15.tgz", | |
1132 | - "integrity": "sha512-kkeU/pe+hABcYDH6Uqy8RmIsr2S/y5bP2rp+Gat4CcRjCcVne6KudS1NrZQhUCRysrTDCAhcbcf9gt+/+pGO2g==", | |
1133 | - "dependencies": { | |
1134 | - "@babel/runtime": "^7.23.9" | |
1135 | - }, | |
1136 | - "engines": { | |
1137 | - "node": ">=12.0.0" | |
1138 | - }, | |
1139 | - "funding": { | |
1140 | - "type": "opencollective", | |
1141 | - "url": "https://opencollective.com/mui-org" | |
1142 | - }, | |
1143 | - "peerDependencies": { | |
1144 | - "@mui/material": "^5.0.0", | |
1145 | - "@types/react": "^17.0.0 || ^18.0.0", | |
1146 | - "react": "^17.0.0 || ^18.0.0" | |
1147 | - }, | |
1148 | - "peerDependenciesMeta": { | |
1149 | - "@types/react": { | |
1150 | - "optional": true | |
1151 | - } | |
1152 | - } | |
1153 | - }, | |
1154 | - "node_modules/@mui/material": { | |
1155 | - "version": "5.15.18", | |
1156 | - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.18.tgz", | |
1157 | - "integrity": "sha512-n+/dsiqux74fFfcRUJjok+ieNQ7+BEk6/OwX9cLcLvriZrZb+/7Y8+Fd2HlUUbn5N0CDurgAHm0VH1DqyJ9HAw==", | |
1158 | - "dependencies": { | |
1159 | - "@babel/runtime": "^7.23.9", | |
1160 | - "@mui/base": "5.0.0-beta.40", | |
1161 | - "@mui/core-downloads-tracker": "^5.15.18", | |
1162 | - "@mui/system": "^5.15.15", | |
1163 | - "@mui/types": "^7.2.14", | |
1164 | - "@mui/utils": "^5.15.14", | |
1165 | - "@types/react-transition-group": "^4.4.10", | |
1166 | - "clsx": "^2.1.0", | |
1167 | - "csstype": "^3.1.3", | |
1168 | - "prop-types": "^15.8.1", | |
1169 | - "react-is": "^18.2.0", | |
1170 | - "react-transition-group": "^4.4.5" | |
1171 | - }, | |
1172 | - "engines": { | |
1173 | - "node": ">=12.0.0" | |
1174 | - }, | |
1175 | - "funding": { | |
1176 | - "type": "opencollective", | |
1177 | - "url": "https://opencollective.com/mui-org" | |
1178 | - }, | |
1179 | - "peerDependencies": { | |
1180 | - "@emotion/react": "^11.5.0", | |
1181 | - "@emotion/styled": "^11.3.0", | |
1182 | - "@types/react": "^17.0.0 || ^18.0.0", | |
1183 | - "react": "^17.0.0 || ^18.0.0", | |
1184 | - "react-dom": "^17.0.0 || ^18.0.0" | |
1185 | - }, | |
1186 | - "peerDependenciesMeta": { | |
1187 | - "@emotion/react": { | |
1188 | - "optional": true | |
1189 | - }, | |
1190 | - "@emotion/styled": { | |
1191 | - "optional": true | |
1192 | - }, | |
1193 | - "@types/react": { | |
1194 | - "optional": true | |
1195 | - } | |
1196 | - } | |
1197 | - }, | |
1198 | - "node_modules/@mui/material/node_modules/react-is": { | |
1199 | - "version": "18.3.1", | |
1200 | - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", | |
1201 | - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" | |
1202 | - }, | |
1203 | - "node_modules/@mui/private-theming": { | |
1204 | - "version": "5.15.14", | |
1205 | - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.14.tgz", | |
1206 | - "integrity": "sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==", | |
1207 | - "dependencies": { | |
1208 | - "@babel/runtime": "^7.23.9", | |
1209 | - "@mui/utils": "^5.15.14", | |
1210 | - "prop-types": "^15.8.1" | |
1211 | - }, | |
1212 | - "engines": { | |
1213 | - "node": ">=12.0.0" | |
1214 | - }, | |
1215 | - "funding": { | |
1216 | - "type": "opencollective", | |
1217 | - "url": "https://opencollective.com/mui-org" | |
1218 | - }, | |
1219 | - "peerDependencies": { | |
1220 | - "@types/react": "^17.0.0 || ^18.0.0", | |
1221 | - "react": "^17.0.0 || ^18.0.0" | |
1222 | - }, | |
1223 | - "peerDependenciesMeta": { | |
1224 | - "@types/react": { | |
1225 | - "optional": true | |
1226 | - } | |
1227 | - } | |
1228 | - }, | |
1229 | - "node_modules/@mui/styled-engine": { | |
1230 | - "version": "5.15.14", | |
1231 | - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz", | |
1232 | - "integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==", | |
1233 | - "dependencies": { | |
1234 | - "@babel/runtime": "^7.23.9", | |
1235 | - "@emotion/cache": "^11.11.0", | |
1236 | - "csstype": "^3.1.3", | |
1237 | - "prop-types": "^15.8.1" | |
1238 | - }, | |
1239 | - "engines": { | |
1240 | - "node": ">=12.0.0" | |
1241 | - }, | |
1242 | - "funding": { | |
1243 | - "type": "opencollective", | |
1244 | - "url": "https://opencollective.com/mui-org" | |
1245 | - }, | |
1246 | - "peerDependencies": { | |
1247 | - "@emotion/react": "^11.4.1", | |
1248 | - "@emotion/styled": "^11.3.0", | |
1249 | - "react": "^17.0.0 || ^18.0.0" | |
1250 | - }, | |
1251 | - "peerDependenciesMeta": { | |
1252 | - "@emotion/react": { | |
1253 | - "optional": true | |
1254 | - }, | |
1255 | - "@emotion/styled": { | |
1256 | - "optional": true | |
1257 | - } | |
1258 | - } | |
1259 | - }, | |
1260 | - "node_modules/@mui/styled-engine-sc": { | |
1261 | - "version": "6.0.0-alpha.18", | |
1262 | - "resolved": "https://registry.npmjs.org/@mui/styled-engine-sc/-/styled-engine-sc-6.0.0-alpha.18.tgz", | |
1263 | - "integrity": "sha512-W3mqR1K01rPL0BVNTgGpIYxdbQ/nTAlwYaohRdmX7FZvbm1yKw9F90OIGxM503dfRMVBi6a/neYPgIUebcGsHw==", | |
1264 | - "dependencies": { | |
1265 | - "@babel/runtime": "^7.23.9", | |
1266 | - "csstype": "^3.1.3", | |
1267 | - "hoist-non-react-statics": "^3.3.2", | |
1268 | - "prop-types": "^15.8.1" | |
1269 | - }, | |
1270 | - "engines": { | |
1271 | - "node": ">=12.0.0" | |
1272 | - }, | |
1273 | - "funding": { | |
1274 | - "type": "opencollective", | |
1275 | - "url": "https://opencollective.com/mui-org" | |
1276 | - }, | |
1277 | - "peerDependencies": { | |
1278 | - "styled-components": "^6.0.0" | |
1279 | - } | |
1280 | - }, | |
1281 | - "node_modules/@mui/system": { | |
1282 | - "version": "5.15.15", | |
1283 | - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz", | |
1284 | - "integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==", | |
1285 | - "dependencies": { | |
1286 | - "@babel/runtime": "^7.23.9", | |
1287 | - "@mui/private-theming": "^5.15.14", | |
1288 | - "@mui/styled-engine": "^5.15.14", | |
1289 | - "@mui/types": "^7.2.14", | |
1290 | - "@mui/utils": "^5.15.14", | |
1291 | - "clsx": "^2.1.0", | |
1292 | - "csstype": "^3.1.3", | |
1293 | - "prop-types": "^15.8.1" | |
1294 | - }, | |
1295 | - "engines": { | |
1296 | - "node": ">=12.0.0" | |
1297 | - }, | |
1298 | - "funding": { | |
1299 | - "type": "opencollective", | |
1300 | - "url": "https://opencollective.com/mui-org" | |
1301 | - }, | |
1302 | - "peerDependencies": { | |
1303 | - "@emotion/react": "^11.5.0", | |
1304 | - "@emotion/styled": "^11.3.0", | |
1305 | - "@types/react": "^17.0.0 || ^18.0.0", | |
1306 | - "react": "^17.0.0 || ^18.0.0" | |
1307 | - }, | |
1308 | - "peerDependenciesMeta": { | |
1309 | - "@emotion/react": { | |
1310 | - "optional": true | |
1311 | - }, | |
1312 | - "@emotion/styled": { | |
1313 | - "optional": true | |
1314 | - }, | |
1315 | - "@types/react": { | |
1316 | - "optional": true | |
1317 | - } | |
1318 | - } | |
1319 | - }, | |
1320 | - "node_modules/@mui/types": { | |
1321 | - "version": "7.2.14", | |
1322 | - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.14.tgz", | |
1323 | - "integrity": "sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==", | |
1324 | - "peerDependencies": { | |
1325 | - "@types/react": "^17.0.0 || ^18.0.0" | |
1326 | - }, | |
1327 | - "peerDependenciesMeta": { | |
1328 | - "@types/react": { | |
1329 | - "optional": true | |
1330 | - } | |
1331 | - } | |
1332 | - }, | |
1333 | - "node_modules/@mui/utils": { | |
1334 | - "version": "5.15.14", | |
1335 | - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.14.tgz", | |
1336 | - "integrity": "sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==", | |
1337 | - "dependencies": { | |
1338 | - "@babel/runtime": "^7.23.9", | |
1339 | - "@types/prop-types": "^15.7.11", | |
1340 | - "prop-types": "^15.8.1", | |
1341 | - "react-is": "^18.2.0" | |
1342 | - }, | |
1343 | - "engines": { | |
1344 | - "node": ">=12.0.0" | |
1345 | - }, | |
1346 | - "funding": { | |
1347 | - "type": "opencollective", | |
1348 | - "url": "https://opencollective.com/mui-org" | |
1349 | - }, | |
1350 | - "peerDependencies": { | |
1351 | - "@types/react": "^17.0.0 || ^18.0.0", | |
1352 | - "react": "^17.0.0 || ^18.0.0" | |
1353 | - }, | |
1354 | - "peerDependenciesMeta": { | |
1355 | - "@types/react": { | |
1356 | - "optional": true | |
1357 | - } | |
1358 | - } | |
1359 | - }, | |
1360 | - "node_modules/@mui/utils/node_modules/react-is": { | |
1361 | - "version": "18.3.1", | |
1362 | - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", | |
1363 | - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==" | |
1364 | - }, | |
1365 | - "node_modules/@mui/x-data-grid": { | |
1366 | - "version": "7.5.0", | |
1367 | - "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.5.0.tgz", | |
1368 | - "integrity": "sha512-CdIDjqMHPWJvG/M6z36fnqIR+z2nW8Guk/wLUOv7vRJNVeOJasrkqg3xVZ3IFEGtbkTlQQ6iu1Os8YlF2HPWww==", | |
1369 | - "dependencies": { | |
1370 | - "@babel/runtime": "^7.24.5", | |
1371 | - "@mui/system": "^5.15.14", | |
1372 | - "@mui/utils": "^5.15.14", | |
1373 | - "clsx": "^2.1.1", | |
1374 | - "prop-types": "^15.8.1", | |
1375 | - "reselect": "^4.1.8" | |
1376 | - }, | |
1377 | - "engines": { | |
1378 | - "node": ">=14.0.0" | |
1379 | - }, | |
1380 | - "funding": { | |
1381 | - "type": "opencollective", | |
1382 | - "url": "https://opencollective.com/mui-org" | |
1383 | - }, | |
1384 | - "peerDependencies": { | |
1385 | - "@mui/material": "^5.15.14", | |
1386 | - "react": "^17.0.0 || ^18.0.0", | |
1387 | - "react-dom": "^17.0.0 || ^18.0.0" | |
1388 | - } | |
1389 | - }, | |
1390 | - "node_modules/@mui/x-data-grid-generator": { | |
1391 | - "version": "7.4.0", | |
1392 | - "resolved": "https://registry.npmjs.org/@mui/x-data-grid-generator/-/x-data-grid-generator-7.4.0.tgz", | |
1393 | - "integrity": "sha512-E5kQmjZx/9DXNMvC0BNrzjE5tCkmiIIs2EEedcXAzI6vM4oQv2CxcekNNVcYSL+lI+X7Y+tZ3MwnqviYKk9WDg==", | |
1394 | - "dependencies": { | |
1395 | - "@babel/runtime": "^7.24.0", | |
1396 | - "@mui/base": "^5.0.0-beta.40", | |
1397 | - "@mui/x-data-grid-premium": "7.4.0", | |
1398 | - "chance": "^1.1.11", | |
1399 | - "clsx": "^2.1.1", | |
1400 | - "lru-cache": "^7.18.3" | |
1401 | - }, | |
1402 | - "engines": { | |
1403 | - "node": ">=14.0.0" | |
1404 | - }, | |
1405 | - "peerDependencies": { | |
1406 | - "@mui/icons-material": "^5.4.1", | |
1407 | - "@mui/material": "^5.15.14", | |
1408 | - "react": "^17.0.0 || ^18.0.0" | |
1409 | - } | |
1410 | - }, | |
1411 | - "node_modules/@mui/x-data-grid-generator/node_modules/lru-cache": { | |
1412 | - "version": "7.18.3", | |
1413 | - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", | |
1414 | - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", | |
1415 | - "engines": { | |
1416 | - "node": ">=12" | |
1417 | - } | |
1418 | - }, | |
1419 | - "node_modules/@mui/x-data-grid-premium": { | |
1420 | - "version": "7.4.0", | |
1421 | - "resolved": "https://registry.npmjs.org/@mui/x-data-grid-premium/-/x-data-grid-premium-7.4.0.tgz", | |
1422 | - "integrity": "sha512-BnzkY9T17sNeftnkCU7LSkArUhir7dXN1Xd+prPrqcdEOJcSqFJ1pyQaGmb8IKdj6y86tLlvJZ8N19k/wHzNSw==", | |
1423 | - "dependencies": { | |
1424 | - "@babel/runtime": "^7.24.0", | |
1425 | - "@mui/system": "^5.15.14", | |
1426 | - "@mui/utils": "^5.15.14", | |
1427 | - "@mui/x-data-grid": "7.4.0", | |
1428 | - "@mui/x-data-grid-pro": "7.4.0", | |
1429 | - "@mui/x-license": "7.2.0", | |
1430 | - "@types/format-util": "^1.0.4", | |
1431 | - "clsx": "^2.1.1", | |
1432 | - "exceljs": "^4.4.0", | |
1433 | - "prop-types": "^15.8.1", | |
1434 | - "reselect": "^4.1.8" | |
1435 | - }, | |
1436 | - "engines": { | |
1437 | - "node": ">=14.0.0" | |
1438 | - }, | |
1439 | - "peerDependencies": { | |
1440 | - "@mui/material": "^5.15.14", | |
1441 | - "react": "^17.0.0 || ^18.0.0", | |
1442 | - "react-dom": "^17.0.0 || ^18.0.0" | |
1443 | - } | |
1444 | - }, | |
1445 | - "node_modules/@mui/x-data-grid-premium/node_modules/@mui/x-data-grid": { | |
1446 | - "version": "7.4.0", | |
1447 | - "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.4.0.tgz", | |
1448 | - "integrity": "sha512-ILu0AVqqHQf4wN/nblsJ/k7PkvlB115vQ/FEiYk7neZlc/kOJOUyst3MWMVClAecZ8+JEs476q40xd4r1CtMfw==", | |
1449 | - "dependencies": { | |
1450 | - "@babel/runtime": "^7.24.0", | |
1451 | - "@mui/system": "^5.15.14", | |
1452 | - "@mui/utils": "^5.15.14", | |
1453 | - "clsx": "^2.1.1", | |
1454 | - "prop-types": "^15.8.1", | |
1455 | - "reselect": "^4.1.8" | |
1456 | - }, | |
1457 | - "engines": { | |
1458 | - "node": ">=14.0.0" | |
1459 | - }, | |
1460 | - "funding": { | |
1461 | - "type": "opencollective", | |
1462 | - "url": "https://opencollective.com/mui-org" | |
1463 | - }, | |
1464 | - "peerDependencies": { | |
1465 | - "@mui/material": "^5.15.14", | |
1466 | - "react": "^17.0.0 || ^18.0.0", | |
1467 | - "react-dom": "^17.0.0 || ^18.0.0" | |
1468 | - } | |
1469 | - }, | |
1470 | - "node_modules/@mui/x-data-grid-pro": { | |
1471 | - "version": "7.4.0", | |
1472 | - "resolved": "https://registry.npmjs.org/@mui/x-data-grid-pro/-/x-data-grid-pro-7.4.0.tgz", | |
1473 | - "integrity": "sha512-ORg7zm4oGL7E/hw9hJCk9PYwj31doLCCq52DLtZQr4mZgtx1jptVoq0jSoMRXy3nS2+khkcojALqlkLM6lYdiQ==", | |
1474 | - "dependencies": { | |
1475 | - "@babel/runtime": "^7.24.0", | |
1476 | - "@mui/system": "^5.15.14", | |
1477 | - "@mui/utils": "^5.15.14", | |
1478 | - "@mui/x-data-grid": "7.4.0", | |
1479 | - "@mui/x-license": "7.2.0", | |
1480 | - "@types/format-util": "^1.0.4", | |
1481 | - "clsx": "^2.1.1", | |
1482 | - "prop-types": "^15.8.1", | |
1483 | - "reselect": "^4.1.8" | |
1484 | - }, | |
1485 | - "engines": { | |
1486 | - "node": ">=14.0.0" | |
1487 | - }, | |
1488 | - "peerDependencies": { | |
1489 | - "@mui/material": "^5.15.14", | |
1490 | - "react": "^17.0.0 || ^18.0.0", | |
1491 | - "react-dom": "^17.0.0 || ^18.0.0" | |
1492 | - } | |
1493 | - }, | |
1494 | - "node_modules/@mui/x-data-grid-pro/node_modules/@mui/x-data-grid": { | |
1495 | - "version": "7.4.0", | |
1496 | - "resolved": "https://registry.npmjs.org/@mui/x-data-grid/-/x-data-grid-7.4.0.tgz", | |
1497 | - "integrity": "sha512-ILu0AVqqHQf4wN/nblsJ/k7PkvlB115vQ/FEiYk7neZlc/kOJOUyst3MWMVClAecZ8+JEs476q40xd4r1CtMfw==", | |
1498 | - "dependencies": { | |
1499 | - "@babel/runtime": "^7.24.0", | |
1500 | - "@mui/system": "^5.15.14", | |
1501 | - "@mui/utils": "^5.15.14", | |
1502 | - "clsx": "^2.1.1", | |
1503 | - "prop-types": "^15.8.1", | |
1504 | - "reselect": "^4.1.8" | |
1505 | - }, | |
1506 | - "engines": { | |
1507 | - "node": ">=14.0.0" | |
1508 | - }, | |
1509 | - "funding": { | |
1510 | - "type": "opencollective", | |
1511 | - "url": "https://opencollective.com/mui-org" | |
1512 | - }, | |
1513 | - "peerDependencies": { | |
1514 | - "@mui/material": "^5.15.14", | |
1515 | - "react": "^17.0.0 || ^18.0.0", | |
1516 | - "react-dom": "^17.0.0 || ^18.0.0" | |
1517 | - } | |
1518 | - }, | |
1519 | - "node_modules/@mui/x-license": { | |
1520 | - "version": "7.2.0", | |
1521 | - "resolved": "https://registry.npmjs.org/@mui/x-license/-/x-license-7.2.0.tgz", | |
1522 | - "integrity": "sha512-z9mqsfNPVFqjfxcPgz15o29Vb3FupSImwpMd5CFjZqNasJu3ptLpKxbIUnTtJMUicRdhsVfm3d93Z5XQkq1JuQ==", | |
1523 | - "dependencies": { | |
1524 | - "@babel/runtime": "^7.24.0", | |
1525 | - "@mui/utils": "^5.15.14" | |
1526 | - }, | |
1527 | - "engines": { | |
1528 | - "node": ">=14.0.0" | |
1529 | - }, | |
1530 | - "peerDependencies": { | |
1531 | - "react": "^17.0.0 || ^18.0.0" | |
1532 | - } | |
1533 | - }, | |
1534 | - "node_modules/@nodelib/fs.scandir": { | |
1535 | - "version": "2.1.5", | |
1536 | - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", | |
1537 | - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", | |
1538 | - "dev": true, | |
1539 | - "dependencies": { | |
1540 | - "@nodelib/fs.stat": "2.0.5", | |
1541 | - "run-parallel": "^1.1.9" | |
1542 | - }, | |
1543 | - "engines": { | |
1544 | - "node": ">= 8" | |
1545 | - } | |
1546 | - }, | |
1547 | - "node_modules/@nodelib/fs.stat": { | |
1548 | - "version": "2.0.5", | |
1549 | - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", | |
1550 | - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", | |
1551 | - "dev": true, | |
1552 | - "engines": { | |
1553 | - "node": ">= 8" | |
1554 | - } | |
1555 | - }, | |
1556 | - "node_modules/@nodelib/fs.walk": { | |
1557 | - "version": "1.2.8", | |
1558 | - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", | |
1559 | - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", | |
1560 | - "dev": true, | |
1561 | - "dependencies": { | |
1562 | - "@nodelib/fs.scandir": "2.1.5", | |
1563 | - "fastq": "^1.6.0" | |
1564 | - }, | |
1565 | - "engines": { | |
1566 | - "node": ">= 8" | |
1567 | - } | |
1568 | - }, | |
1569 | - "node_modules/@pkgjs/parseargs": { | |
1570 | - "version": "0.11.0", | |
1571 | - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", | |
1572 | - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", | |
1573 | - "dev": true, | |
1574 | - "optional": true, | |
1575 | - "engines": { | |
1576 | - "node": ">=14" | |
1577 | - } | |
1578 | - }, | |
1579 | - "node_modules/@popperjs/core": { | |
1580 | - "version": "2.11.8", | |
1581 | - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", | |
1582 | - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", | |
1583 | - "funding": { | |
1584 | - "type": "opencollective", | |
1585 | - "url": "https://opencollective.com/popperjs" | |
1586 | - } | |
1587 | - }, | |
1588 | - "node_modules/@remix-run/router": { | |
1589 | - "version": "1.16.0", | |
1590 | - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.16.0.tgz", | |
1591 | - "integrity": "sha512-Quz1KOffeEf/zwkCBM3kBtH4ZoZ+pT3xIXBG4PPW/XFtDP7EGhtTiC2+gpL9GnR7+Qdet5Oa6cYSvwKYg6kN9Q==", | |
1592 | - "engines": { | |
1593 | - "node": ">=14.0.0" | |
1594 | - } | |
1595 | - }, | |
1596 | - "node_modules/@rollup/pluginutils": { | |
1597 | - "version": "5.1.0", | |
1598 | - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", | |
1599 | - "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", | |
1600 | - "peer": true, | |
1601 | - "dependencies": { | |
1602 | - "@types/estree": "^1.0.0", | |
1603 | - "estree-walker": "^2.0.2", | |
1604 | - "picomatch": "^2.3.1" | |
1605 | - }, | |
1606 | - "engines": { | |
1607 | - "node": ">=14.0.0" | |
1608 | - }, | |
1609 | - "peerDependencies": { | |
1610 | - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" | |
1611 | - }, | |
1612 | - "peerDependenciesMeta": { | |
1613 | - "rollup": { | |
1614 | - "optional": true | |
1615 | - } | |
1616 | - } | |
1617 | - }, | |
1618 | - "node_modules/@rollup/rollup-android-arm-eabi": { | |
1619 | - "version": "4.17.2", | |
1620 | - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.17.2.tgz", | |
1621 | - "integrity": "sha512-NM0jFxY8bB8QLkoKxIQeObCaDlJKewVlIEkuyYKm5An1tdVZ966w2+MPQ2l8LBZLjR+SgyV+nRkTIunzOYBMLQ==", | |
1622 | - "cpu": [ | |
1623 | - "arm" | |
1624 | - ], | |
1625 | - "optional": true, | |
1626 | - "os": [ | |
1627 | - "android" | |
1628 | - ] | |
1629 | - }, | |
1630 | - "node_modules/@rollup/rollup-android-arm64": { | |
1631 | - "version": "4.17.2", | |
1632 | - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.17.2.tgz", | |
1633 | - "integrity": "sha512-yeX/Usk7daNIVwkq2uGoq2BYJKZY1JfyLTaHO/jaiSwi/lsf8fTFoQW/n6IdAsx5tx+iotu2zCJwz8MxI6D/Bw==", | |
1634 | - "cpu": [ | |
1635 | - "arm64" | |
1636 | - ], | |
1637 | - "optional": true, | |
1638 | - "os": [ | |
1639 | - "android" | |
1640 | - ] | |
1641 | - }, | |
1642 | - "node_modules/@rollup/rollup-darwin-arm64": { | |
1643 | - "version": "4.17.2", | |
1644 | - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.17.2.tgz", | |
1645 | - "integrity": "sha512-kcMLpE6uCwls023+kknm71ug7MZOrtXo+y5p/tsg6jltpDtgQY1Eq5sGfHcQfb+lfuKwhBmEURDga9N0ol4YPw==", | |
1646 | - "cpu": [ | |
1647 | - "arm64" | |
1648 | - ], | |
1649 | - "optional": true, | |
1650 | - "os": [ | |
1651 | - "darwin" | |
1652 | - ] | |
1653 | - }, | |
1654 | - "node_modules/@rollup/rollup-darwin-x64": { | |
1655 | - "version": "4.17.2", | |
1656 | - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.17.2.tgz", | |
1657 | - "integrity": "sha512-AtKwD0VEx0zWkL0ZjixEkp5tbNLzX+FCqGG1SvOu993HnSz4qDI6S4kGzubrEJAljpVkhRSlg5bzpV//E6ysTQ==", | |
1658 | - "cpu": [ | |
1659 | - "x64" | |
1660 | - ], | |
1661 | - "optional": true, | |
1662 | - "os": [ | |
1663 | - "darwin" | |
1664 | - ] | |
1665 | - }, | |
1666 | - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { | |
1667 | - "version": "4.17.2", | |
1668 | - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.17.2.tgz", | |
1669 | - "integrity": "sha512-3reX2fUHqN7sffBNqmEyMQVj/CKhIHZd4y631duy0hZqI8Qoqf6lTtmAKvJFYa6bhU95B1D0WgzHkmTg33In0A==", | |
1670 | - "cpu": [ | |
1671 | - "arm" | |
1672 | - ], | |
1673 | - "optional": true, | |
1674 | - "os": [ | |
1675 | - "linux" | |
1676 | - ] | |
1677 | - }, | |
1678 | - "node_modules/@rollup/rollup-linux-arm-musleabihf": { | |
1679 | - "version": "4.17.2", | |
1680 | - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.17.2.tgz", | |
1681 | - "integrity": "sha512-uSqpsp91mheRgw96xtyAGP9FW5ChctTFEoXP0r5FAzj/3ZRv3Uxjtc7taRQSaQM/q85KEKjKsZuiZM3GyUivRg==", | |
1682 | - "cpu": [ | |
1683 | - "arm" | |
1684 | - ], | |
1685 | - "optional": true, | |
1686 | - "os": [ | |
1687 | - "linux" | |
1688 | - ] | |
1689 | - }, | |
1690 | - "node_modules/@rollup/rollup-linux-arm64-gnu": { | |
1691 | - "version": "4.17.2", | |
1692 | - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.17.2.tgz", | |
1693 | - "integrity": "sha512-EMMPHkiCRtE8Wdk3Qhtciq6BndLtstqZIroHiiGzB3C5LDJmIZcSzVtLRbwuXuUft1Cnv+9fxuDtDxz3k3EW2A==", | |
1694 | - "cpu": [ | |
1695 | - "arm64" | |
1696 | - ], | |
1697 | - "optional": true, | |
1698 | - "os": [ | |
1699 | - "linux" | |
1700 | - ] | |
1701 | - }, | |
1702 | - "node_modules/@rollup/rollup-linux-arm64-musl": { | |
1703 | - "version": "4.17.2", | |
1704 | - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.17.2.tgz", | |
1705 | - "integrity": "sha512-NMPylUUZ1i0z/xJUIx6VUhISZDRT+uTWpBcjdv0/zkp7b/bQDF+NfnfdzuTiB1G6HTodgoFa93hp0O1xl+/UbA==", | |
1706 | - "cpu": [ | |
1707 | - "arm64" | |
1708 | - ], | |
1709 | - "optional": true, | |
1710 | - "os": [ | |
1711 | - "linux" | |
1712 | - ] | |
1713 | - }, | |
1714 | - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { | |
1715 | - "version": "4.17.2", | |
1716 | - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.17.2.tgz", | |
1717 | - "integrity": "sha512-T19My13y8uYXPw/L/k0JYaX1fJKFT/PWdXiHr8mTbXWxjVF1t+8Xl31DgBBvEKclw+1b00Chg0hxE2O7bTG7GQ==", | |
1718 | - "cpu": [ | |
1719 | - "ppc64" | |
1720 | - ], | |
1721 | - "optional": true, | |
1722 | - "os": [ | |
1723 | - "linux" | |
1724 | - ] | |
1725 | - }, | |
1726 | - "node_modules/@rollup/rollup-linux-riscv64-gnu": { | |
1727 | - "version": "4.17.2", | |
1728 | - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.17.2.tgz", | |
1729 | - "integrity": "sha512-BOaNfthf3X3fOWAB+IJ9kxTgPmMqPPH5f5k2DcCsRrBIbWnaJCgX2ll77dV1TdSy9SaXTR5iDXRL8n7AnoP5cg==", | |
1730 | - "cpu": [ | |
1731 | - "riscv64" | |
1732 | - ], | |
1733 | - "optional": true, | |
1734 | - "os": [ | |
1735 | - "linux" | |
1736 | - ] | |
1737 | - }, | |
1738 | - "node_modules/@rollup/rollup-linux-s390x-gnu": { | |
1739 | - "version": "4.17.2", | |
1740 | - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.17.2.tgz", | |
1741 | - "integrity": "sha512-W0UP/x7bnn3xN2eYMql2T/+wpASLE5SjObXILTMPUBDB/Fg/FxC+gX4nvCfPBCbNhz51C+HcqQp2qQ4u25ok6g==", | |
1742 | - "cpu": [ | |
1743 | - "s390x" | |
1744 | - ], | |
1745 | - "optional": true, | |
1746 | - "os": [ | |
1747 | - "linux" | |
1748 | - ] | |
1749 | - }, | |
1750 | - "node_modules/@rollup/rollup-linux-x64-gnu": { | |
1751 | - "version": "4.17.2", | |
1752 | - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.17.2.tgz", | |
1753 | - "integrity": "sha512-Hy7pLwByUOuyaFC6mAr7m+oMC+V7qyifzs/nW2OJfC8H4hbCzOX07Ov0VFk/zP3kBsELWNFi7rJtgbKYsav9QQ==", | |
1754 | - "cpu": [ | |
1755 | - "x64" | |
1756 | - ], | |
1757 | - "optional": true, | |
1758 | - "os": [ | |
1759 | - "linux" | |
1760 | - ] | |
1761 | - }, | |
1762 | - "node_modules/@rollup/rollup-linux-x64-musl": { | |
1763 | - "version": "4.17.2", | |
1764 | - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.17.2.tgz", | |
1765 | - "integrity": "sha512-h1+yTWeYbRdAyJ/jMiVw0l6fOOm/0D1vNLui9iPuqgRGnXA0u21gAqOyB5iHjlM9MMfNOm9RHCQ7zLIzT0x11Q==", | |
1766 | - "cpu": [ | |
1767 | - "x64" | |
1768 | - ], | |
1769 | - "optional": true, | |
1770 | - "os": [ | |
1771 | - "linux" | |
1772 | - ] | |
1773 | - }, | |
1774 | - "node_modules/@rollup/rollup-win32-arm64-msvc": { | |
1775 | - "version": "4.17.2", | |
1776 | - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.17.2.tgz", | |
1777 | - "integrity": "sha512-tmdtXMfKAjy5+IQsVtDiCfqbynAQE/TQRpWdVataHmhMb9DCoJxp9vLcCBjEQWMiUYxO1QprH/HbY9ragCEFLA==", | |
1778 | - "cpu": [ | |
1779 | - "arm64" | |
1780 | - ], | |
1781 | - "optional": true, | |
1782 | - "os": [ | |
1783 | - "win32" | |
1784 | - ] | |
1785 | - }, | |
1786 | - "node_modules/@rollup/rollup-win32-ia32-msvc": { | |
1787 | - "version": "4.17.2", | |
1788 | - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.17.2.tgz", | |
1789 | - "integrity": "sha512-7II/QCSTAHuE5vdZaQEwJq2ZACkBpQDOmQsE6D6XUbnBHW8IAhm4eTufL6msLJorzrHDFv3CF8oCA/hSIRuZeQ==", | |
1790 | - "cpu": [ | |
1791 | - "ia32" | |
1792 | - ], | |
1793 | - "optional": true, | |
1794 | - "os": [ | |
1795 | - "win32" | |
1796 | - ] | |
1797 | - }, | |
1798 | - "node_modules/@rollup/rollup-win32-x64-msvc": { | |
1799 | - "version": "4.17.2", | |
1800 | - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.17.2.tgz", | |
1801 | - "integrity": "sha512-TGGO7v7qOq4CYmSBVEYpI1Y5xDuCEnbVC5Vth8mOsW0gDSzxNrVERPc790IGHsrT2dQSimgMr9Ub3Y1Jci5/8w==", | |
1802 | - "cpu": [ | |
1803 | - "x64" | |
1804 | - ], | |
1805 | - "optional": true, | |
1806 | - "os": [ | |
1807 | - "win32" | |
1808 | - ] | |
1809 | - }, | |
1810 | - "node_modules/@rushstack/node-core-library": { | |
1811 | - "version": "4.0.2", | |
1812 | - "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-4.0.2.tgz", | |
1813 | - "integrity": "sha512-hyES82QVpkfQMeBMteQUnrhASL/KHPhd7iJ8euduwNJG4mu2GSOKybf0rOEjOm1Wz7CwJEUm9y0yD7jg2C1bfg==", | |
1814 | - "peer": true, | |
1815 | - "dependencies": { | |
1816 | - "fs-extra": "~7.0.1", | |
1817 | - "import-lazy": "~4.0.0", | |
1818 | - "jju": "~1.4.0", | |
1819 | - "resolve": "~1.22.1", | |
1820 | - "semver": "~7.5.4", | |
1821 | - "z-schema": "~5.0.2" | |
1822 | - }, | |
1823 | - "peerDependencies": { | |
1824 | - "@types/node": "*" | |
1825 | - }, | |
1826 | - "peerDependenciesMeta": { | |
1827 | - "@types/node": { | |
1828 | - "optional": true | |
1829 | - } | |
1830 | - } | |
1831 | - }, | |
1832 | - "node_modules/@rushstack/node-core-library/node_modules/resolve": { | |
1833 | - "version": "1.22.8", | |
1834 | - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", | |
1835 | - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", | |
1836 | - "peer": true, | |
1837 | - "dependencies": { | |
1838 | - "is-core-module": "^2.13.0", | |
1839 | - "path-parse": "^1.0.7", | |
1840 | - "supports-preserve-symlinks-flag": "^1.0.0" | |
1841 | - }, | |
1842 | - "bin": { | |
1843 | - "resolve": "bin/resolve" | |
1844 | - }, | |
1845 | - "funding": { | |
1846 | - "url": "https://github.com/sponsors/ljharb" | |
1847 | - } | |
1848 | - }, | |
1849 | - "node_modules/@rushstack/node-core-library/node_modules/semver": { | |
1850 | - "version": "7.5.4", | |
1851 | - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", | |
1852 | - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", | |
1853 | - "peer": true, | |
1854 | - "dependencies": { | |
1855 | - "lru-cache": "^6.0.0" | |
1856 | - }, | |
1857 | - "bin": { | |
1858 | - "semver": "bin/semver.js" | |
1859 | - }, | |
1860 | - "engines": { | |
1861 | - "node": ">=10" | |
1862 | - } | |
1863 | - }, | |
1864 | - "node_modules/@rushstack/rig-package": { | |
1865 | - "version": "0.5.2", | |
1866 | - "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.2.tgz", | |
1867 | - "integrity": "sha512-mUDecIJeH3yYGZs2a48k+pbhM6JYwWlgjs2Ca5f2n1G2/kgdgP9D/07oglEGf6mRyXEnazhEENeYTSNDRCwdqA==", | |
1868 | - "peer": true, | |
1869 | - "dependencies": { | |
1870 | - "resolve": "~1.22.1", | |
1871 | - "strip-json-comments": "~3.1.1" | |
1872 | - } | |
1873 | - }, | |
1874 | - "node_modules/@rushstack/rig-package/node_modules/resolve": { | |
1875 | - "version": "1.22.8", | |
1876 | - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", | |
1877 | - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", | |
1878 | - "peer": true, | |
1879 | - "dependencies": { | |
1880 | - "is-core-module": "^2.13.0", | |
1881 | - "path-parse": "^1.0.7", | |
1882 | - "supports-preserve-symlinks-flag": "^1.0.0" | |
1883 | - }, | |
1884 | - "bin": { | |
1885 | - "resolve": "bin/resolve" | |
1886 | - }, | |
1887 | - "funding": { | |
1888 | - "url": "https://github.com/sponsors/ljharb" | |
1889 | - } | |
1890 | - }, | |
1891 | - "node_modules/@rushstack/terminal": { | |
1892 | - "version": "0.10.0", | |
1893 | - "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.10.0.tgz", | |
1894 | - "integrity": "sha512-UbELbXnUdc7EKwfH2sb8ChqNgapUOdqcCIdQP4NGxBpTZV2sQyeekuK3zmfQSa/MN+/7b4kBogl2wq0vpkpYGw==", | |
1895 | - "peer": true, | |
1896 | - "dependencies": { | |
1897 | - "@rushstack/node-core-library": "4.0.2", | |
1898 | - "supports-color": "~8.1.1" | |
1899 | - }, | |
1900 | - "peerDependencies": { | |
1901 | - "@types/node": "*" | |
1902 | - }, | |
1903 | - "peerDependenciesMeta": { | |
1904 | - "@types/node": { | |
1905 | - "optional": true | |
1906 | - } | |
1907 | - } | |
1908 | - }, | |
1909 | - "node_modules/@rushstack/terminal/node_modules/supports-color": { | |
1910 | - "version": "8.1.1", | |
1911 | - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", | |
1912 | - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", | |
1913 | - "peer": true, | |
1914 | - "dependencies": { | |
1915 | - "has-flag": "^4.0.0" | |
1916 | - }, | |
1917 | - "engines": { | |
1918 | - "node": ">=10" | |
1919 | - }, | |
1920 | - "funding": { | |
1921 | - "url": "https://github.com/chalk/supports-color?sponsor=1" | |
1922 | - } | |
1923 | - }, | |
1924 | - "node_modules/@rushstack/ts-command-line": { | |
1925 | - "version": "4.19.1", | |
1926 | - "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.19.1.tgz", | |
1927 | - "integrity": "sha512-J7H768dgcpG60d7skZ5uSSwyCZs/S2HrWP1Ds8d1qYAyaaeJmpmmLr9BVw97RjFzmQPOYnoXcKA4GkqDCkduQg==", | |
1928 | - "peer": true, | |
1929 | - "dependencies": { | |
1930 | - "@rushstack/terminal": "0.10.0", | |
1931 | - "@types/argparse": "1.0.38", | |
1932 | - "argparse": "~1.0.9", | |
1933 | - "string-argv": "~0.3.1" | |
1934 | - } | |
1935 | - }, | |
1936 | - "node_modules/@rushstack/ts-command-line/node_modules/argparse": { | |
1937 | - "version": "1.0.10", | |
1938 | - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", | |
1939 | - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", | |
1940 | - "peer": true, | |
1941 | - "dependencies": { | |
1942 | - "sprintf-js": "~1.0.2" | |
1943 | - } | |
1944 | - }, | |
1945 | - "node_modules/@swc/core": { | |
1946 | - "version": "1.4.17", | |
1947 | - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.4.17.tgz", | |
1948 | - "integrity": "sha512-tq+mdWvodMBNBBZbwFIMTVGYHe9N7zvEaycVVjfvAx20k1XozHbHhRv+9pEVFJjwRxLdXmtvFZd3QZHRAOpoNQ==", | |
1949 | - "hasInstallScript": true, | |
1950 | - "dependencies": { | |
1951 | - "@swc/counter": "^0.1.2", | |
1952 | - "@swc/types": "^0.1.5" | |
1953 | - }, | |
1954 | - "engines": { | |
1955 | - "node": ">=10" | |
1956 | - }, | |
1957 | - "funding": { | |
1958 | - "type": "opencollective", | |
1959 | - "url": "https://opencollective.com/swc" | |
1960 | - }, | |
1961 | - "optionalDependencies": { | |
1962 | - "@swc/core-darwin-arm64": "1.4.17", | |
1963 | - "@swc/core-darwin-x64": "1.4.17", | |
1964 | - "@swc/core-linux-arm-gnueabihf": "1.4.17", | |
1965 | - "@swc/core-linux-arm64-gnu": "1.4.17", | |
1966 | - "@swc/core-linux-arm64-musl": "1.4.17", | |
1967 | - "@swc/core-linux-x64-gnu": "1.4.17", | |
1968 | - "@swc/core-linux-x64-musl": "1.4.17", | |
1969 | - "@swc/core-win32-arm64-msvc": "1.4.17", | |
1970 | - "@swc/core-win32-ia32-msvc": "1.4.17", | |
1971 | - "@swc/core-win32-x64-msvc": "1.4.17" | |
1972 | - }, | |
1973 | - "peerDependencies": { | |
1974 | - "@swc/helpers": "^0.5.0" | |
1975 | - }, | |
1976 | - "peerDependenciesMeta": { | |
1977 | - "@swc/helpers": { | |
1978 | - "optional": true | |
1979 | - } | |
1980 | - } | |
1981 | - }, | |
1982 | - "node_modules/@swc/core-darwin-arm64": { | |
1983 | - "version": "1.4.17", | |
1984 | - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.4.17.tgz", | |
1985 | - "integrity": "sha512-HVl+W4LezoqHBAYg2JCqR+s9ife9yPfgWSj37iIawLWzOmuuJ7jVdIB7Ee2B75bEisSEKyxRlTl6Y1Oq3owBgw==", | |
1986 | - "cpu": [ | |
1987 | - "arm64" | |
1988 | - ], | |
1989 | - "optional": true, | |
1990 | - "os": [ | |
1991 | - "darwin" | |
1992 | - ], | |
1993 | - "engines": { | |
1994 | - "node": ">=10" | |
1995 | - } | |
1996 | - }, | |
1997 | - "node_modules/@swc/core-darwin-x64": { | |
1998 | - "version": "1.4.17", | |
1999 | - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.4.17.tgz", | |
2000 | - "integrity": "sha512-WYRO9Fdzq4S/he8zjW5I95G1zcvyd9yyD3Tgi4/ic84P5XDlSMpBDpBLbr/dCPjmSg7aUXxNQqKqGkl6dQxYlA==", | |
2001 | - "cpu": [ | |
2002 | - "x64" | |
2003 | - ], | |
2004 | - "optional": true, | |
2005 | - "os": [ | |
2006 | - "darwin" | |
2007 | - ], | |
2008 | - "engines": { | |
2009 | - "node": ">=10" | |
2010 | - } | |
2011 | - }, | |
2012 | - "node_modules/@swc/core-linux-arm-gnueabihf": { | |
2013 | - "version": "1.4.17", | |
2014 | - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.4.17.tgz", | |
2015 | - "integrity": "sha512-cgbvpWOvtMH0XFjvwppUCR+Y+nf6QPaGu6AQ5hqCP+5Lv2zO5PG0RfasC4zBIjF53xgwEaaWmGP5/361P30X8Q==", | |
2016 | - "cpu": [ | |
2017 | - "arm" | |
2018 | - ], | |
2019 | - "optional": true, | |
2020 | - "os": [ | |
2021 | - "linux" | |
2022 | - ], | |
2023 | - "engines": { | |
2024 | - "node": ">=10" | |
2025 | - } | |
2026 | - }, | |
2027 | - "node_modules/@swc/core-linux-arm64-gnu": { | |
2028 | - "version": "1.4.17", | |
2029 | - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.4.17.tgz", | |
2030 | - "integrity": "sha512-l7zHgaIY24cF9dyQ/FOWbmZDsEj2a9gRFbmgx2u19e3FzOPuOnaopFj0fRYXXKCmtdx+anD750iBIYnTR+pq/Q==", | |
2031 | - "cpu": [ | |
2032 | - "arm64" | |
2033 | - ], | |
2034 | - "optional": true, | |
2035 | - "os": [ | |
2036 | - "linux" | |
2037 | - ], | |
2038 | - "engines": { | |
2039 | - "node": ">=10" | |
2040 | - } | |
2041 | - }, | |
2042 | - "node_modules/@swc/core-linux-arm64-musl": { | |
2043 | - "version": "1.4.17", | |
2044 | - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.4.17.tgz", | |
2045 | - "integrity": "sha512-qhH4gr9gAlVk8MBtzXbzTP3BJyqbAfUOATGkyUtohh85fPXQYuzVlbExix3FZXTwFHNidGHY8C+ocscI7uDaYw==", | |
2046 | - "cpu": [ | |
2047 | - "arm64" | |
2048 | - ], | |
2049 | - "optional": true, | |
2050 | - "os": [ | |
2051 | - "linux" | |
2052 | - ], | |
2053 | - "engines": { | |
2054 | - "node": ">=10" | |
2055 | - } | |
2056 | - }, | |
2057 | - "node_modules/@swc/core-linux-x64-gnu": { | |
2058 | - "version": "1.4.17", | |
2059 | - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.4.17.tgz", | |
2060 | - "integrity": "sha512-vRDFATL1oN5oZMImkwbgSHEkp8xG1ofEASBypze01W1Tqto8t+yo6gsp69wzCZBlxldsvPpvFZW55Jq0Rn+UnA==", | |
2061 | - "cpu": [ | |
2062 | - "x64" | |
2063 | - ], | |
2064 | - "optional": true, | |
2065 | - "os": [ | |
2066 | - "linux" | |
2067 | - ], | |
2068 | - "engines": { | |
2069 | - "node": ">=10" | |
2070 | - } | |
2071 | - }, | |
2072 | - "node_modules/@swc/core-linux-x64-musl": { | |
2073 | - "version": "1.4.17", | |
2074 | - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.4.17.tgz", | |
2075 | - "integrity": "sha512-zQNPXAXn3nmPqv54JVEN8k2JMEcMTQ6veVuU0p5O+A7KscJq+AGle/7ZQXzpXSfUCXlLMX4wvd+rwfGhh3J4cw==", | |
2076 | - "cpu": [ | |
2077 | - "x64" | |
2078 | - ], | |
2079 | - "optional": true, | |
2080 | - "os": [ | |
2081 | - "linux" | |
2082 | - ], | |
2083 | - "engines": { | |
2084 | - "node": ">=10" | |
2085 | - } | |
2086 | - }, | |
2087 | - "node_modules/@swc/core-win32-arm64-msvc": { | |
2088 | - "version": "1.4.17", | |
2089 | - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.4.17.tgz", | |
2090 | - "integrity": "sha512-z86n7EhOwyzxwm+DLE5NoLkxCTme2lq7QZlDjbQyfCxOt6isWz8rkW5QowTX8w9Rdmk34ncrjSLvnHOeLY17+w==", | |
2091 | - "cpu": [ | |
2092 | - "arm64" | |
2093 | - ], | |
2094 | - "optional": true, | |
2095 | - "os": [ | |
2096 | - "win32" | |
2097 | - ], | |
2098 | - "engines": { | |
2099 | - "node": ">=10" | |
2100 | - } | |
2101 | - }, | |
2102 | - "node_modules/@swc/core-win32-ia32-msvc": { | |
2103 | - "version": "1.4.17", | |
2104 | - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.4.17.tgz", | |
2105 | - "integrity": "sha512-JBwuSTJIgiJJX6wtr4wmXbfvOswHFj223AumUrK544QV69k60FJ9q2adPW9Csk+a8wm1hLxq4HKa2K334UHJ/g==", | |
2106 | - "cpu": [ | |
2107 | - "ia32" | |
2108 | - ], | |
2109 | - "optional": true, | |
2110 | - "os": [ | |
2111 | - "win32" | |
2112 | - ], | |
2113 | - "engines": { | |
2114 | - "node": ">=10" | |
2115 | - } | |
2116 | - }, | |
2117 | - "node_modules/@swc/core-win32-x64-msvc": { | |
2118 | - "version": "1.4.17", | |
2119 | - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.4.17.tgz", | |
2120 | - "integrity": "sha512-jFkOnGQamtVDBm3MF5Kq1lgW8vx4Rm1UvJWRUfg+0gx7Uc3Jp3QMFeMNw/rDNQYRDYPG3yunCC+2463ycd5+dg==", | |
2121 | - "cpu": [ | |
2122 | - "x64" | |
2123 | - ], | |
2124 | - "optional": true, | |
2125 | - "os": [ | |
2126 | - "win32" | |
2127 | - ], | |
2128 | - "engines": { | |
2129 | - "node": ">=10" | |
2130 | - } | |
2131 | - }, | |
2132 | - "node_modules/@swc/counter": { | |
2133 | - "version": "0.1.3", | |
2134 | - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", | |
2135 | - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" | |
2136 | - }, | |
2137 | - "node_modules/@swc/types": { | |
2138 | - "version": "0.1.6", | |
2139 | - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.6.tgz", | |
2140 | - "integrity": "sha512-/JLo/l2JsT/LRd80C3HfbmVpxOAJ11FO2RCEslFrgzLltoP9j8XIbsyDcfCt2WWyX+CM96rBoNM+IToAkFOugg==", | |
2141 | - "dependencies": { | |
2142 | - "@swc/counter": "^0.1.3" | |
2143 | - } | |
2144 | - }, | |
2145 | - "node_modules/@types/argparse": { | |
2146 | - "version": "1.0.38", | |
2147 | - "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz", | |
2148 | - "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==", | |
2149 | - "peer": true | |
2150 | - }, | |
2151 | - "node_modules/@types/estree": { | |
2152 | - "version": "1.0.5", | |
2153 | - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", | |
2154 | - "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" | |
2155 | - }, | |
2156 | - "node_modules/@types/format-util": { | |
2157 | - "version": "1.0.4", | |
2158 | - "resolved": "https://registry.npmjs.org/@types/format-util/-/format-util-1.0.4.tgz", | |
2159 | - "integrity": "sha512-xrCYOdHh5zA3LUrn6CvspYwlzSWxPso11Lx32WnAG6KvLCRecKZ/Rh21PLXUkzUFsQmrGcx/traJAFjR6dVS5Q==" | |
2160 | - }, | |
2161 | - "node_modules/@types/node": { | |
2162 | - "version": "20.12.12", | |
2163 | - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", | |
2164 | - "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", | |
2165 | - "optional": true, | |
2166 | - "peer": true, | |
2167 | - "dependencies": { | |
2168 | - "undici-types": "~5.26.4" | |
2169 | - } | |
2170 | - }, | |
2171 | - "node_modules/@types/parse-json": { | |
2172 | - "version": "4.0.2", | |
2173 | - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", | |
2174 | - "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" | |
2175 | - }, | |
2176 | - "node_modules/@types/prop-types": { | |
2177 | - "version": "15.7.12", | |
2178 | - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", | |
2179 | - "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" | |
2180 | - }, | |
2181 | - "node_modules/@types/react": { | |
2182 | - "version": "18.3.1", | |
2183 | - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.1.tgz", | |
2184 | - "integrity": "sha512-V0kuGBX3+prX+DQ/7r2qsv1NsdfnCLnTgnRJ1pYnxykBhGMz+qj+box5lq7XsO5mtZsBqpjwwTu/7wszPfMBcw==", | |
2185 | - "dependencies": { | |
2186 | - "@types/prop-types": "*", | |
2187 | - "csstype": "^3.0.2" | |
2188 | - } | |
2189 | - }, | |
2190 | - "node_modules/@types/react-dom": { | |
2191 | - "version": "18.3.0", | |
2192 | - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz", | |
2193 | - "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==", | |
2194 | - "dependencies": { | |
2195 | - "@types/react": "*" | |
2196 | - } | |
2197 | - }, | |
2198 | - "node_modules/@types/react-transition-group": { | |
2199 | - "version": "4.4.10", | |
2200 | - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", | |
2201 | - "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", | |
2202 | - "dependencies": { | |
2203 | - "@types/react": "*" | |
2204 | - } | |
2205 | - }, | |
2206 | - "node_modules/@types/stylis": { | |
2207 | - "version": "4.2.5", | |
2208 | - "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", | |
2209 | - "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==" | |
2210 | - }, | |
2211 | - "node_modules/@ungap/structured-clone": { | |
2212 | - "version": "1.2.0", | |
2213 | - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", | |
2214 | - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", | |
2215 | - "dev": true | |
2216 | - }, | |
2217 | - "node_modules/@vitejs/plugin-react-swc": { | |
2218 | - "version": "3.6.0", | |
2219 | - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.6.0.tgz", | |
2220 | - "integrity": "sha512-XFRbsGgpGxGzEV5i5+vRiro1bwcIaZDIdBRP16qwm+jP68ue/S8FJTBEgOeojtVDYrbSua3XFp71kC8VJE6v+g==", | |
2221 | - "dependencies": { | |
2222 | - "@swc/core": "^1.3.107" | |
2223 | - }, | |
2224 | - "peerDependencies": { | |
2225 | - "vite": "^4 || ^5" | |
2226 | - } | |
2227 | - }, | |
2228 | - "node_modules/@volar/language-core": { | |
2229 | - "version": "1.11.1", | |
2230 | - "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-1.11.1.tgz", | |
2231 | - "integrity": "sha512-dOcNn3i9GgZAcJt43wuaEykSluAuOkQgzni1cuxLxTV0nJKanQztp7FxyswdRILaKH+P2XZMPRp2S4MV/pElCw==", | |
2232 | - "peer": true, | |
2233 | - "dependencies": { | |
2234 | - "@volar/source-map": "1.11.1" | |
2235 | - } | |
2236 | - }, | |
2237 | - "node_modules/@volar/source-map": { | |
2238 | - "version": "1.11.1", | |
2239 | - "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-1.11.1.tgz", | |
2240 | - "integrity": "sha512-hJnOnwZ4+WT5iupLRnuzbULZ42L7BWWPMmruzwtLhJfpDVoZLjNBxHDi2sY2bgZXCKlpU5XcsMFoYrsQmPhfZg==", | |
2241 | - "peer": true, | |
2242 | - "dependencies": { | |
2243 | - "muggle-string": "^0.3.1" | |
2244 | - } | |
2245 | - }, | |
2246 | - "node_modules/@volar/typescript": { | |
2247 | - "version": "1.11.1", | |
2248 | - "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-1.11.1.tgz", | |
2249 | - "integrity": "sha512-iU+t2mas/4lYierSnoFOeRFQUhAEMgsFuQxoxvwn5EdQopw43j+J27a4lt9LMInx1gLJBC6qL14WYGlgymaSMQ==", | |
2250 | - "peer": true, | |
2251 | - "dependencies": { | |
2252 | - "@volar/language-core": "1.11.1", | |
2253 | - "path-browserify": "^1.0.1" | |
2254 | - } | |
2255 | - }, | |
2256 | - "node_modules/@vue/compiler-core": { | |
2257 | - "version": "3.4.26", | |
2258 | - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.26.tgz", | |
2259 | - "integrity": "sha512-N9Vil6Hvw7NaiyFUFBPXrAyETIGlQ8KcFMkyk6hW1Cl6NvoqvP+Y8p1Eqvx+UdqsnrnI9+HMUEJegzia3mhXmQ==", | |
2260 | - "peer": true, | |
2261 | - "dependencies": { | |
2262 | - "@babel/parser": "^7.24.4", | |
2263 | - "@vue/shared": "3.4.26", | |
2264 | - "entities": "^4.5.0", | |
2265 | - "estree-walker": "^2.0.2", | |
2266 | - "source-map-js": "^1.2.0" | |
2267 | - } | |
2268 | - }, | |
2269 | - "node_modules/@vue/compiler-dom": { | |
2270 | - "version": "3.4.26", | |
2271 | - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.26.tgz", | |
2272 | - "integrity": "sha512-4CWbR5vR9fMg23YqFOhr6t6WB1Fjt62d6xdFPyj8pxrYub7d+OgZaObMsoxaF9yBUHPMiPFK303v61PwAuGvZA==", | |
2273 | - "peer": true, | |
2274 | - "dependencies": { | |
2275 | - "@vue/compiler-core": "3.4.26", | |
2276 | - "@vue/shared": "3.4.26" | |
2277 | - } | |
2278 | - }, | |
2279 | - "node_modules/@vue/language-core": { | |
2280 | - "version": "1.8.27", | |
2281 | - "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-1.8.27.tgz", | |
2282 | - "integrity": "sha512-L8Kc27VdQserNaCUNiSFdDl9LWT24ly8Hpwf1ECy3aFb9m6bDhBGQYOujDm21N7EW3moKIOKEanQwe1q5BK+mA==", | |
2283 | - "peer": true, | |
2284 | - "dependencies": { | |
2285 | - "@volar/language-core": "~1.11.1", | |
2286 | - "@volar/source-map": "~1.11.1", | |
2287 | - "@vue/compiler-dom": "^3.3.0", | |
2288 | - "@vue/shared": "^3.3.0", | |
2289 | - "computeds": "^0.0.1", | |
2290 | - "minimatch": "^9.0.3", | |
2291 | - "muggle-string": "^0.3.1", | |
2292 | - "path-browserify": "^1.0.1", | |
2293 | - "vue-template-compiler": "^2.7.14" | |
2294 | - }, | |
2295 | - "peerDependencies": { | |
2296 | - "typescript": "*" | |
2297 | - }, | |
2298 | - "peerDependenciesMeta": { | |
2299 | - "typescript": { | |
2300 | - "optional": true | |
2301 | - } | |
2302 | - } | |
2303 | - }, | |
2304 | - "node_modules/@vue/language-core/node_modules/brace-expansion": { | |
2305 | - "version": "2.0.1", | |
2306 | - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", | |
2307 | - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", | |
2308 | - "peer": true, | |
2309 | - "dependencies": { | |
2310 | - "balanced-match": "^1.0.0" | |
2311 | - } | |
2312 | - }, | |
2313 | - "node_modules/@vue/language-core/node_modules/minimatch": { | |
2314 | - "version": "9.0.4", | |
2315 | - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", | |
2316 | - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", | |
2317 | - "peer": true, | |
2318 | - "dependencies": { | |
2319 | - "brace-expansion": "^2.0.1" | |
2320 | - }, | |
2321 | - "engines": { | |
2322 | - "node": ">=16 || 14 >=14.17" | |
2323 | - }, | |
2324 | - "funding": { | |
2325 | - "url": "https://github.com/sponsors/isaacs" | |
2326 | - } | |
2327 | - }, | |
2328 | - "node_modules/@vue/shared": { | |
2329 | - "version": "3.4.26", | |
2330 | - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.26.tgz", | |
2331 | - "integrity": "sha512-Fg4zwR0GNnjzodMt3KRy2AWGMKQXByl56+4HjN87soxLNU9P5xcJkstAlIeEF3cU6UYOzmJl1tV0dVPGIljCnQ==", | |
2332 | - "peer": true | |
2333 | - }, | |
2334 | - "node_modules/acorn": { | |
2335 | - "version": "8.11.3", | |
2336 | - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", | |
2337 | - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", | |
2338 | - "dev": true, | |
2339 | - "bin": { | |
2340 | - "acorn": "bin/acorn" | |
2341 | - }, | |
2342 | - "engines": { | |
2343 | - "node": ">=0.4.0" | |
2344 | - } | |
2345 | - }, | |
2346 | - "node_modules/acorn-jsx": { | |
2347 | - "version": "5.3.2", | |
2348 | - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", | |
2349 | - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", | |
2350 | - "dev": true, | |
2351 | - "peerDependencies": { | |
2352 | - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" | |
2353 | - } | |
2354 | - }, | |
2355 | - "node_modules/ajv": { | |
2356 | - "version": "6.12.6", | |
2357 | - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", | |
2358 | - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", | |
2359 | - "dependencies": { | |
2360 | - "fast-deep-equal": "^3.1.1", | |
2361 | - "fast-json-stable-stringify": "^2.0.0", | |
2362 | - "json-schema-traverse": "^0.4.1", | |
2363 | - "uri-js": "^4.2.2" | |
2364 | - }, | |
2365 | - "funding": { | |
2366 | - "type": "github", | |
2367 | - "url": "https://github.com/sponsors/epoberezkin" | |
2368 | - } | |
2369 | - }, | |
2370 | - "node_modules/ansi-regex": { | |
2371 | - "version": "5.0.1", | |
2372 | - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", | |
2373 | - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", | |
2374 | - "dev": true, | |
2375 | - "engines": { | |
2376 | - "node": ">=8" | |
2377 | - } | |
2378 | - }, | |
2379 | - "node_modules/ansi-styles": { | |
2380 | - "version": "4.3.0", | |
2381 | - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", | |
2382 | - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", | |
2383 | - "dev": true, | |
2384 | - "dependencies": { | |
2385 | - "color-convert": "^2.0.1" | |
2386 | - }, | |
2387 | - "engines": { | |
2388 | - "node": ">=8" | |
2389 | - }, | |
2390 | - "funding": { | |
2391 | - "url": "https://github.com/chalk/ansi-styles?sponsor=1" | |
2392 | - } | |
2393 | - }, | |
2394 | - "node_modules/any-promise": { | |
2395 | - "version": "1.3.0", | |
2396 | - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", | |
2397 | - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", | |
2398 | - "dev": true | |
2399 | - }, | |
2400 | - "node_modules/anymatch": { | |
2401 | - "version": "3.1.3", | |
2402 | - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", | |
2403 | - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", | |
2404 | - "dev": true, | |
2405 | - "dependencies": { | |
2406 | - "normalize-path": "^3.0.0", | |
2407 | - "picomatch": "^2.0.4" | |
2408 | - }, | |
2409 | - "engines": { | |
2410 | - "node": ">= 8" | |
2411 | - } | |
2412 | - }, | |
2413 | - "node_modules/archiver": { | |
2414 | - "version": "5.3.2", | |
2415 | - "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", | |
2416 | - "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", | |
2417 | - "dependencies": { | |
2418 | - "archiver-utils": "^2.1.0", | |
2419 | - "async": "^3.2.4", | |
2420 | - "buffer-crc32": "^0.2.1", | |
2421 | - "readable-stream": "^3.6.0", | |
2422 | - "readdir-glob": "^1.1.2", | |
2423 | - "tar-stream": "^2.2.0", | |
2424 | - "zip-stream": "^4.1.0" | |
2425 | - }, | |
2426 | - "engines": { | |
2427 | - "node": ">= 10" | |
2428 | - } | |
2429 | - }, | |
2430 | - "node_modules/archiver-utils": { | |
2431 | - "version": "2.1.0", | |
2432 | - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", | |
2433 | - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", | |
2434 | - "dependencies": { | |
2435 | - "glob": "^7.1.4", | |
2436 | - "graceful-fs": "^4.2.0", | |
2437 | - "lazystream": "^1.0.0", | |
2438 | - "lodash.defaults": "^4.2.0", | |
2439 | - "lodash.difference": "^4.5.0", | |
2440 | - "lodash.flatten": "^4.4.0", | |
2441 | - "lodash.isplainobject": "^4.0.6", | |
2442 | - "lodash.union": "^4.6.0", | |
2443 | - "normalize-path": "^3.0.0", | |
2444 | - "readable-stream": "^2.0.0" | |
2445 | - }, | |
2446 | - "engines": { | |
2447 | - "node": ">= 6" | |
2448 | - } | |
2449 | - }, | |
2450 | - "node_modules/archiver-utils/node_modules/isarray": { | |
2451 | - "version": "1.0.0", | |
2452 | - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", | |
2453 | - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" | |
2454 | - }, | |
2455 | - "node_modules/archiver-utils/node_modules/readable-stream": { | |
2456 | - "version": "2.3.8", | |
2457 | - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", | |
2458 | - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", | |
2459 | - "dependencies": { | |
2460 | - "core-util-is": "~1.0.0", | |
2461 | - "inherits": "~2.0.3", | |
2462 | - "isarray": "~1.0.0", | |
2463 | - "process-nextick-args": "~2.0.0", | |
2464 | - "safe-buffer": "~5.1.1", | |
2465 | - "string_decoder": "~1.1.1", | |
2466 | - "util-deprecate": "~1.0.1" | |
2467 | - } | |
2468 | - }, | |
2469 | - "node_modules/archiver-utils/node_modules/safe-buffer": { | |
2470 | - "version": "5.1.2", | |
2471 | - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", | |
2472 | - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" | |
2473 | - }, | |
2474 | - "node_modules/archiver-utils/node_modules/string_decoder": { | |
2475 | - "version": "1.1.1", | |
2476 | - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", | |
2477 | - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", | |
2478 | - "dependencies": { | |
2479 | - "safe-buffer": "~5.1.0" | |
2480 | - } | |
2481 | - }, | |
2482 | - "node_modules/arg": { | |
2483 | - "version": "5.0.2", | |
2484 | - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", | |
2485 | - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", | |
2486 | - "dev": true | |
2487 | - }, | |
2488 | - "node_modules/argparse": { | |
2489 | - "version": "2.0.1", | |
2490 | - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", | |
2491 | - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", | |
2492 | - "dev": true | |
2493 | - }, | |
2494 | - "node_modules/array-buffer-byte-length": { | |
2495 | - "version": "1.0.1", | |
2496 | - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", | |
2497 | - "integrity": "sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==", | |
2498 | - "dev": true, | |
2499 | - "dependencies": { | |
2500 | - "call-bind": "^1.0.5", | |
2501 | - "is-array-buffer": "^3.0.4" | |
2502 | - }, | |
2503 | - "engines": { | |
2504 | - "node": ">= 0.4" | |
2505 | - }, | |
2506 | - "funding": { | |
2507 | - "url": "https://github.com/sponsors/ljharb" | |
2508 | - } | |
2509 | - }, | |
2510 | - "node_modules/array-includes": { | |
2511 | - "version": "3.1.8", | |
2512 | - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", | |
2513 | - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", | |
2514 | - "dev": true, | |
2515 | - "dependencies": { | |
2516 | - "call-bind": "^1.0.7", | |
2517 | - "define-properties": "^1.2.1", | |
2518 | - "es-abstract": "^1.23.2", | |
2519 | - "es-object-atoms": "^1.0.0", | |
2520 | - "get-intrinsic": "^1.2.4", | |
2521 | - "is-string": "^1.0.7" | |
2522 | - }, | |
2523 | - "engines": { | |
2524 | - "node": ">= 0.4" | |
2525 | - }, | |
2526 | - "funding": { | |
2527 | - "url": "https://github.com/sponsors/ljharb" | |
2528 | - } | |
2529 | - }, | |
2530 | - "node_modules/array.prototype.findlast": { | |
2531 | - "version": "1.2.5", | |
2532 | - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", | |
2533 | - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", | |
2534 | - "dev": true, | |
2535 | - "dependencies": { | |
2536 | - "call-bind": "^1.0.7", | |
2537 | - "define-properties": "^1.2.1", | |
2538 | - "es-abstract": "^1.23.2", | |
2539 | - "es-errors": "^1.3.0", | |
2540 | - "es-object-atoms": "^1.0.0", | |
2541 | - "es-shim-unscopables": "^1.0.2" | |
2542 | - }, | |
2543 | - "engines": { | |
2544 | - "node": ">= 0.4" | |
2545 | - }, | |
2546 | - "funding": { | |
2547 | - "url": "https://github.com/sponsors/ljharb" | |
2548 | - } | |
2549 | - }, | |
2550 | - "node_modules/array.prototype.flat": { | |
2551 | - "version": "1.3.2", | |
2552 | - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", | |
2553 | - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", | |
2554 | - "dev": true, | |
2555 | - "dependencies": { | |
2556 | - "call-bind": "^1.0.2", | |
2557 | - "define-properties": "^1.2.0", | |
2558 | - "es-abstract": "^1.22.1", | |
2559 | - "es-shim-unscopables": "^1.0.0" | |
2560 | - }, | |
2561 | - "engines": { | |
2562 | - "node": ">= 0.4" | |
2563 | - }, | |
2564 | - "funding": { | |
2565 | - "url": "https://github.com/sponsors/ljharb" | |
2566 | - } | |
2567 | - }, | |
2568 | - "node_modules/array.prototype.flatmap": { | |
2569 | - "version": "1.3.2", | |
2570 | - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", | |
2571 | - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", | |
2572 | - "dev": true, | |
2573 | - "dependencies": { | |
2574 | - "call-bind": "^1.0.2", | |
2575 | - "define-properties": "^1.2.0", | |
2576 | - "es-abstract": "^1.22.1", | |
2577 | - "es-shim-unscopables": "^1.0.0" | |
2578 | - }, | |
2579 | - "engines": { | |
2580 | - "node": ">= 0.4" | |
2581 | - }, | |
2582 | - "funding": { | |
2583 | - "url": "https://github.com/sponsors/ljharb" | |
2584 | - } | |
2585 | - }, | |
2586 | - "node_modules/array.prototype.toreversed": { | |
2587 | - "version": "1.1.2", | |
2588 | - "resolved": "https://registry.npmjs.org/array.prototype.toreversed/-/array.prototype.toreversed-1.1.2.tgz", | |
2589 | - "integrity": "sha512-wwDCoT4Ck4Cz7sLtgUmzR5UV3YF5mFHUlbChCzZBQZ+0m2cl/DH3tKgvphv1nKgFsJ48oCSg6p91q2Vm0I/ZMA==", | |
2590 | - "dev": true, | |
2591 | - "dependencies": { | |
2592 | - "call-bind": "^1.0.2", | |
2593 | - "define-properties": "^1.2.0", | |
2594 | - "es-abstract": "^1.22.1", | |
2595 | - "es-shim-unscopables": "^1.0.0" | |
2596 | - } | |
2597 | - }, | |
2598 | - "node_modules/array.prototype.tosorted": { | |
2599 | - "version": "1.1.3", | |
2600 | - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.3.tgz", | |
2601 | - "integrity": "sha512-/DdH4TiTmOKzyQbp/eadcCVexiCb36xJg7HshYOYJnNZFDj33GEv0P7GxsynpShhq4OLYJzbGcBDkLsDt7MnNg==", | |
2602 | - "dev": true, | |
2603 | - "dependencies": { | |
2604 | - "call-bind": "^1.0.5", | |
2605 | - "define-properties": "^1.2.1", | |
2606 | - "es-abstract": "^1.22.3", | |
2607 | - "es-errors": "^1.1.0", | |
2608 | - "es-shim-unscopables": "^1.0.2" | |
2609 | - } | |
2610 | - }, | |
2611 | - "node_modules/arraybuffer.prototype.slice": { | |
2612 | - "version": "1.0.3", | |
2613 | - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz", | |
2614 | - "integrity": "sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A==", | |
2615 | - "dev": true, | |
2616 | - "dependencies": { | |
2617 | - "array-buffer-byte-length": "^1.0.1", | |
2618 | - "call-bind": "^1.0.5", | |
2619 | - "define-properties": "^1.2.1", | |
2620 | - "es-abstract": "^1.22.3", | |
2621 | - "es-errors": "^1.2.1", | |
2622 | - "get-intrinsic": "^1.2.3", | |
2623 | - "is-array-buffer": "^3.0.4", | |
2624 | - "is-shared-array-buffer": "^1.0.2" | |
2625 | - }, | |
2626 | - "engines": { | |
2627 | - "node": ">= 0.4" | |
2628 | - }, | |
2629 | - "funding": { | |
2630 | - "url": "https://github.com/sponsors/ljharb" | |
2631 | - } | |
2632 | - }, | |
2633 | - "node_modules/async": { | |
2634 | - "version": "3.2.5", | |
2635 | - "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", | |
2636 | - "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" | |
2637 | - }, | |
2638 | - "node_modules/asynckit": { | |
2639 | - "version": "0.4.0", | |
2640 | - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", | |
2641 | - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" | |
2642 | - }, | |
2643 | - "node_modules/autoprefixer": { | |
2644 | - "version": "10.4.19", | |
2645 | - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", | |
2646 | - "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", | |
2647 | - "dev": true, | |
2648 | - "funding": [ | |
2649 | - { | |
2650 | - "type": "opencollective", | |
2651 | - "url": "https://opencollective.com/postcss/" | |
2652 | - }, | |
2653 | - { | |
2654 | - "type": "tidelift", | |
2655 | - "url": "https://tidelift.com/funding/github/npm/autoprefixer" | |
2656 | - }, | |
2657 | - { | |
2658 | - "type": "github", | |
2659 | - "url": "https://github.com/sponsors/ai" | |
2660 | - } | |
2661 | - ], | |
2662 | - "dependencies": { | |
2663 | - "browserslist": "^4.23.0", | |
2664 | - "caniuse-lite": "^1.0.30001599", | |
2665 | - "fraction.js": "^4.3.7", | |
2666 | - "normalize-range": "^0.1.2", | |
2667 | - "picocolors": "^1.0.0", | |
2668 | - "postcss-value-parser": "^4.2.0" | |
2669 | - }, | |
2670 | - "bin": { | |
2671 | - "autoprefixer": "bin/autoprefixer" | |
2672 | - }, | |
2673 | - "engines": { | |
2674 | - "node": "^10 || ^12 || >=14" | |
2675 | - }, | |
2676 | - "peerDependencies": { | |
2677 | - "postcss": "^8.1.0" | |
2678 | - } | |
2679 | - }, | |
2680 | - "node_modules/available-typed-arrays": { | |
2681 | - "version": "1.0.7", | |
2682 | - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", | |
2683 | - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", | |
2684 | - "dev": true, | |
2685 | - "dependencies": { | |
2686 | - "possible-typed-array-names": "^1.0.0" | |
2687 | - }, | |
2688 | - "engines": { | |
2689 | - "node": ">= 0.4" | |
2690 | - }, | |
2691 | - "funding": { | |
2692 | - "url": "https://github.com/sponsors/ljharb" | |
2693 | - } | |
2694 | - }, | |
2695 | - "node_modules/axios": { | |
2696 | - "version": "1.6.8", | |
2697 | - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", | |
2698 | - "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", | |
2699 | - "dependencies": { | |
2700 | - "follow-redirects": "^1.15.6", | |
2701 | - "form-data": "^4.0.0", | |
2702 | - "proxy-from-env": "^1.1.0" | |
2703 | - } | |
2704 | - }, | |
2705 | - "node_modules/babel-plugin-macros": { | |
2706 | - "version": "3.1.0", | |
2707 | - "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", | |
2708 | - "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", | |
2709 | - "dependencies": { | |
2710 | - "@babel/runtime": "^7.12.5", | |
2711 | - "cosmiconfig": "^7.0.0", | |
2712 | - "resolve": "^1.19.0" | |
2713 | - }, | |
2714 | - "engines": { | |
2715 | - "node": ">=10", | |
2716 | - "npm": ">=6" | |
2717 | - } | |
2718 | - }, | |
2719 | - "node_modules/babel-plugin-macros/node_modules/resolve": { | |
2720 | - "version": "1.22.8", | |
2721 | - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", | |
2722 | - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", | |
2723 | - "dependencies": { | |
2724 | - "is-core-module": "^2.13.0", | |
2725 | - "path-parse": "^1.0.7", | |
2726 | - "supports-preserve-symlinks-flag": "^1.0.0" | |
2727 | - }, | |
2728 | - "bin": { | |
2729 | - "resolve": "bin/resolve" | |
2730 | - }, | |
2731 | - "funding": { | |
2732 | - "url": "https://github.com/sponsors/ljharb" | |
2733 | - } | |
2734 | - }, | |
2735 | - "node_modules/balanced-match": { | |
2736 | - "version": "1.0.2", | |
2737 | - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", | |
2738 | - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" | |
2739 | - }, | |
2740 | - "node_modules/base64-js": { | |
2741 | - "version": "1.5.1", | |
2742 | - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", | |
2743 | - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", | |
2744 | - "funding": [ | |
2745 | - { | |
2746 | - "type": "github", | |
2747 | - "url": "https://github.com/sponsors/feross" | |
2748 | - }, | |
2749 | - { | |
2750 | - "type": "patreon", | |
2751 | - "url": "https://www.patreon.com/feross" | |
2752 | - }, | |
2753 | - { | |
2754 | - "type": "consulting", | |
2755 | - "url": "https://feross.org/support" | |
2756 | - } | |
2757 | - ] | |
2758 | - }, | |
2759 | - "node_modules/big-integer": { | |
2760 | - "version": "1.6.52", | |
2761 | - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", | |
2762 | - "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", | |
2763 | - "engines": { | |
2764 | - "node": ">=0.6" | |
2765 | - } | |
2766 | - }, | |
2767 | - "node_modules/binary": { | |
2768 | - "version": "0.3.0", | |
2769 | - "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", | |
2770 | - "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", | |
2771 | - "dependencies": { | |
2772 | - "buffers": "~0.1.1", | |
2773 | - "chainsaw": "~0.1.0" | |
2774 | - }, | |
2775 | - "engines": { | |
2776 | - "node": "*" | |
2777 | - } | |
2778 | - }, | |
2779 | - "node_modules/binary-extensions": { | |
2780 | - "version": "2.3.0", | |
2781 | - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", | |
2782 | - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", | |
2783 | - "dev": true, | |
2784 | - "engines": { | |
2785 | - "node": ">=8" | |
2786 | - }, | |
2787 | - "funding": { | |
2788 | - "url": "https://github.com/sponsors/sindresorhus" | |
2789 | - } | |
2790 | - }, | |
2791 | - "node_modules/bl": { | |
2792 | - "version": "4.1.0", | |
2793 | - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", | |
2794 | - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", | |
2795 | - "dependencies": { | |
2796 | - "buffer": "^5.5.0", | |
2797 | - "inherits": "^2.0.4", | |
2798 | - "readable-stream": "^3.4.0" | |
2799 | - } | |
2800 | - }, | |
2801 | - "node_modules/bluebird": { | |
2802 | - "version": "3.4.7", | |
2803 | - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", | |
2804 | - "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==" | |
2805 | - }, | |
2806 | - "node_modules/brace-expansion": { | |
2807 | - "version": "1.1.11", | |
2808 | - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", | |
2809 | - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", | |
2810 | - "dependencies": { | |
2811 | - "balanced-match": "^1.0.0", | |
2812 | - "concat-map": "0.0.1" | |
2813 | - } | |
2814 | - }, | |
2815 | - "node_modules/braces": { | |
2816 | - "version": "3.0.2", | |
2817 | - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", | |
2818 | - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", | |
2819 | - "dev": true, | |
2820 | - "dependencies": { | |
2821 | - "fill-range": "^7.0.1" | |
2822 | - }, | |
2823 | - "engines": { | |
2824 | - "node": ">=8" | |
2825 | - } | |
2826 | - }, | |
2827 | - "node_modules/browserslist": { | |
2828 | - "version": "4.23.0", | |
2829 | - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", | |
2830 | - "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", | |
2831 | - "dev": true, | |
2832 | - "funding": [ | |
2833 | - { | |
2834 | - "type": "opencollective", | |
2835 | - "url": "https://opencollective.com/browserslist" | |
2836 | - }, | |
2837 | - { | |
2838 | - "type": "tidelift", | |
2839 | - "url": "https://tidelift.com/funding/github/npm/browserslist" | |
2840 | - }, | |
2841 | - { | |
2842 | - "type": "github", | |
2843 | - "url": "https://github.com/sponsors/ai" | |
2844 | - } | |
2845 | - ], | |
2846 | - "dependencies": { | |
2847 | - "caniuse-lite": "^1.0.30001587", | |
2848 | - "electron-to-chromium": "^1.4.668", | |
2849 | - "node-releases": "^2.0.14", | |
2850 | - "update-browserslist-db": "^1.0.13" | |
2851 | - }, | |
2852 | - "bin": { | |
2853 | - "browserslist": "cli.js" | |
2854 | - }, | |
2855 | - "engines": { | |
2856 | - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" | |
2857 | - } | |
2858 | - }, | |
2859 | - "node_modules/buffer": { | |
2860 | - "version": "5.7.1", | |
2861 | - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", | |
2862 | - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", | |
2863 | - "funding": [ | |
2864 | - { | |
2865 | - "type": "github", | |
2866 | - "url": "https://github.com/sponsors/feross" | |
2867 | - }, | |
2868 | - { | |
2869 | - "type": "patreon", | |
2870 | - "url": "https://www.patreon.com/feross" | |
2871 | - }, | |
2872 | - { | |
2873 | - "type": "consulting", | |
2874 | - "url": "https://feross.org/support" | |
2875 | - } | |
2876 | - ], | |
2877 | - "dependencies": { | |
2878 | - "base64-js": "^1.3.1", | |
2879 | - "ieee754": "^1.1.13" | |
2880 | - } | |
2881 | - }, | |
2882 | - "node_modules/buffer-crc32": { | |
2883 | - "version": "0.2.13", | |
2884 | - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", | |
2885 | - "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", | |
2886 | - "engines": { | |
2887 | - "node": "*" | |
2888 | - } | |
2889 | - }, | |
2890 | - "node_modules/buffer-indexof-polyfill": { | |
2891 | - "version": "1.0.2", | |
2892 | - "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", | |
2893 | - "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", | |
2894 | - "engines": { | |
2895 | - "node": ">=0.10" | |
2896 | - } | |
2897 | - }, | |
2898 | - "node_modules/buffers": { | |
2899 | - "version": "0.1.1", | |
2900 | - "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", | |
2901 | - "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", | |
2902 | - "engines": { | |
2903 | - "node": ">=0.2.0" | |
2904 | - } | |
2905 | - }, | |
2906 | - "node_modules/call-bind": { | |
2907 | - "version": "1.0.7", | |
2908 | - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", | |
2909 | - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", | |
2910 | - "dev": true, | |
2911 | - "dependencies": { | |
2912 | - "es-define-property": "^1.0.0", | |
2913 | - "es-errors": "^1.3.0", | |
2914 | - "function-bind": "^1.1.2", | |
2915 | - "get-intrinsic": "^1.2.4", | |
2916 | - "set-function-length": "^1.2.1" | |
2917 | - }, | |
2918 | - "engines": { | |
2919 | - "node": ">= 0.4" | |
2920 | - }, | |
2921 | - "funding": { | |
2922 | - "url": "https://github.com/sponsors/ljharb" | |
2923 | - } | |
2924 | - }, | |
2925 | - "node_modules/callsites": { | |
2926 | - "version": "3.1.0", | |
2927 | - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", | |
2928 | - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", | |
2929 | - "engines": { | |
2930 | - "node": ">=6" | |
2931 | - } | |
2932 | - }, | |
2933 | - "node_modules/camelcase-css": { | |
2934 | - "version": "2.0.1", | |
2935 | - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", | |
2936 | - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", | |
2937 | - "dev": true, | |
2938 | - "engines": { | |
2939 | - "node": ">= 6" | |
2940 | - } | |
2941 | - }, | |
2942 | - "node_modules/camelize": { | |
2943 | - "version": "1.0.1", | |
2944 | - "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", | |
2945 | - "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", | |
2946 | - "funding": { | |
2947 | - "url": "https://github.com/sponsors/ljharb" | |
2948 | - } | |
2949 | - }, | |
2950 | - "node_modules/caniuse-lite": { | |
2951 | - "version": "1.0.30001614", | |
2952 | - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001614.tgz", | |
2953 | - "integrity": "sha512-jmZQ1VpmlRwHgdP1/uiKzgiAuGOfLEJsYFP4+GBou/QQ4U6IOJCB4NP1c+1p9RGLpwObcT94jA5/uO+F1vBbog==", | |
2954 | - "dev": true, | |
2955 | - "funding": [ | |
2956 | - { | |
2957 | - "type": "opencollective", | |
2958 | - "url": "https://opencollective.com/browserslist" | |
2959 | - }, | |
2960 | - { | |
2961 | - "type": "tidelift", | |
2962 | - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" | |
2963 | - }, | |
2964 | - { | |
2965 | - "type": "github", | |
2966 | - "url": "https://github.com/sponsors/ai" | |
2967 | - } | |
2968 | - ] | |
2969 | - }, | |
2970 | - "node_modules/chainsaw": { | |
2971 | - "version": "0.1.0", | |
2972 | - "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", | |
2973 | - "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", | |
2974 | - "dependencies": { | |
2975 | - "traverse": ">=0.3.0 <0.4" | |
2976 | - }, | |
2977 | - "engines": { | |
2978 | - "node": "*" | |
2979 | - } | |
2980 | - }, | |
2981 | - "node_modules/chalk": { | |
2982 | - "version": "4.1.2", | |
2983 | - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", | |
2984 | - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", | |
2985 | - "dev": true, | |
2986 | - "dependencies": { | |
2987 | - "ansi-styles": "^4.1.0", | |
2988 | - "supports-color": "^7.1.0" | |
2989 | - }, | |
2990 | - "engines": { | |
2991 | - "node": ">=10" | |
2992 | - }, | |
2993 | - "funding": { | |
2994 | - "url": "https://github.com/chalk/chalk?sponsor=1" | |
2995 | - } | |
2996 | - }, | |
2997 | - "node_modules/chance": { | |
2998 | - "version": "1.1.11", | |
2999 | - "resolved": "https://registry.npmjs.org/chance/-/chance-1.1.11.tgz", | |
3000 | - "integrity": "sha512-kqTg3WWywappJPqtgrdvbA380VoXO2eu9VCV895JgbyHsaErXdyHK9LOZ911OvAk6L0obK7kDk9CGs8+oBawVA==" | |
3001 | - }, | |
3002 | - "node_modules/chokidar": { | |
3003 | - "version": "3.6.0", | |
3004 | - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", | |
3005 | - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", | |
3006 | - "dev": true, | |
3007 | - "dependencies": { | |
3008 | - "anymatch": "~3.1.2", | |
3009 | - "braces": "~3.0.2", | |
3010 | - "glob-parent": "~5.1.2", | |
3011 | - "is-binary-path": "~2.1.0", | |
3012 | - "is-glob": "~4.0.1", | |
3013 | - "normalize-path": "~3.0.0", | |
3014 | - "readdirp": "~3.6.0" | |
3015 | - }, | |
3016 | - "engines": { | |
3017 | - "node": ">= 8.10.0" | |
3018 | - }, | |
3019 | - "funding": { | |
3020 | - "url": "https://paulmillr.com/funding/" | |
3021 | - }, | |
3022 | - "optionalDependencies": { | |
3023 | - "fsevents": "~2.3.2" | |
3024 | - } | |
3025 | - }, | |
3026 | - "node_modules/chokidar/node_modules/glob-parent": { | |
3027 | - "version": "5.1.2", | |
3028 | - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", | |
3029 | - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", | |
3030 | - "dev": true, | |
3031 | - "dependencies": { | |
3032 | - "is-glob": "^4.0.1" | |
3033 | - }, | |
3034 | - "engines": { | |
3035 | - "node": ">= 6" | |
3036 | - } | |
3037 | - }, | |
3038 | - "node_modules/clsx": { | |
3039 | - "version": "2.1.1", | |
3040 | - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", | |
3041 | - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", | |
3042 | - "engines": { | |
3043 | - "node": ">=6" | |
3044 | - } | |
3045 | - }, | |
3046 | - "node_modules/color-convert": { | |
3047 | - "version": "2.0.1", | |
3048 | - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", | |
3049 | - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", | |
3050 | - "dev": true, | |
3051 | - "dependencies": { | |
3052 | - "color-name": "~1.1.4" | |
3053 | - }, | |
3054 | - "engines": { | |
3055 | - "node": ">=7.0.0" | |
3056 | - } | |
3057 | - }, | |
3058 | - "node_modules/color-name": { | |
3059 | - "version": "1.1.4", | |
3060 | - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", | |
3061 | - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", | |
3062 | - "dev": true | |
3063 | - }, | |
3064 | - "node_modules/combined-stream": { | |
3065 | - "version": "1.0.8", | |
3066 | - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", | |
3067 | - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", | |
3068 | - "dependencies": { | |
3069 | - "delayed-stream": "~1.0.0" | |
3070 | - }, | |
3071 | - "engines": { | |
3072 | - "node": ">= 0.8" | |
3073 | - } | |
3074 | - }, | |
3075 | - "node_modules/commander": { | |
3076 | - "version": "9.5.0", | |
3077 | - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", | |
3078 | - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", | |
3079 | - "optional": true, | |
3080 | - "peer": true, | |
3081 | - "engines": { | |
3082 | - "node": "^12.20.0 || >=14" | |
3083 | - } | |
3084 | - }, | |
3085 | - "node_modules/compress-commons": { | |
3086 | - "version": "4.1.2", | |
3087 | - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", | |
3088 | - "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", | |
3089 | - "dependencies": { | |
3090 | - "buffer-crc32": "^0.2.13", | |
3091 | - "crc32-stream": "^4.0.2", | |
3092 | - "normalize-path": "^3.0.0", | |
3093 | - "readable-stream": "^3.6.0" | |
3094 | - }, | |
3095 | - "engines": { | |
3096 | - "node": ">= 10" | |
3097 | - } | |
3098 | - }, | |
3099 | - "node_modules/computeds": { | |
3100 | - "version": "0.0.1", | |
3101 | - "resolved": "https://registry.npmjs.org/computeds/-/computeds-0.0.1.tgz", | |
3102 | - "integrity": "sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==", | |
3103 | - "peer": true | |
3104 | - }, | |
3105 | - "node_modules/concat-map": { | |
3106 | - "version": "0.0.1", | |
3107 | - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", | |
3108 | - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" | |
3109 | - }, | |
3110 | - "node_modules/convert-source-map": { | |
3111 | - "version": "1.9.0", | |
3112 | - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", | |
3113 | - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" | |
3114 | - }, | |
3115 | - "node_modules/core-util-is": { | |
3116 | - "version": "1.0.3", | |
3117 | - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", | |
3118 | - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" | |
3119 | - }, | |
3120 | - "node_modules/cosmiconfig": { | |
3121 | - "version": "7.1.0", | |
3122 | - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", | |
3123 | - "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", | |
3124 | - "dependencies": { | |
3125 | - "@types/parse-json": "^4.0.0", | |
3126 | - "import-fresh": "^3.2.1", | |
3127 | - "parse-json": "^5.0.0", | |
3128 | - "path-type": "^4.0.0", | |
3129 | - "yaml": "^1.10.0" | |
3130 | - }, | |
3131 | - "engines": { | |
3132 | - "node": ">=10" | |
3133 | - } | |
3134 | - }, | |
3135 | - "node_modules/crc-32": { | |
3136 | - "version": "1.2.2", | |
3137 | - "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", | |
3138 | - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", | |
3139 | - "bin": { | |
3140 | - "crc32": "bin/crc32.njs" | |
3141 | - }, | |
3142 | - "engines": { | |
3143 | - "node": ">=0.8" | |
3144 | - } | |
3145 | - }, | |
3146 | - "node_modules/crc32-stream": { | |
3147 | - "version": "4.0.3", | |
3148 | - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", | |
3149 | - "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", | |
3150 | - "dependencies": { | |
3151 | - "crc-32": "^1.2.0", | |
3152 | - "readable-stream": "^3.4.0" | |
3153 | - }, | |
3154 | - "engines": { | |
3155 | - "node": ">= 10" | |
3156 | - } | |
3157 | - }, | |
3158 | - "node_modules/cross-spawn": { | |
3159 | - "version": "7.0.3", | |
3160 | - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", | |
3161 | - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", | |
3162 | - "dev": true, | |
3163 | - "dependencies": { | |
3164 | - "path-key": "^3.1.0", | |
3165 | - "shebang-command": "^2.0.0", | |
3166 | - "which": "^2.0.1" | |
3167 | - }, | |
3168 | - "engines": { | |
3169 | - "node": ">= 8" | |
3170 | - } | |
3171 | - }, | |
3172 | - "node_modules/css-color-keywords": { | |
3173 | - "version": "1.0.0", | |
3174 | - "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", | |
3175 | - "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", | |
3176 | - "engines": { | |
3177 | - "node": ">=4" | |
3178 | - } | |
3179 | - }, | |
3180 | - "node_modules/css-to-react-native": { | |
3181 | - "version": "3.2.0", | |
3182 | - "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", | |
3183 | - "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", | |
3184 | - "dependencies": { | |
3185 | - "camelize": "^1.0.0", | |
3186 | - "css-color-keywords": "^1.0.0", | |
3187 | - "postcss-value-parser": "^4.0.2" | |
3188 | - } | |
3189 | - }, | |
3190 | - "node_modules/cssesc": { | |
3191 | - "version": "3.0.0", | |
3192 | - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", | |
3193 | - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", | |
3194 | - "dev": true, | |
3195 | - "bin": { | |
3196 | - "cssesc": "bin/cssesc" | |
3197 | - }, | |
3198 | - "engines": { | |
3199 | - "node": ">=4" | |
3200 | - } | |
3201 | - }, | |
3202 | - "node_modules/csstype": { | |
3203 | - "version": "3.1.3", | |
3204 | - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", | |
3205 | - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" | |
3206 | - }, | |
3207 | - "node_modules/data-view-buffer": { | |
3208 | - "version": "1.0.1", | |
3209 | - "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", | |
3210 | - "integrity": "sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==", | |
3211 | - "dev": true, | |
3212 | - "dependencies": { | |
3213 | - "call-bind": "^1.0.6", | |
3214 | - "es-errors": "^1.3.0", | |
3215 | - "is-data-view": "^1.0.1" | |
3216 | - }, | |
3217 | - "engines": { | |
3218 | - "node": ">= 0.4" | |
3219 | - }, | |
3220 | - "funding": { | |
3221 | - "url": "https://github.com/sponsors/ljharb" | |
3222 | - } | |
3223 | - }, | |
3224 | - "node_modules/data-view-byte-length": { | |
3225 | - "version": "1.0.1", | |
3226 | - "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz", | |
3227 | - "integrity": "sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ==", | |
3228 | - "dev": true, | |
3229 | - "dependencies": { | |
3230 | - "call-bind": "^1.0.7", | |
3231 | - "es-errors": "^1.3.0", | |
3232 | - "is-data-view": "^1.0.1" | |
3233 | - }, | |
3234 | - "engines": { | |
3235 | - "node": ">= 0.4" | |
3236 | - }, | |
3237 | - "funding": { | |
3238 | - "url": "https://github.com/sponsors/ljharb" | |
3239 | - } | |
3240 | - }, | |
3241 | - "node_modules/data-view-byte-offset": { | |
3242 | - "version": "1.0.0", | |
3243 | - "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz", | |
3244 | - "integrity": "sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA==", | |
3245 | - "dev": true, | |
3246 | - "dependencies": { | |
3247 | - "call-bind": "^1.0.6", | |
3248 | - "es-errors": "^1.3.0", | |
3249 | - "is-data-view": "^1.0.1" | |
3250 | - }, | |
3251 | - "engines": { | |
3252 | - "node": ">= 0.4" | |
3253 | - }, | |
3254 | - "funding": { | |
3255 | - "url": "https://github.com/sponsors/ljharb" | |
3256 | - } | |
3257 | - }, | |
3258 | - "node_modules/dayjs": { | |
3259 | - "version": "1.11.11", | |
3260 | - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.11.tgz", | |
3261 | - "integrity": "sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==" | |
3262 | - }, | |
3263 | - "node_modules/de-indent": { | |
3264 | - "version": "1.0.2", | |
3265 | - "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", | |
3266 | - "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", | |
3267 | - "peer": true | |
3268 | - }, | |
3269 | - "node_modules/debug": { | |
3270 | - "version": "4.3.4", | |
3271 | - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", | |
3272 | - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", | |
3273 | - "dependencies": { | |
3274 | - "ms": "2.1.2" | |
3275 | - }, | |
3276 | - "engines": { | |
3277 | - "node": ">=6.0" | |
3278 | - }, | |
3279 | - "peerDependenciesMeta": { | |
3280 | - "supports-color": { | |
3281 | - "optional": true | |
3282 | - } | |
3283 | - } | |
3284 | - }, | |
3285 | - "node_modules/deep-is": { | |
3286 | - "version": "0.1.4", | |
3287 | - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", | |
3288 | - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", | |
3289 | - "dev": true | |
3290 | - }, | |
3291 | - "node_modules/define-data-property": { | |
3292 | - "version": "1.1.4", | |
3293 | - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", | |
3294 | - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", | |
3295 | - "dev": true, | |
3296 | - "dependencies": { | |
3297 | - "es-define-property": "^1.0.0", | |
3298 | - "es-errors": "^1.3.0", | |
3299 | - "gopd": "^1.0.1" | |
3300 | - }, | |
3301 | - "engines": { | |
3302 | - "node": ">= 0.4" | |
3303 | - }, | |
3304 | - "funding": { | |
3305 | - "url": "https://github.com/sponsors/ljharb" | |
3306 | - } | |
3307 | - }, | |
3308 | - "node_modules/define-properties": { | |
3309 | - "version": "1.2.1", | |
3310 | - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", | |
3311 | - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", | |
3312 | - "dev": true, | |
3313 | - "dependencies": { | |
3314 | - "define-data-property": "^1.0.1", | |
3315 | - "has-property-descriptors": "^1.0.0", | |
3316 | - "object-keys": "^1.1.1" | |
3317 | - }, | |
3318 | - "engines": { | |
3319 | - "node": ">= 0.4" | |
3320 | - }, | |
3321 | - "funding": { | |
3322 | - "url": "https://github.com/sponsors/ljharb" | |
3323 | - } | |
3324 | - }, | |
3325 | - "node_modules/delayed-stream": { | |
3326 | - "version": "1.0.0", | |
3327 | - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", | |
3328 | - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", | |
3329 | - "engines": { | |
3330 | - "node": ">=0.4.0" | |
3331 | - } | |
3332 | - }, | |
3333 | - "node_modules/didyoumean": { | |
3334 | - "version": "1.2.2", | |
3335 | - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", | |
3336 | - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", | |
3337 | - "dev": true | |
3338 | - }, | |
3339 | - "node_modules/dlv": { | |
3340 | - "version": "1.1.3", | |
3341 | - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", | |
3342 | - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", | |
3343 | - "dev": true | |
3344 | - }, | |
3345 | - "node_modules/doctrine": { | |
3346 | - "version": "3.0.0", | |
3347 | - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", | |
3348 | - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", | |
3349 | - "dev": true, | |
3350 | - "dependencies": { | |
3351 | - "esutils": "^2.0.2" | |
3352 | - }, | |
3353 | - "engines": { | |
3354 | - "node": ">=6.0.0" | |
3355 | - } | |
3356 | - }, | |
3357 | - "node_modules/dom-helpers": { | |
3358 | - "version": "5.2.1", | |
3359 | - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", | |
3360 | - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", | |
3361 | - "dependencies": { | |
3362 | - "@babel/runtime": "^7.8.7", | |
3363 | - "csstype": "^3.0.2" | |
3364 | - } | |
3365 | - }, | |
3366 | - "node_modules/duplexer2": { | |
3367 | - "version": "0.1.4", | |
3368 | - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", | |
3369 | - "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", | |
3370 | - "dependencies": { | |
3371 | - "readable-stream": "^2.0.2" | |
3372 | - } | |
3373 | - }, | |
3374 | - "node_modules/duplexer2/node_modules/isarray": { | |
3375 | - "version": "1.0.0", | |
3376 | - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", | |
3377 | - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" | |
3378 | - }, | |
3379 | - "node_modules/duplexer2/node_modules/readable-stream": { | |
3380 | - "version": "2.3.8", | |
3381 | - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", | |
3382 | - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", | |
3383 | - "dependencies": { | |
3384 | - "core-util-is": "~1.0.0", | |
3385 | - "inherits": "~2.0.3", | |
3386 | - "isarray": "~1.0.0", | |
3387 | - "process-nextick-args": "~2.0.0", | |
3388 | - "safe-buffer": "~5.1.1", | |
3389 | - "string_decoder": "~1.1.1", | |
3390 | - "util-deprecate": "~1.0.1" | |
3391 | - } | |
3392 | - }, | |
3393 | - "node_modules/duplexer2/node_modules/safe-buffer": { | |
3394 | - "version": "5.1.2", | |
3395 | - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", | |
3396 | - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" | |
3397 | - }, | |
3398 | - "node_modules/duplexer2/node_modules/string_decoder": { | |
3399 | - "version": "1.1.1", | |
3400 | - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", | |
3401 | - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", | |
3402 | - "dependencies": { | |
3403 | - "safe-buffer": "~5.1.0" | |
3404 | - } | |
3405 | - }, | |
3406 | - "node_modules/eastasianwidth": { | |
3407 | - "version": "0.2.0", | |
3408 | - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", | |
3409 | - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", | |
3410 | - "dev": true | |
3411 | - }, | |
3412 | - "node_modules/electron-to-chromium": { | |
3413 | - "version": "1.4.752", | |
3414 | - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.752.tgz", | |
3415 | - "integrity": "sha512-P3QJreYI/AUTcfBVrC4zy9KvnZWekViThgQMX/VpJ+IsOBbcX5JFpORM4qWapwWQ+agb2nYAOyn/4PMXOk0m2Q==", | |
3416 | - "dev": true | |
3417 | - }, | |
3418 | - "node_modules/emoji-regex": { | |
3419 | - "version": "9.2.2", | |
3420 | - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", | |
3421 | - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", | |
3422 | - "dev": true | |
3423 | - }, | |
3424 | - "node_modules/end-of-stream": { | |
3425 | - "version": "1.4.4", | |
3426 | - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", | |
3427 | - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", | |
3428 | - "dependencies": { | |
3429 | - "once": "^1.4.0" | |
3430 | - } | |
3431 | - }, | |
3432 | - "node_modules/entities": { | |
3433 | - "version": "4.5.0", | |
3434 | - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", | |
3435 | - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", | |
3436 | - "peer": true, | |
3437 | - "engines": { | |
3438 | - "node": ">=0.12" | |
3439 | - }, | |
3440 | - "funding": { | |
3441 | - "url": "https://github.com/fb55/entities?sponsor=1" | |
3442 | - } | |
3443 | - }, | |
3444 | - "node_modules/error-ex": { | |
3445 | - "version": "1.3.2", | |
3446 | - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", | |
3447 | - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", | |
3448 | - "dependencies": { | |
3449 | - "is-arrayish": "^0.2.1" | |
3450 | - } | |
3451 | - }, | |
3452 | - "node_modules/es-abstract": { | |
3453 | - "version": "1.23.3", | |
3454 | - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", | |
3455 | - "integrity": "sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A==", | |
3456 | - "dev": true, | |
3457 | - "dependencies": { | |
3458 | - "array-buffer-byte-length": "^1.0.1", | |
3459 | - "arraybuffer.prototype.slice": "^1.0.3", | |
3460 | - "available-typed-arrays": "^1.0.7", | |
3461 | - "call-bind": "^1.0.7", | |
3462 | - "data-view-buffer": "^1.0.1", | |
3463 | - "data-view-byte-length": "^1.0.1", | |
3464 | - "data-view-byte-offset": "^1.0.0", | |
3465 | - "es-define-property": "^1.0.0", | |
3466 | - "es-errors": "^1.3.0", | |
3467 | - "es-object-atoms": "^1.0.0", | |
3468 | - "es-set-tostringtag": "^2.0.3", | |
3469 | - "es-to-primitive": "^1.2.1", | |
3470 | - "function.prototype.name": "^1.1.6", | |
3471 | - "get-intrinsic": "^1.2.4", | |
3472 | - "get-symbol-description": "^1.0.2", | |
3473 | - "globalthis": "^1.0.3", | |
3474 | - "gopd": "^1.0.1", | |
3475 | - "has-property-descriptors": "^1.0.2", | |
3476 | - "has-proto": "^1.0.3", | |
3477 | - "has-symbols": "^1.0.3", | |
3478 | - "hasown": "^2.0.2", | |
3479 | - "internal-slot": "^1.0.7", | |
3480 | - "is-array-buffer": "^3.0.4", | |
3481 | - "is-callable": "^1.2.7", | |
3482 | - "is-data-view": "^1.0.1", | |
3483 | - "is-negative-zero": "^2.0.3", | |
3484 | - "is-regex": "^1.1.4", | |
3485 | - "is-shared-array-buffer": "^1.0.3", | |
3486 | - "is-string": "^1.0.7", | |
3487 | - "is-typed-array": "^1.1.13", | |
3488 | - "is-weakref": "^1.0.2", | |
3489 | - "object-inspect": "^1.13.1", | |
3490 | - "object-keys": "^1.1.1", | |
3491 | - "object.assign": "^4.1.5", | |
3492 | - "regexp.prototype.flags": "^1.5.2", | |
3493 | - "safe-array-concat": "^1.1.2", | |
3494 | - "safe-regex-test": "^1.0.3", | |
3495 | - "string.prototype.trim": "^1.2.9", | |
3496 | - "string.prototype.trimend": "^1.0.8", | |
3497 | - "string.prototype.trimstart": "^1.0.8", | |
3498 | - "typed-array-buffer": "^1.0.2", | |
3499 | - "typed-array-byte-length": "^1.0.1", | |
3500 | - "typed-array-byte-offset": "^1.0.2", | |
3501 | - "typed-array-length": "^1.0.6", | |
3502 | - "unbox-primitive": "^1.0.2", | |
3503 | - "which-typed-array": "^1.1.15" | |
3504 | - }, | |
3505 | - "engines": { | |
3506 | - "node": ">= 0.4" | |
3507 | - }, | |
3508 | - "funding": { | |
3509 | - "url": "https://github.com/sponsors/ljharb" | |
3510 | - } | |
3511 | - }, | |
3512 | - "node_modules/es-define-property": { | |
3513 | - "version": "1.0.0", | |
3514 | - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", | |
3515 | - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", | |
3516 | - "dev": true, | |
3517 | - "dependencies": { | |
3518 | - "get-intrinsic": "^1.2.4" | |
3519 | - }, | |
3520 | - "engines": { | |
3521 | - "node": ">= 0.4" | |
3522 | - } | |
3523 | - }, | |
3524 | - "node_modules/es-errors": { | |
3525 | - "version": "1.3.0", | |
3526 | - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", | |
3527 | - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", | |
3528 | - "dev": true, | |
3529 | - "engines": { | |
3530 | - "node": ">= 0.4" | |
3531 | - } | |
3532 | - }, | |
3533 | - "node_modules/es-iterator-helpers": { | |
3534 | - "version": "1.0.19", | |
3535 | - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.19.tgz", | |
3536 | - "integrity": "sha512-zoMwbCcH5hwUkKJkT8kDIBZSz9I6mVG//+lDCinLCGov4+r7NIy0ld8o03M0cJxl2spVf6ESYVS6/gpIfq1FFw==", | |
3537 | - "dev": true, | |
3538 | - "dependencies": { | |
3539 | - "call-bind": "^1.0.7", | |
3540 | - "define-properties": "^1.2.1", | |
3541 | - "es-abstract": "^1.23.3", | |
3542 | - "es-errors": "^1.3.0", | |
3543 | - "es-set-tostringtag": "^2.0.3", | |
3544 | - "function-bind": "^1.1.2", | |
3545 | - "get-intrinsic": "^1.2.4", | |
3546 | - "globalthis": "^1.0.3", | |
3547 | - "has-property-descriptors": "^1.0.2", | |
3548 | - "has-proto": "^1.0.3", | |
3549 | - "has-symbols": "^1.0.3", | |
3550 | - "internal-slot": "^1.0.7", | |
3551 | - "iterator.prototype": "^1.1.2", | |
3552 | - "safe-array-concat": "^1.1.2" | |
3553 | - }, | |
3554 | - "engines": { | |
3555 | - "node": ">= 0.4" | |
3556 | - } | |
3557 | - }, | |
3558 | - "node_modules/es-object-atoms": { | |
3559 | - "version": "1.0.0", | |
3560 | - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", | |
3561 | - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", | |
3562 | - "dev": true, | |
3563 | - "dependencies": { | |
3564 | - "es-errors": "^1.3.0" | |
3565 | - }, | |
3566 | - "engines": { | |
3567 | - "node": ">= 0.4" | |
3568 | - } | |
3569 | - }, | |
3570 | - "node_modules/es-set-tostringtag": { | |
3571 | - "version": "2.0.3", | |
3572 | - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", | |
3573 | - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", | |
3574 | - "dev": true, | |
3575 | - "dependencies": { | |
3576 | - "get-intrinsic": "^1.2.4", | |
3577 | - "has-tostringtag": "^1.0.2", | |
3578 | - "hasown": "^2.0.1" | |
3579 | - }, | |
3580 | - "engines": { | |
3581 | - "node": ">= 0.4" | |
3582 | - } | |
3583 | - }, | |
3584 | - "node_modules/es-shim-unscopables": { | |
3585 | - "version": "1.0.2", | |
3586 | - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", | |
3587 | - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", | |
3588 | - "dev": true, | |
3589 | - "dependencies": { | |
3590 | - "hasown": "^2.0.0" | |
3591 | - } | |
3592 | - }, | |
3593 | - "node_modules/es-to-primitive": { | |
3594 | - "version": "1.2.1", | |
3595 | - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", | |
3596 | - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", | |
3597 | - "dev": true, | |
3598 | - "dependencies": { | |
3599 | - "is-callable": "^1.1.4", | |
3600 | - "is-date-object": "^1.0.1", | |
3601 | - "is-symbol": "^1.0.2" | |
3602 | - }, | |
3603 | - "engines": { | |
3604 | - "node": ">= 0.4" | |
3605 | - }, | |
3606 | - "funding": { | |
3607 | - "url": "https://github.com/sponsors/ljharb" | |
3608 | - } | |
3609 | - }, | |
3610 | - "node_modules/esbuild": { | |
3611 | - "version": "0.20.2", | |
3612 | - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", | |
3613 | - "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", | |
3614 | - "hasInstallScript": true, | |
3615 | - "bin": { | |
3616 | - "esbuild": "bin/esbuild" | |
3617 | - }, | |
3618 | - "engines": { | |
3619 | - "node": ">=12" | |
3620 | - }, | |
3621 | - "optionalDependencies": { | |
3622 | - "@esbuild/aix-ppc64": "0.20.2", | |
3623 | - "@esbuild/android-arm": "0.20.2", | |
3624 | - "@esbuild/android-arm64": "0.20.2", | |
3625 | - "@esbuild/android-x64": "0.20.2", | |
3626 | - "@esbuild/darwin-arm64": "0.20.2", | |
3627 | - "@esbuild/darwin-x64": "0.20.2", | |
3628 | - "@esbuild/freebsd-arm64": "0.20.2", | |
3629 | - "@esbuild/freebsd-x64": "0.20.2", | |
3630 | - "@esbuild/linux-arm": "0.20.2", | |
3631 | - "@esbuild/linux-arm64": "0.20.2", | |
3632 | - "@esbuild/linux-ia32": "0.20.2", | |
3633 | - "@esbuild/linux-loong64": "0.20.2", | |
3634 | - "@esbuild/linux-mips64el": "0.20.2", | |
3635 | - "@esbuild/linux-ppc64": "0.20.2", | |
3636 | - "@esbuild/linux-riscv64": "0.20.2", | |
3637 | - "@esbuild/linux-s390x": "0.20.2", | |
3638 | - "@esbuild/linux-x64": "0.20.2", | |
3639 | - "@esbuild/netbsd-x64": "0.20.2", | |
3640 | - "@esbuild/openbsd-x64": "0.20.2", | |
3641 | - "@esbuild/sunos-x64": "0.20.2", | |
3642 | - "@esbuild/win32-arm64": "0.20.2", | |
3643 | - "@esbuild/win32-ia32": "0.20.2", | |
3644 | - "@esbuild/win32-x64": "0.20.2" | |
3645 | - } | |
3646 | - }, | |
3647 | - "node_modules/escalade": { | |
3648 | - "version": "3.1.2", | |
3649 | - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", | |
3650 | - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", | |
3651 | - "dev": true, | |
3652 | - "engines": { | |
3653 | - "node": ">=6" | |
3654 | - } | |
3655 | - }, | |
3656 | - "node_modules/escape-string-regexp": { | |
3657 | - "version": "4.0.0", | |
3658 | - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", | |
3659 | - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", | |
3660 | - "engines": { | |
3661 | - "node": ">=10" | |
3662 | - }, | |
3663 | - "funding": { | |
3664 | - "url": "https://github.com/sponsors/sindresorhus" | |
3665 | - } | |
3666 | - }, | |
3667 | - "node_modules/eslint": { | |
3668 | - "version": "8.57.0", | |
3669 | - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", | |
3670 | - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", | |
3671 | - "dev": true, | |
3672 | - "dependencies": { | |
3673 | - "@eslint-community/eslint-utils": "^4.2.0", | |
3674 | - "@eslint-community/regexpp": "^4.6.1", | |
3675 | - "@eslint/eslintrc": "^2.1.4", | |
3676 | - "@eslint/js": "8.57.0", | |
3677 | - "@humanwhocodes/config-array": "^0.11.14", | |
3678 | - "@humanwhocodes/module-importer": "^1.0.1", | |
3679 | - "@nodelib/fs.walk": "^1.2.8", | |
3680 | - "@ungap/structured-clone": "^1.2.0", | |
3681 | - "ajv": "^6.12.4", | |
3682 | - "chalk": "^4.0.0", | |
3683 | - "cross-spawn": "^7.0.2", | |
3684 | - "debug": "^4.3.2", | |
3685 | - "doctrine": "^3.0.0", | |
3686 | - "escape-string-regexp": "^4.0.0", | |
3687 | - "eslint-scope": "^7.2.2", | |
3688 | - "eslint-visitor-keys": "^3.4.3", | |
3689 | - "espree": "^9.6.1", | |
3690 | - "esquery": "^1.4.2", | |
3691 | - "esutils": "^2.0.2", | |
3692 | - "fast-deep-equal": "^3.1.3", | |
3693 | - "file-entry-cache": "^6.0.1", | |
3694 | - "find-up": "^5.0.0", | |
3695 | - "glob-parent": "^6.0.2", | |
3696 | - "globals": "^13.19.0", | |
3697 | - "graphemer": "^1.4.0", | |
3698 | - "ignore": "^5.2.0", | |
3699 | - "imurmurhash": "^0.1.4", | |
3700 | - "is-glob": "^4.0.0", | |
3701 | - "is-path-inside": "^3.0.3", | |
3702 | - "js-yaml": "^4.1.0", | |
3703 | - "json-stable-stringify-without-jsonify": "^1.0.1", | |
3704 | - "levn": "^0.4.1", | |
3705 | - "lodash.merge": "^4.6.2", | |
3706 | - "minimatch": "^3.1.2", | |
3707 | - "natural-compare": "^1.4.0", | |
3708 | - "optionator": "^0.9.3", | |
3709 | - "strip-ansi": "^6.0.1", | |
3710 | - "text-table": "^0.2.0" | |
3711 | - }, | |
3712 | - "bin": { | |
3713 | - "eslint": "bin/eslint.js" | |
3714 | - }, | |
3715 | - "engines": { | |
3716 | - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" | |
3717 | - }, | |
3718 | - "funding": { | |
3719 | - "url": "https://opencollective.com/eslint" | |
3720 | - } | |
3721 | - }, | |
3722 | - "node_modules/eslint-plugin-react": { | |
3723 | - "version": "7.34.1", | |
3724 | - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.34.1.tgz", | |
3725 | - "integrity": "sha512-N97CxlouPT1AHt8Jn0mhhN2RrADlUAsk1/atcT2KyA/l9Q/E6ll7OIGwNumFmWfZ9skV3XXccYS19h80rHtgkw==", | |
3726 | - "dev": true, | |
3727 | - "dependencies": { | |
3728 | - "array-includes": "^3.1.7", | |
3729 | - "array.prototype.findlast": "^1.2.4", | |
3730 | - "array.prototype.flatmap": "^1.3.2", | |
3731 | - "array.prototype.toreversed": "^1.1.2", | |
3732 | - "array.prototype.tosorted": "^1.1.3", | |
3733 | - "doctrine": "^2.1.0", | |
3734 | - "es-iterator-helpers": "^1.0.17", | |
3735 | - "estraverse": "^5.3.0", | |
3736 | - "jsx-ast-utils": "^2.4.1 || ^3.0.0", | |
3737 | - "minimatch": "^3.1.2", | |
3738 | - "object.entries": "^1.1.7", | |
3739 | - "object.fromentries": "^2.0.7", | |
3740 | - "object.hasown": "^1.1.3", | |
3741 | - "object.values": "^1.1.7", | |
3742 | - "prop-types": "^15.8.1", | |
3743 | - "resolve": "^2.0.0-next.5", | |
3744 | - "semver": "^6.3.1", | |
3745 | - "string.prototype.matchall": "^4.0.10" | |
3746 | - }, | |
3747 | - "engines": { | |
3748 | - "node": ">=4" | |
3749 | - }, | |
3750 | - "peerDependencies": { | |
3751 | - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" | |
3752 | - } | |
3753 | - }, | |
3754 | - "node_modules/eslint-plugin-react-hooks": { | |
3755 | - "version": "4.6.2", | |
3756 | - "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.2.tgz", | |
3757 | - "integrity": "sha512-QzliNJq4GinDBcD8gPB5v0wh6g8q3SUi6EFF0x8N/BL9PoVs0atuGc47ozMRyOWAKdwaZ5OnbOEa3WR+dSGKuQ==", | |
3758 | - "dev": true, | |
3759 | - "engines": { | |
3760 | - "node": ">=10" | |
3761 | - }, | |
3762 | - "peerDependencies": { | |
3763 | - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" | |
3764 | - } | |
3765 | - }, | |
3766 | - "node_modules/eslint-plugin-react-refresh": { | |
3767 | - "version": "0.4.6", | |
3768 | - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.6.tgz", | |
3769 | - "integrity": "sha512-NjGXdm7zgcKRkKMua34qVO9doI7VOxZ6ancSvBELJSSoX97jyndXcSoa8XBh69JoB31dNz3EEzlMcizZl7LaMA==", | |
3770 | - "dev": true, | |
3771 | - "peerDependencies": { | |
3772 | - "eslint": ">=7" | |
3773 | - } | |
3774 | - }, | |
3775 | - "node_modules/eslint-plugin-react/node_modules/doctrine": { | |
3776 | - "version": "2.1.0", | |
3777 | - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", | |
3778 | - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", | |
3779 | - "dev": true, | |
3780 | - "dependencies": { | |
3781 | - "esutils": "^2.0.2" | |
3782 | - }, | |
3783 | - "engines": { | |
3784 | - "node": ">=0.10.0" | |
3785 | - } | |
3786 | - }, | |
3787 | - "node_modules/eslint-scope": { | |
3788 | - "version": "7.2.2", | |
3789 | - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", | |
3790 | - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", | |
3791 | - "dev": true, | |
3792 | - "dependencies": { | |
3793 | - "esrecurse": "^4.3.0", | |
3794 | - "estraverse": "^5.2.0" | |
3795 | - }, | |
3796 | - "engines": { | |
3797 | - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" | |
3798 | - }, | |
3799 | - "funding": { | |
3800 | - "url": "https://opencollective.com/eslint" | |
3801 | - } | |
3802 | - }, | |
3803 | - "node_modules/eslint-visitor-keys": { | |
3804 | - "version": "3.4.3", | |
3805 | - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", | |
3806 | - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", | |
3807 | - "dev": true, | |
3808 | - "engines": { | |
3809 | - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" | |
3810 | - }, | |
3811 | - "funding": { | |
3812 | - "url": "https://opencollective.com/eslint" | |
3813 | - } | |
3814 | - }, | |
3815 | - "node_modules/espree": { | |
3816 | - "version": "9.6.1", | |
3817 | - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", | |
3818 | - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", | |
3819 | - "dev": true, | |
3820 | - "dependencies": { | |
3821 | - "acorn": "^8.9.0", | |
3822 | - "acorn-jsx": "^5.3.2", | |
3823 | - "eslint-visitor-keys": "^3.4.1" | |
3824 | - }, | |
3825 | - "engines": { | |
3826 | - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" | |
3827 | - }, | |
3828 | - "funding": { | |
3829 | - "url": "https://opencollective.com/eslint" | |
3830 | - } | |
3831 | - }, | |
3832 | - "node_modules/esquery": { | |
3833 | - "version": "1.5.0", | |
3834 | - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", | |
3835 | - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", | |
3836 | - "dev": true, | |
3837 | - "dependencies": { | |
3838 | - "estraverse": "^5.1.0" | |
3839 | - }, | |
3840 | - "engines": { | |
3841 | - "node": ">=0.10" | |
3842 | - } | |
3843 | - }, | |
3844 | - "node_modules/esrecurse": { | |
3845 | - "version": "4.3.0", | |
3846 | - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", | |
3847 | - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", | |
3848 | - "dev": true, | |
3849 | - "dependencies": { | |
3850 | - "estraverse": "^5.2.0" | |
3851 | - }, | |
3852 | - "engines": { | |
3853 | - "node": ">=4.0" | |
3854 | - } | |
3855 | - }, | |
3856 | - "node_modules/estraverse": { | |
3857 | - "version": "5.3.0", | |
3858 | - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", | |
3859 | - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", | |
3860 | - "dev": true, | |
3861 | - "engines": { | |
3862 | - "node": ">=4.0" | |
3863 | - } | |
3864 | - }, | |
3865 | - "node_modules/estree-walker": { | |
3866 | - "version": "2.0.2", | |
3867 | - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", | |
3868 | - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", | |
3869 | - "peer": true | |
3870 | - }, | |
3871 | - "node_modules/esutils": { | |
3872 | - "version": "2.0.3", | |
3873 | - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", | |
3874 | - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", | |
3875 | - "dev": true, | |
3876 | - "engines": { | |
3877 | - "node": ">=0.10.0" | |
3878 | - } | |
3879 | - }, | |
3880 | - "node_modules/exceljs": { | |
3881 | - "version": "4.4.0", | |
3882 | - "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.4.0.tgz", | |
3883 | - "integrity": "sha512-XctvKaEMaj1Ii9oDOqbW/6e1gXknSY4g/aLCDicOXqBE4M0nRWkUu0PTp++UPNzoFY12BNHMfs/VadKIS6llvg==", | |
3884 | - "dependencies": { | |
3885 | - "archiver": "^5.0.0", | |
3886 | - "dayjs": "^1.8.34", | |
3887 | - "fast-csv": "^4.3.1", | |
3888 | - "jszip": "^3.10.1", | |
3889 | - "readable-stream": "^3.6.0", | |
3890 | - "saxes": "^5.0.1", | |
3891 | - "tmp": "^0.2.0", | |
3892 | - "unzipper": "^0.10.11", | |
3893 | - "uuid": "^8.3.0" | |
3894 | - }, | |
3895 | - "engines": { | |
3896 | - "node": ">=8.3.0" | |
3897 | - } | |
3898 | - }, | |
3899 | - "node_modules/fast-csv": { | |
3900 | - "version": "4.3.6", | |
3901 | - "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz", | |
3902 | - "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==", | |
3903 | - "dependencies": { | |
3904 | - "@fast-csv/format": "4.3.5", | |
3905 | - "@fast-csv/parse": "4.3.6" | |
3906 | - }, | |
3907 | - "engines": { | |
3908 | - "node": ">=10.0.0" | |
3909 | - } | |
3910 | - }, | |
3911 | - "node_modules/fast-deep-equal": { | |
3912 | - "version": "3.1.3", | |
3913 | - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", | |
3914 | - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" | |
3915 | - }, | |
3916 | - "node_modules/fast-glob": { | |
3917 | - "version": "3.3.2", | |
3918 | - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", | |
3919 | - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", | |
3920 | - "dev": true, | |
3921 | - "dependencies": { | |
3922 | - "@nodelib/fs.stat": "^2.0.2", | |
3923 | - "@nodelib/fs.walk": "^1.2.3", | |
3924 | - "glob-parent": "^5.1.2", | |
3925 | - "merge2": "^1.3.0", | |
3926 | - "micromatch": "^4.0.4" | |
3927 | - }, | |
3928 | - "engines": { | |
3929 | - "node": ">=8.6.0" | |
3930 | - } | |
3931 | - }, | |
3932 | - "node_modules/fast-glob/node_modules/glob-parent": { | |
3933 | - "version": "5.1.2", | |
3934 | - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", | |
3935 | - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", | |
3936 | - "dev": true, | |
3937 | - "dependencies": { | |
3938 | - "is-glob": "^4.0.1" | |
3939 | - }, | |
3940 | - "engines": { | |
3941 | - "node": ">= 6" | |
3942 | - } | |
3943 | - }, | |
3944 | - "node_modules/fast-json-stable-stringify": { | |
3945 | - "version": "2.1.0", | |
3946 | - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", | |
3947 | - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" | |
3948 | - }, | |
3949 | - "node_modules/fast-levenshtein": { | |
3950 | - "version": "2.0.6", | |
3951 | - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", | |
3952 | - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", | |
3953 | - "dev": true | |
3954 | - }, | |
3955 | - "node_modules/fastq": { | |
3956 | - "version": "1.17.1", | |
3957 | - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", | |
3958 | - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", | |
3959 | - "dev": true, | |
3960 | - "dependencies": { | |
3961 | - "reusify": "^1.0.4" | |
3962 | - } | |
3963 | - }, | |
3964 | - "node_modules/file-entry-cache": { | |
3965 | - "version": "6.0.1", | |
3966 | - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", | |
3967 | - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", | |
3968 | - "dev": true, | |
3969 | - "dependencies": { | |
3970 | - "flat-cache": "^3.0.4" | |
3971 | - }, | |
3972 | - "engines": { | |
3973 | - "node": "^10.12.0 || >=12.0.0" | |
3974 | - } | |
3975 | - }, | |
3976 | - "node_modules/fill-range": { | |
3977 | - "version": "7.0.1", | |
3978 | - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", | |
3979 | - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", | |
3980 | - "dev": true, | |
3981 | - "dependencies": { | |
3982 | - "to-regex-range": "^5.0.1" | |
3983 | - }, | |
3984 | - "engines": { | |
3985 | - "node": ">=8" | |
3986 | - } | |
3987 | - }, | |
3988 | - "node_modules/find-root": { | |
3989 | - "version": "1.1.0", | |
3990 | - "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", | |
3991 | - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" | |
3992 | - }, | |
3993 | - "node_modules/find-up": { | |
3994 | - "version": "5.0.0", | |
3995 | - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", | |
3996 | - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", | |
3997 | - "dev": true, | |
3998 | - "dependencies": { | |
3999 | - "locate-path": "^6.0.0", | |
4000 | - "path-exists": "^4.0.0" | |
4001 | - }, | |
4002 | - "engines": { | |
4003 | - "node": ">=10" | |
4004 | - }, | |
4005 | - "funding": { | |
4006 | - "url": "https://github.com/sponsors/sindresorhus" | |
4007 | - } | |
4008 | - }, | |
4009 | - "node_modules/flat-cache": { | |
4010 | - "version": "3.2.0", | |
4011 | - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", | |
4012 | - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", | |
4013 | - "dev": true, | |
4014 | - "dependencies": { | |
4015 | - "flatted": "^3.2.9", | |
4016 | - "keyv": "^4.5.3", | |
4017 | - "rimraf": "^3.0.2" | |
4018 | - }, | |
4019 | - "engines": { | |
4020 | - "node": "^10.12.0 || >=12.0.0" | |
4021 | - } | |
4022 | - }, | |
4023 | - "node_modules/flatted": { | |
4024 | - "version": "3.3.1", | |
4025 | - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", | |
4026 | - "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", | |
4027 | - "dev": true | |
4028 | - }, | |
4029 | - "node_modules/follow-redirects": { | |
4030 | - "version": "1.15.6", | |
4031 | - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", | |
4032 | - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", | |
4033 | - "funding": [ | |
4034 | - { | |
4035 | - "type": "individual", | |
4036 | - "url": "https://github.com/sponsors/RubenVerborgh" | |
4037 | - } | |
4038 | - ], | |
4039 | - "engines": { | |
4040 | - "node": ">=4.0" | |
4041 | - }, | |
4042 | - "peerDependenciesMeta": { | |
4043 | - "debug": { | |
4044 | - "optional": true | |
4045 | - } | |
4046 | - } | |
4047 | - }, | |
4048 | - "node_modules/for-each": { | |
4049 | - "version": "0.3.3", | |
4050 | - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", | |
4051 | - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", | |
4052 | - "dev": true, | |
4053 | - "dependencies": { | |
4054 | - "is-callable": "^1.1.3" | |
4055 | - } | |
4056 | - }, | |
4057 | - "node_modules/foreground-child": { | |
4058 | - "version": "3.1.1", | |
4059 | - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", | |
4060 | - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", | |
4061 | - "dev": true, | |
4062 | - "dependencies": { | |
4063 | - "cross-spawn": "^7.0.0", | |
4064 | - "signal-exit": "^4.0.1" | |
4065 | - }, | |
4066 | - "engines": { | |
4067 | - "node": ">=14" | |
4068 | - }, | |
4069 | - "funding": { | |
4070 | - "url": "https://github.com/sponsors/isaacs" | |
4071 | - } | |
4072 | - }, | |
4073 | - "node_modules/form-data": { | |
4074 | - "version": "4.0.0", | |
4075 | - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", | |
4076 | - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", | |
4077 | - "dependencies": { | |
4078 | - "asynckit": "^0.4.0", | |
4079 | - "combined-stream": "^1.0.8", | |
4080 | - "mime-types": "^2.1.12" | |
4081 | - }, | |
4082 | - "engines": { | |
4083 | - "node": ">= 6" | |
4084 | - } | |
4085 | - }, | |
4086 | - "node_modules/fraction.js": { | |
4087 | - "version": "4.3.7", | |
4088 | - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", | |
4089 | - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", | |
4090 | - "dev": true, | |
4091 | - "engines": { | |
4092 | - "node": "*" | |
4093 | - }, | |
4094 | - "funding": { | |
4095 | - "type": "patreon", | |
4096 | - "url": "https://github.com/sponsors/rawify" | |
4097 | - } | |
4098 | - }, | |
4099 | - "node_modules/framer-motion": { | |
4100 | - "version": "10.18.0", | |
4101 | - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-10.18.0.tgz", | |
4102 | - "integrity": "sha512-oGlDh1Q1XqYPksuTD/usb0I70hq95OUzmL9+6Zd+Hs4XV0oaISBa/UUMSjYiq6m8EUF32132mOJ8xVZS+I0S6w==", | |
4103 | - "dependencies": { | |
4104 | - "tslib": "^2.4.0" | |
4105 | - }, | |
4106 | - "optionalDependencies": { | |
4107 | - "@emotion/is-prop-valid": "^0.8.2" | |
4108 | - }, | |
4109 | - "peerDependencies": { | |
4110 | - "react": "^18.0.0", | |
4111 | - "react-dom": "^18.0.0" | |
4112 | - }, | |
4113 | - "peerDependenciesMeta": { | |
4114 | - "react": { | |
4115 | - "optional": true | |
4116 | - }, | |
4117 | - "react-dom": { | |
4118 | - "optional": true | |
4119 | - } | |
4120 | - } | |
4121 | - }, | |
4122 | - "node_modules/framer-motion/node_modules/@emotion/is-prop-valid": { | |
4123 | - "version": "0.8.8", | |
4124 | - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", | |
4125 | - "integrity": "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==", | |
4126 | - "optional": true, | |
4127 | - "dependencies": { | |
4128 | - "@emotion/memoize": "0.7.4" | |
4129 | - } | |
4130 | - }, | |
4131 | - "node_modules/framer-motion/node_modules/@emotion/memoize": { | |
4132 | - "version": "0.7.4", | |
4133 | - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.4.tgz", | |
4134 | - "integrity": "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==", | |
4135 | - "optional": true | |
4136 | - }, | |
4137 | - "node_modules/fs-constants": { | |
4138 | - "version": "1.0.0", | |
4139 | - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", | |
4140 | - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" | |
4141 | - }, | |
4142 | - "node_modules/fs-extra": { | |
4143 | - "version": "7.0.1", | |
4144 | - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", | |
4145 | - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", | |
4146 | - "peer": true, | |
4147 | - "dependencies": { | |
4148 | - "graceful-fs": "^4.1.2", | |
4149 | - "jsonfile": "^4.0.0", | |
4150 | - "universalify": "^0.1.0" | |
4151 | - }, | |
4152 | - "engines": { | |
4153 | - "node": ">=6 <7 || >=8" | |
4154 | - } | |
4155 | - }, | |
4156 | - "node_modules/fs.realpath": { | |
4157 | - "version": "1.0.0", | |
4158 | - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", | |
4159 | - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" | |
4160 | - }, | |
4161 | - "node_modules/fsevents": { | |
4162 | - "version": "2.3.3", | |
4163 | - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", | |
4164 | - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", | |
4165 | - "hasInstallScript": true, | |
4166 | - "optional": true, | |
4167 | - "os": [ | |
4168 | - "darwin" | |
4169 | - ], | |
4170 | - "engines": { | |
4171 | - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" | |
4172 | - } | |
4173 | - }, | |
4174 | - "node_modules/fstream": { | |
4175 | - "version": "1.0.12", | |
4176 | - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", | |
4177 | - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", | |
4178 | - "dependencies": { | |
4179 | - "graceful-fs": "^4.1.2", | |
4180 | - "inherits": "~2.0.0", | |
4181 | - "mkdirp": ">=0.5 0", | |
4182 | - "rimraf": "2" | |
4183 | - }, | |
4184 | - "engines": { | |
4185 | - "node": ">=0.6" | |
4186 | - } | |
4187 | - }, | |
4188 | - "node_modules/fstream/node_modules/rimraf": { | |
4189 | - "version": "2.7.1", | |
4190 | - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", | |
4191 | - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", | |
4192 | - "dependencies": { | |
4193 | - "glob": "^7.1.3" | |
4194 | - }, | |
4195 | - "bin": { | |
4196 | - "rimraf": "bin.js" | |
4197 | - } | |
4198 | - }, | |
4199 | - "node_modules/function-bind": { | |
4200 | - "version": "1.1.2", | |
4201 | - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", | |
4202 | - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", | |
4203 | - "funding": { | |
4204 | - "url": "https://github.com/sponsors/ljharb" | |
4205 | - } | |
4206 | - }, | |
4207 | - "node_modules/function.prototype.name": { | |
4208 | - "version": "1.1.6", | |
4209 | - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", | |
4210 | - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", | |
4211 | - "dev": true, | |
4212 | - "dependencies": { | |
4213 | - "call-bind": "^1.0.2", | |
4214 | - "define-properties": "^1.2.0", | |
4215 | - "es-abstract": "^1.22.1", | |
4216 | - "functions-have-names": "^1.2.3" | |
4217 | - }, | |
4218 | - "engines": { | |
4219 | - "node": ">= 0.4" | |
4220 | - }, | |
4221 | - "funding": { | |
4222 | - "url": "https://github.com/sponsors/ljharb" | |
4223 | - } | |
4224 | - }, | |
4225 | - "node_modules/functions-have-names": { | |
4226 | - "version": "1.2.3", | |
4227 | - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", | |
4228 | - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", | |
4229 | - "dev": true, | |
4230 | - "funding": { | |
4231 | - "url": "https://github.com/sponsors/ljharb" | |
4232 | - } | |
4233 | - }, | |
4234 | - "node_modules/get-intrinsic": { | |
4235 | - "version": "1.2.4", | |
4236 | - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", | |
4237 | - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", | |
4238 | - "dev": true, | |
4239 | - "dependencies": { | |
4240 | - "es-errors": "^1.3.0", | |
4241 | - "function-bind": "^1.1.2", | |
4242 | - "has-proto": "^1.0.1", | |
4243 | - "has-symbols": "^1.0.3", | |
4244 | - "hasown": "^2.0.0" | |
4245 | - }, | |
4246 | - "engines": { | |
4247 | - "node": ">= 0.4" | |
4248 | - }, | |
4249 | - "funding": { | |
4250 | - "url": "https://github.com/sponsors/ljharb" | |
4251 | - } | |
4252 | - }, | |
4253 | - "node_modules/get-symbol-description": { | |
4254 | - "version": "1.0.2", | |
4255 | - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.2.tgz", | |
4256 | - "integrity": "sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg==", | |
4257 | - "dev": true, | |
4258 | - "dependencies": { | |
4259 | - "call-bind": "^1.0.5", | |
4260 | - "es-errors": "^1.3.0", | |
4261 | - "get-intrinsic": "^1.2.4" | |
4262 | - }, | |
4263 | - "engines": { | |
4264 | - "node": ">= 0.4" | |
4265 | - }, | |
4266 | - "funding": { | |
4267 | - "url": "https://github.com/sponsors/ljharb" | |
4268 | - } | |
4269 | - }, | |
4270 | - "node_modules/glob": { | |
4271 | - "version": "7.2.3", | |
4272 | - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", | |
4273 | - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", | |
4274 | - "dependencies": { | |
4275 | - "fs.realpath": "^1.0.0", | |
4276 | - "inflight": "^1.0.4", | |
4277 | - "inherits": "2", | |
4278 | - "minimatch": "^3.1.1", | |
4279 | - "once": "^1.3.0", | |
4280 | - "path-is-absolute": "^1.0.0" | |
4281 | - }, | |
4282 | - "engines": { | |
4283 | - "node": "*" | |
4284 | - }, | |
4285 | - "funding": { | |
4286 | - "url": "https://github.com/sponsors/isaacs" | |
4287 | - } | |
4288 | - }, | |
4289 | - "node_modules/glob-parent": { | |
4290 | - "version": "6.0.2", | |
4291 | - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", | |
4292 | - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", | |
4293 | - "dev": true, | |
4294 | - "dependencies": { | |
4295 | - "is-glob": "^4.0.3" | |
4296 | - }, | |
4297 | - "engines": { | |
4298 | - "node": ">=10.13.0" | |
4299 | - } | |
4300 | - }, | |
4301 | - "node_modules/globals": { | |
4302 | - "version": "13.24.0", | |
4303 | - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", | |
4304 | - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", | |
4305 | - "dev": true, | |
4306 | - "dependencies": { | |
4307 | - "type-fest": "^0.20.2" | |
4308 | - }, | |
4309 | - "engines": { | |
4310 | - "node": ">=8" | |
4311 | - }, | |
4312 | - "funding": { | |
4313 | - "url": "https://github.com/sponsors/sindresorhus" | |
4314 | - } | |
4315 | - }, | |
4316 | - "node_modules/globalthis": { | |
4317 | - "version": "1.0.4", | |
4318 | - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", | |
4319 | - "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", | |
4320 | - "dev": true, | |
4321 | - "dependencies": { | |
4322 | - "define-properties": "^1.2.1", | |
4323 | - "gopd": "^1.0.1" | |
4324 | - }, | |
4325 | - "engines": { | |
4326 | - "node": ">= 0.4" | |
4327 | - }, | |
4328 | - "funding": { | |
4329 | - "url": "https://github.com/sponsors/ljharb" | |
4330 | - } | |
4331 | - }, | |
4332 | - "node_modules/gopd": { | |
4333 | - "version": "1.0.1", | |
4334 | - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", | |
4335 | - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", | |
4336 | - "dev": true, | |
4337 | - "dependencies": { | |
4338 | - "get-intrinsic": "^1.1.3" | |
4339 | - }, | |
4340 | - "funding": { | |
4341 | - "url": "https://github.com/sponsors/ljharb" | |
4342 | - } | |
4343 | - }, | |
4344 | - "node_modules/graceful-fs": { | |
4345 | - "version": "4.2.11", | |
4346 | - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", | |
4347 | - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" | |
4348 | - }, | |
4349 | - "node_modules/graphemer": { | |
4350 | - "version": "1.4.0", | |
4351 | - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", | |
4352 | - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", | |
4353 | - "dev": true | |
4354 | - }, | |
4355 | - "node_modules/has-bigints": { | |
4356 | - "version": "1.0.2", | |
4357 | - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", | |
4358 | - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", | |
4359 | - "dev": true, | |
4360 | - "funding": { | |
4361 | - "url": "https://github.com/sponsors/ljharb" | |
4362 | - } | |
4363 | - }, | |
4364 | - "node_modules/has-flag": { | |
4365 | - "version": "4.0.0", | |
4366 | - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", | |
4367 | - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", | |
4368 | - "engines": { | |
4369 | - "node": ">=8" | |
4370 | - } | |
4371 | - }, | |
4372 | - "node_modules/has-property-descriptors": { | |
4373 | - "version": "1.0.2", | |
4374 | - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", | |
4375 | - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", | |
4376 | - "dev": true, | |
4377 | - "dependencies": { | |
4378 | - "es-define-property": "^1.0.0" | |
4379 | - }, | |
4380 | - "funding": { | |
4381 | - "url": "https://github.com/sponsors/ljharb" | |
4382 | - } | |
4383 | - }, | |
4384 | - "node_modules/has-proto": { | |
4385 | - "version": "1.0.3", | |
4386 | - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", | |
4387 | - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", | |
4388 | - "dev": true, | |
4389 | - "engines": { | |
4390 | - "node": ">= 0.4" | |
4391 | - }, | |
4392 | - "funding": { | |
4393 | - "url": "https://github.com/sponsors/ljharb" | |
4394 | - } | |
4395 | - }, | |
4396 | - "node_modules/has-symbols": { | |
4397 | - "version": "1.0.3", | |
4398 | - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", | |
4399 | - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", | |
4400 | - "dev": true, | |
4401 | - "engines": { | |
4402 | - "node": ">= 0.4" | |
4403 | - }, | |
4404 | - "funding": { | |
4405 | - "url": "https://github.com/sponsors/ljharb" | |
4406 | - } | |
4407 | - }, | |
4408 | - "node_modules/has-tostringtag": { | |
4409 | - "version": "1.0.2", | |
4410 | - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", | |
4411 | - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", | |
4412 | - "dev": true, | |
4413 | - "dependencies": { | |
4414 | - "has-symbols": "^1.0.3" | |
4415 | - }, | |
4416 | - "engines": { | |
4417 | - "node": ">= 0.4" | |
4418 | - }, | |
4419 | - "funding": { | |
4420 | - "url": "https://github.com/sponsors/ljharb" | |
4421 | - } | |
4422 | - }, | |
4423 | - "node_modules/hasown": { | |
4424 | - "version": "2.0.2", | |
4425 | - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", | |
4426 | - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", | |
4427 | - "dependencies": { | |
4428 | - "function-bind": "^1.1.2" | |
4429 | - }, | |
4430 | - "engines": { | |
4431 | - "node": ">= 0.4" | |
4432 | - } | |
4433 | - }, | |
4434 | - "node_modules/he": { | |
4435 | - "version": "1.2.0", | |
4436 | - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", | |
4437 | - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", | |
4438 | - "peer": true, | |
4439 | - "bin": { | |
4440 | - "he": "bin/he" | |
4441 | - } | |
4442 | - }, | |
4443 | - "node_modules/hoist-non-react-statics": { | |
4444 | - "version": "3.3.2", | |
4445 | - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", | |
4446 | - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", | |
4447 | - "dependencies": { | |
4448 | - "react-is": "^16.7.0" | |
4449 | - } | |
4450 | - }, | |
4451 | - "node_modules/ieee754": { | |
4452 | - "version": "1.2.1", | |
4453 | - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", | |
4454 | - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", | |
4455 | - "funding": [ | |
4456 | - { | |
4457 | - "type": "github", | |
4458 | - "url": "https://github.com/sponsors/feross" | |
4459 | - }, | |
4460 | - { | |
4461 | - "type": "patreon", | |
4462 | - "url": "https://www.patreon.com/feross" | |
4463 | - }, | |
4464 | - { | |
4465 | - "type": "consulting", | |
4466 | - "url": "https://feross.org/support" | |
4467 | - } | |
4468 | - ] | |
4469 | - }, | |
4470 | - "node_modules/ignore": { | |
4471 | - "version": "5.3.1", | |
4472 | - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", | |
4473 | - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", | |
4474 | - "dev": true, | |
4475 | - "engines": { | |
4476 | - "node": ">= 4" | |
4477 | - } | |
4478 | - }, | |
4479 | - "node_modules/immediate": { | |
4480 | - "version": "3.0.6", | |
4481 | - "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", | |
4482 | - "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" | |
4483 | - }, | |
4484 | - "node_modules/import-fresh": { | |
4485 | - "version": "3.3.0", | |
4486 | - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", | |
4487 | - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", | |
4488 | - "dependencies": { | |
4489 | - "parent-module": "^1.0.0", | |
4490 | - "resolve-from": "^4.0.0" | |
4491 | - }, | |
4492 | - "engines": { | |
4493 | - "node": ">=6" | |
4494 | - }, | |
4495 | - "funding": { | |
4496 | - "url": "https://github.com/sponsors/sindresorhus" | |
4497 | - } | |
4498 | - }, | |
4499 | - "node_modules/import-lazy": { | |
4500 | - "version": "4.0.0", | |
4501 | - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", | |
4502 | - "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", | |
4503 | - "peer": true, | |
4504 | - "engines": { | |
4505 | - "node": ">=8" | |
4506 | - } | |
4507 | - }, | |
4508 | - "node_modules/imurmurhash": { | |
4509 | - "version": "0.1.4", | |
4510 | - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", | |
4511 | - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", | |
4512 | - "dev": true, | |
4513 | - "engines": { | |
4514 | - "node": ">=0.8.19" | |
4515 | - } | |
4516 | - }, | |
4517 | - "node_modules/inflight": { | |
4518 | - "version": "1.0.6", | |
4519 | - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", | |
4520 | - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", | |
4521 | - "dependencies": { | |
4522 | - "once": "^1.3.0", | |
4523 | - "wrappy": "1" | |
4524 | - } | |
4525 | - }, | |
4526 | - "node_modules/inherits": { | |
4527 | - "version": "2.0.4", | |
4528 | - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", | |
4529 | - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" | |
4530 | - }, | |
4531 | - "node_modules/internal-slot": { | |
4532 | - "version": "1.0.7", | |
4533 | - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.7.tgz", | |
4534 | - "integrity": "sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g==", | |
4535 | - "dev": true, | |
4536 | - "dependencies": { | |
4537 | - "es-errors": "^1.3.0", | |
4538 | - "hasown": "^2.0.0", | |
4539 | - "side-channel": "^1.0.4" | |
4540 | - }, | |
4541 | - "engines": { | |
4542 | - "node": ">= 0.4" | |
4543 | - } | |
4544 | - }, | |
4545 | - "node_modules/is-array-buffer": { | |
4546 | - "version": "3.0.4", | |
4547 | - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", | |
4548 | - "integrity": "sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw==", | |
4549 | - "dev": true, | |
4550 | - "dependencies": { | |
4551 | - "call-bind": "^1.0.2", | |
4552 | - "get-intrinsic": "^1.2.1" | |
4553 | - }, | |
4554 | - "engines": { | |
4555 | - "node": ">= 0.4" | |
4556 | - }, | |
4557 | - "funding": { | |
4558 | - "url": "https://github.com/sponsors/ljharb" | |
4559 | - } | |
4560 | - }, | |
4561 | - "node_modules/is-arrayish": { | |
4562 | - "version": "0.2.1", | |
4563 | - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", | |
4564 | - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" | |
4565 | - }, | |
4566 | - "node_modules/is-async-function": { | |
4567 | - "version": "2.0.0", | |
4568 | - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", | |
4569 | - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", | |
4570 | - "dev": true, | |
4571 | - "dependencies": { | |
4572 | - "has-tostringtag": "^1.0.0" | |
4573 | - }, | |
4574 | - "engines": { | |
4575 | - "node": ">= 0.4" | |
4576 | - }, | |
4577 | - "funding": { | |
4578 | - "url": "https://github.com/sponsors/ljharb" | |
4579 | - } | |
4580 | - }, | |
4581 | - "node_modules/is-bigint": { | |
4582 | - "version": "1.0.4", | |
4583 | - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", | |
4584 | - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", | |
4585 | - "dev": true, | |
4586 | - "dependencies": { | |
4587 | - "has-bigints": "^1.0.1" | |
4588 | - }, | |
4589 | - "funding": { | |
4590 | - "url": "https://github.com/sponsors/ljharb" | |
4591 | - } | |
4592 | - }, | |
4593 | - "node_modules/is-binary-path": { | |
4594 | - "version": "2.1.0", | |
4595 | - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", | |
4596 | - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", | |
4597 | - "dev": true, | |
4598 | - "dependencies": { | |
4599 | - "binary-extensions": "^2.0.0" | |
4600 | - }, | |
4601 | - "engines": { | |
4602 | - "node": ">=8" | |
4603 | - } | |
4604 | - }, | |
4605 | - "node_modules/is-boolean-object": { | |
4606 | - "version": "1.1.2", | |
4607 | - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", | |
4608 | - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", | |
4609 | - "dev": true, | |
4610 | - "dependencies": { | |
4611 | - "call-bind": "^1.0.2", | |
4612 | - "has-tostringtag": "^1.0.0" | |
4613 | - }, | |
4614 | - "engines": { | |
4615 | - "node": ">= 0.4" | |
4616 | - }, | |
4617 | - "funding": { | |
4618 | - "url": "https://github.com/sponsors/ljharb" | |
4619 | - } | |
4620 | - }, | |
4621 | - "node_modules/is-callable": { | |
4622 | - "version": "1.2.7", | |
4623 | - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", | |
4624 | - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", | |
4625 | - "dev": true, | |
4626 | - "engines": { | |
4627 | - "node": ">= 0.4" | |
4628 | - }, | |
4629 | - "funding": { | |
4630 | - "url": "https://github.com/sponsors/ljharb" | |
4631 | - } | |
4632 | - }, | |
4633 | - "node_modules/is-core-module": { | |
4634 | - "version": "2.13.1", | |
4635 | - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", | |
4636 | - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", | |
4637 | - "dependencies": { | |
4638 | - "hasown": "^2.0.0" | |
4639 | - }, | |
4640 | - "funding": { | |
4641 | - "url": "https://github.com/sponsors/ljharb" | |
4642 | - } | |
4643 | - }, | |
4644 | - "node_modules/is-data-view": { | |
4645 | - "version": "1.0.1", | |
4646 | - "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.1.tgz", | |
4647 | - "integrity": "sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w==", | |
4648 | - "dev": true, | |
4649 | - "dependencies": { | |
4650 | - "is-typed-array": "^1.1.13" | |
4651 | - }, | |
4652 | - "engines": { | |
4653 | - "node": ">= 0.4" | |
4654 | - }, | |
4655 | - "funding": { | |
4656 | - "url": "https://github.com/sponsors/ljharb" | |
4657 | - } | |
4658 | - }, | |
4659 | - "node_modules/is-date-object": { | |
4660 | - "version": "1.0.5", | |
4661 | - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", | |
4662 | - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", | |
4663 | - "dev": true, | |
4664 | - "dependencies": { | |
4665 | - "has-tostringtag": "^1.0.0" | |
4666 | - }, | |
4667 | - "engines": { | |
4668 | - "node": ">= 0.4" | |
4669 | - }, | |
4670 | - "funding": { | |
4671 | - "url": "https://github.com/sponsors/ljharb" | |
4672 | - } | |
4673 | - }, | |
4674 | - "node_modules/is-extglob": { | |
4675 | - "version": "2.1.1", | |
4676 | - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", | |
4677 | - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", | |
4678 | - "dev": true, | |
4679 | - "engines": { | |
4680 | - "node": ">=0.10.0" | |
4681 | - } | |
4682 | - }, | |
4683 | - "node_modules/is-finalizationregistry": { | |
4684 | - "version": "1.0.2", | |
4685 | - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", | |
4686 | - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", | |
4687 | - "dev": true, | |
4688 | - "dependencies": { | |
4689 | - "call-bind": "^1.0.2" | |
4690 | - }, | |
4691 | - "funding": { | |
4692 | - "url": "https://github.com/sponsors/ljharb" | |
4693 | - } | |
4694 | - }, | |
4695 | - "node_modules/is-fullwidth-code-point": { | |
4696 | - "version": "3.0.0", | |
4697 | - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", | |
4698 | - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", | |
4699 | - "dev": true, | |
4700 | - "engines": { | |
4701 | - "node": ">=8" | |
4702 | - } | |
4703 | - }, | |
4704 | - "node_modules/is-generator-function": { | |
4705 | - "version": "1.0.10", | |
4706 | - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", | |
4707 | - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", | |
4708 | - "dev": true, | |
4709 | - "dependencies": { | |
4710 | - "has-tostringtag": "^1.0.0" | |
4711 | - }, | |
4712 | - "engines": { | |
4713 | - "node": ">= 0.4" | |
4714 | - }, | |
4715 | - "funding": { | |
4716 | - "url": "https://github.com/sponsors/ljharb" | |
4717 | - } | |
4718 | - }, | |
4719 | - "node_modules/is-glob": { | |
4720 | - "version": "4.0.3", | |
4721 | - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", | |
4722 | - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", | |
4723 | - "dev": true, | |
4724 | - "dependencies": { | |
4725 | - "is-extglob": "^2.1.1" | |
4726 | - }, | |
4727 | - "engines": { | |
4728 | - "node": ">=0.10.0" | |
4729 | - } | |
4730 | - }, | |
4731 | - "node_modules/is-map": { | |
4732 | - "version": "2.0.3", | |
4733 | - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", | |
4734 | - "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", | |
4735 | - "dev": true, | |
4736 | - "engines": { | |
4737 | - "node": ">= 0.4" | |
4738 | - }, | |
4739 | - "funding": { | |
4740 | - "url": "https://github.com/sponsors/ljharb" | |
4741 | - } | |
4742 | - }, | |
4743 | - "node_modules/is-negative-zero": { | |
4744 | - "version": "2.0.3", | |
4745 | - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", | |
4746 | - "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", | |
4747 | - "dev": true, | |
4748 | - "engines": { | |
4749 | - "node": ">= 0.4" | |
4750 | - }, | |
4751 | - "funding": { | |
4752 | - "url": "https://github.com/sponsors/ljharb" | |
4753 | - } | |
4754 | - }, | |
4755 | - "node_modules/is-number": { | |
4756 | - "version": "7.0.0", | |
4757 | - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", | |
4758 | - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", | |
4759 | - "dev": true, | |
4760 | - "engines": { | |
4761 | - "node": ">=0.12.0" | |
4762 | - } | |
4763 | - }, | |
4764 | - "node_modules/is-number-object": { | |
4765 | - "version": "1.0.7", | |
4766 | - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", | |
4767 | - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", | |
4768 | - "dev": true, | |
4769 | - "dependencies": { | |
4770 | - "has-tostringtag": "^1.0.0" | |
4771 | - }, | |
4772 | - "engines": { | |
4773 | - "node": ">= 0.4" | |
4774 | - }, | |
4775 | - "funding": { | |
4776 | - "url": "https://github.com/sponsors/ljharb" | |
4777 | - } | |
4778 | - }, | |
4779 | - "node_modules/is-path-inside": { | |
4780 | - "version": "3.0.3", | |
4781 | - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", | |
4782 | - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", | |
4783 | - "dev": true, | |
4784 | - "engines": { | |
4785 | - "node": ">=8" | |
4786 | - } | |
4787 | - }, | |
4788 | - "node_modules/is-regex": { | |
4789 | - "version": "1.1.4", | |
4790 | - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", | |
4791 | - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", | |
4792 | - "dev": true, | |
4793 | - "dependencies": { | |
4794 | - "call-bind": "^1.0.2", | |
4795 | - "has-tostringtag": "^1.0.0" | |
4796 | - }, | |
4797 | - "engines": { | |
4798 | - "node": ">= 0.4" | |
4799 | - }, | |
4800 | - "funding": { | |
4801 | - "url": "https://github.com/sponsors/ljharb" | |
4802 | - } | |
4803 | - }, | |
4804 | - "node_modules/is-set": { | |
4805 | - "version": "2.0.3", | |
4806 | - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", | |
4807 | - "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", | |
4808 | - "dev": true, | |
4809 | - "engines": { | |
4810 | - "node": ">= 0.4" | |
4811 | - }, | |
4812 | - "funding": { | |
4813 | - "url": "https://github.com/sponsors/ljharb" | |
4814 | - } | |
4815 | - }, | |
4816 | - "node_modules/is-shared-array-buffer": { | |
4817 | - "version": "1.0.3", | |
4818 | - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz", | |
4819 | - "integrity": "sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg==", | |
4820 | - "dev": true, | |
4821 | - "dependencies": { | |
4822 | - "call-bind": "^1.0.7" | |
4823 | - }, | |
4824 | - "engines": { | |
4825 | - "node": ">= 0.4" | |
4826 | - }, | |
4827 | - "funding": { | |
4828 | - "url": "https://github.com/sponsors/ljharb" | |
4829 | - } | |
4830 | - }, | |
4831 | - "node_modules/is-string": { | |
4832 | - "version": "1.0.7", | |
4833 | - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", | |
4834 | - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", | |
4835 | - "dev": true, | |
4836 | - "dependencies": { | |
4837 | - "has-tostringtag": "^1.0.0" | |
4838 | - }, | |
4839 | - "engines": { | |
4840 | - "node": ">= 0.4" | |
4841 | - }, | |
4842 | - "funding": { | |
4843 | - "url": "https://github.com/sponsors/ljharb" | |
4844 | - } | |
4845 | - }, | |
4846 | - "node_modules/is-symbol": { | |
4847 | - "version": "1.0.4", | |
4848 | - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", | |
4849 | - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", | |
4850 | - "dev": true, | |
4851 | - "dependencies": { | |
4852 | - "has-symbols": "^1.0.2" | |
4853 | - }, | |
4854 | - "engines": { | |
4855 | - "node": ">= 0.4" | |
4856 | - }, | |
4857 | - "funding": { | |
4858 | - "url": "https://github.com/sponsors/ljharb" | |
4859 | - } | |
4860 | - }, | |
4861 | - "node_modules/is-typed-array": { | |
4862 | - "version": "1.1.13", | |
4863 | - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", | |
4864 | - "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", | |
4865 | - "dev": true, | |
4866 | - "dependencies": { | |
4867 | - "which-typed-array": "^1.1.14" | |
4868 | - }, | |
4869 | - "engines": { | |
4870 | - "node": ">= 0.4" | |
4871 | - }, | |
4872 | - "funding": { | |
4873 | - "url": "https://github.com/sponsors/ljharb" | |
4874 | - } | |
4875 | - }, | |
4876 | - "node_modules/is-weakmap": { | |
4877 | - "version": "2.0.2", | |
4878 | - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", | |
4879 | - "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", | |
4880 | - "dev": true, | |
4881 | - "engines": { | |
4882 | - "node": ">= 0.4" | |
4883 | - }, | |
4884 | - "funding": { | |
4885 | - "url": "https://github.com/sponsors/ljharb" | |
4886 | - } | |
4887 | - }, | |
4888 | - "node_modules/is-weakref": { | |
4889 | - "version": "1.0.2", | |
4890 | - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", | |
4891 | - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", | |
4892 | - "dev": true, | |
4893 | - "dependencies": { | |
4894 | - "call-bind": "^1.0.2" | |
4895 | - }, | |
4896 | - "funding": { | |
4897 | - "url": "https://github.com/sponsors/ljharb" | |
4898 | - } | |
4899 | - }, | |
4900 | - "node_modules/is-weakset": { | |
4901 | - "version": "2.0.3", | |
4902 | - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", | |
4903 | - "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", | |
4904 | - "dev": true, | |
4905 | - "dependencies": { | |
4906 | - "call-bind": "^1.0.7", | |
4907 | - "get-intrinsic": "^1.2.4" | |
4908 | - }, | |
4909 | - "engines": { | |
4910 | - "node": ">= 0.4" | |
4911 | - }, | |
4912 | - "funding": { | |
4913 | - "url": "https://github.com/sponsors/ljharb" | |
4914 | - } | |
4915 | - }, | |
4916 | - "node_modules/isarray": { | |
4917 | - "version": "2.0.5", | |
4918 | - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", | |
4919 | - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", | |
4920 | - "dev": true | |
4921 | - }, | |
4922 | - "node_modules/isexe": { | |
4923 | - "version": "2.0.0", | |
4924 | - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", | |
4925 | - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", | |
4926 | - "dev": true | |
4927 | - }, | |
4928 | - "node_modules/iterator.prototype": { | |
4929 | - "version": "1.1.2", | |
4930 | - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", | |
4931 | - "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", | |
4932 | - "dev": true, | |
4933 | - "dependencies": { | |
4934 | - "define-properties": "^1.2.1", | |
4935 | - "get-intrinsic": "^1.2.1", | |
4936 | - "has-symbols": "^1.0.3", | |
4937 | - "reflect.getprototypeof": "^1.0.4", | |
4938 | - "set-function-name": "^2.0.1" | |
4939 | - } | |
4940 | - }, | |
4941 | - "node_modules/jackspeak": { | |
4942 | - "version": "2.3.6", | |
4943 | - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", | |
4944 | - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", | |
4945 | - "dev": true, | |
4946 | - "dependencies": { | |
4947 | - "@isaacs/cliui": "^8.0.2" | |
4948 | - }, | |
4949 | - "engines": { | |
4950 | - "node": ">=14" | |
4951 | - }, | |
4952 | - "funding": { | |
4953 | - "url": "https://github.com/sponsors/isaacs" | |
4954 | - }, | |
4955 | - "optionalDependencies": { | |
4956 | - "@pkgjs/parseargs": "^0.11.0" | |
4957 | - } | |
4958 | - }, | |
4959 | - "node_modules/jiti": { | |
4960 | - "version": "1.21.0", | |
4961 | - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", | |
4962 | - "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", | |
4963 | - "dev": true, | |
4964 | - "bin": { | |
4965 | - "jiti": "bin/jiti.js" | |
4966 | - } | |
4967 | - }, | |
4968 | - "node_modules/jju": { | |
4969 | - "version": "1.4.0", | |
4970 | - "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", | |
4971 | - "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", | |
4972 | - "peer": true | |
4973 | - }, | |
4974 | - "node_modules/js-tokens": { | |
4975 | - "version": "4.0.0", | |
4976 | - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", | |
4977 | - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" | |
4978 | - }, | |
4979 | - "node_modules/js-yaml": { | |
4980 | - "version": "4.1.0", | |
4981 | - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", | |
4982 | - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", | |
4983 | - "dev": true, | |
4984 | - "dependencies": { | |
4985 | - "argparse": "^2.0.1" | |
4986 | - }, | |
4987 | - "bin": { | |
4988 | - "js-yaml": "bin/js-yaml.js" | |
4989 | - } | |
4990 | - }, | |
4991 | - "node_modules/json-buffer": { | |
4992 | - "version": "3.0.1", | |
4993 | - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", | |
4994 | - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", | |
4995 | - "dev": true | |
4996 | - }, | |
4997 | - "node_modules/json-parse-even-better-errors": { | |
4998 | - "version": "2.3.1", | |
4999 | - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", | |
5000 | - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" | |
5001 | - }, | |
5002 | - "node_modules/json-schema-traverse": { | |
5003 | - "version": "0.4.1", | |
5004 | - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", | |
5005 | - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" | |
5006 | - }, | |
5007 | - "node_modules/json-stable-stringify-without-jsonify": { | |
5008 | - "version": "1.0.1", | |
5009 | - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", | |
5010 | - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", | |
5011 | - "dev": true | |
5012 | - }, | |
5013 | - "node_modules/jsonfile": { | |
5014 | - "version": "4.0.0", | |
5015 | - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", | |
5016 | - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", | |
5017 | - "peer": true, | |
5018 | - "optionalDependencies": { | |
5019 | - "graceful-fs": "^4.1.6" | |
5020 | - } | |
5021 | - }, | |
5022 | - "node_modules/jsx-ast-utils": { | |
5023 | - "version": "3.3.5", | |
5024 | - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", | |
5025 | - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", | |
5026 | - "dev": true, | |
5027 | - "dependencies": { | |
5028 | - "array-includes": "^3.1.6", | |
5029 | - "array.prototype.flat": "^1.3.1", | |
5030 | - "object.assign": "^4.1.4", | |
5031 | - "object.values": "^1.1.6" | |
5032 | - }, | |
5033 | - "engines": { | |
5034 | - "node": ">=4.0" | |
5035 | - } | |
5036 | - }, | |
5037 | - "node_modules/jszip": { | |
5038 | - "version": "3.10.1", | |
5039 | - "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", | |
5040 | - "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", | |
5041 | - "dependencies": { | |
5042 | - "lie": "~3.3.0", | |
5043 | - "pako": "~1.0.2", | |
5044 | - "readable-stream": "~2.3.6", | |
5045 | - "setimmediate": "^1.0.5" | |
5046 | - } | |
5047 | - }, | |
5048 | - "node_modules/jszip/node_modules/isarray": { | |
5049 | - "version": "1.0.0", | |
5050 | - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", | |
5051 | - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" | |
5052 | - }, | |
5053 | - "node_modules/jszip/node_modules/readable-stream": { | |
5054 | - "version": "2.3.8", | |
5055 | - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", | |
5056 | - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", | |
5057 | - "dependencies": { | |
5058 | - "core-util-is": "~1.0.0", | |
5059 | - "inherits": "~2.0.3", | |
5060 | - "isarray": "~1.0.0", | |
5061 | - "process-nextick-args": "~2.0.0", | |
5062 | - "safe-buffer": "~5.1.1", | |
5063 | - "string_decoder": "~1.1.1", | |
5064 | - "util-deprecate": "~1.0.1" | |
5065 | - } | |
5066 | - }, | |
5067 | - "node_modules/jszip/node_modules/safe-buffer": { | |
5068 | - "version": "5.1.2", | |
5069 | - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", | |
5070 | - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" | |
5071 | - }, | |
5072 | - "node_modules/jszip/node_modules/string_decoder": { | |
5073 | - "version": "1.1.1", | |
5074 | - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", | |
5075 | - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", | |
5076 | - "dependencies": { | |
5077 | - "safe-buffer": "~5.1.0" | |
5078 | - } | |
5079 | - }, | |
5080 | - "node_modules/keyv": { | |
5081 | - "version": "4.5.4", | |
5082 | - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", | |
5083 | - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", | |
5084 | - "dev": true, | |
5085 | - "dependencies": { | |
5086 | - "json-buffer": "3.0.1" | |
5087 | - } | |
5088 | - }, | |
5089 | - "node_modules/kolorist": { | |
5090 | - "version": "1.8.0", | |
5091 | - "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", | |
5092 | - "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", | |
5093 | - "peer": true | |
5094 | - }, | |
5095 | - "node_modules/lazystream": { | |
5096 | - "version": "1.0.1", | |
5097 | - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", | |
5098 | - "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", | |
5099 | - "dependencies": { | |
5100 | - "readable-stream": "^2.0.5" | |
5101 | - }, | |
5102 | - "engines": { | |
5103 | - "node": ">= 0.6.3" | |
5104 | - } | |
5105 | - }, | |
5106 | - "node_modules/lazystream/node_modules/isarray": { | |
5107 | - "version": "1.0.0", | |
5108 | - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", | |
5109 | - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" | |
5110 | - }, | |
5111 | - "node_modules/lazystream/node_modules/readable-stream": { | |
5112 | - "version": "2.3.8", | |
5113 | - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", | |
5114 | - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", | |
5115 | - "dependencies": { | |
5116 | - "core-util-is": "~1.0.0", | |
5117 | - "inherits": "~2.0.3", | |
5118 | - "isarray": "~1.0.0", | |
5119 | - "process-nextick-args": "~2.0.0", | |
5120 | - "safe-buffer": "~5.1.1", | |
5121 | - "string_decoder": "~1.1.1", | |
5122 | - "util-deprecate": "~1.0.1" | |
5123 | - } | |
5124 | - }, | |
5125 | - "node_modules/lazystream/node_modules/safe-buffer": { | |
5126 | - "version": "5.1.2", | |
5127 | - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", | |
5128 | - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" | |
5129 | - }, | |
5130 | - "node_modules/lazystream/node_modules/string_decoder": { | |
5131 | - "version": "1.1.1", | |
5132 | - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", | |
5133 | - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", | |
5134 | - "dependencies": { | |
5135 | - "safe-buffer": "~5.1.0" | |
5136 | - } | |
5137 | - }, | |
5138 | - "node_modules/levn": { | |
5139 | - "version": "0.4.1", | |
5140 | - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", | |
5141 | - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", | |
5142 | - "dev": true, | |
5143 | - "dependencies": { | |
5144 | - "prelude-ls": "^1.2.1", | |
5145 | - "type-check": "~0.4.0" | |
5146 | - }, | |
5147 | - "engines": { | |
5148 | - "node": ">= 0.8.0" | |
5149 | - } | |
5150 | - }, | |
5151 | - "node_modules/lie": { | |
5152 | - "version": "3.3.0", | |
5153 | - "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", | |
5154 | - "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", | |
5155 | - "dependencies": { | |
5156 | - "immediate": "~3.0.5" | |
5157 | - } | |
5158 | - }, | |
5159 | - "node_modules/lilconfig": { | |
5160 | - "version": "2.1.0", | |
5161 | - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", | |
5162 | - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", | |
5163 | - "dev": true, | |
5164 | - "engines": { | |
5165 | - "node": ">=10" | |
5166 | - } | |
5167 | - }, | |
5168 | - "node_modules/lines-and-columns": { | |
5169 | - "version": "1.2.4", | |
5170 | - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", | |
5171 | - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" | |
5172 | - }, | |
5173 | - "node_modules/listenercount": { | |
5174 | - "version": "1.0.1", | |
5175 | - "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", | |
5176 | - "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==" | |
5177 | - }, | |
5178 | - "node_modules/locate-path": { | |
5179 | - "version": "6.0.0", | |
5180 | - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", | |
5181 | - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", | |
5182 | - "dev": true, | |
5183 | - "dependencies": { | |
5184 | - "p-locate": "^5.0.0" | |
5185 | - }, | |
5186 | - "engines": { | |
5187 | - "node": ">=10" | |
5188 | - }, | |
5189 | - "funding": { | |
5190 | - "url": "https://github.com/sponsors/sindresorhus" | |
5191 | - } | |
5192 | - }, | |
5193 | - "node_modules/lodash": { | |
5194 | - "version": "4.17.21", | |
5195 | - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", | |
5196 | - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", | |
5197 | - "peer": true | |
5198 | - }, | |
5199 | - "node_modules/lodash.defaults": { | |
5200 | - "version": "4.2.0", | |
5201 | - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", | |
5202 | - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==" | |
5203 | - }, | |
5204 | - "node_modules/lodash.difference": { | |
5205 | - "version": "4.5.0", | |
5206 | - "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", | |
5207 | - "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==" | |
5208 | - }, | |
5209 | - "node_modules/lodash.escaperegexp": { | |
5210 | - "version": "4.1.2", | |
5211 | - "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", | |
5212 | - "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==" | |
5213 | - }, | |
5214 | - "node_modules/lodash.flatten": { | |
5215 | - "version": "4.4.0", | |
5216 | - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", | |
5217 | - "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==" | |
5218 | - }, | |
5219 | - "node_modules/lodash.get": { | |
5220 | - "version": "4.4.2", | |
5221 | - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", | |
5222 | - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", | |
5223 | - "peer": true | |
5224 | - }, | |
5225 | - "node_modules/lodash.groupby": { | |
5226 | - "version": "4.6.0", | |
5227 | - "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz", | |
5228 | - "integrity": "sha512-5dcWxm23+VAoz+awKmBaiBvzox8+RqMgFhi7UvX9DHZr2HdxHXM/Wrf8cfKpsW37RNrvtPn6hSwNqurSILbmJw==" | |
5229 | - }, | |
5230 | - "node_modules/lodash.isboolean": { | |
5231 | - "version": "3.0.3", | |
5232 | - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", | |
5233 | - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" | |
5234 | - }, | |
5235 | - "node_modules/lodash.isequal": { | |
5236 | - "version": "4.5.0", | |
5237 | - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", | |
5238 | - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" | |
5239 | - }, | |
5240 | - "node_modules/lodash.isfunction": { | |
5241 | - "version": "3.0.9", | |
5242 | - "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", | |
5243 | - "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==" | |
5244 | - }, | |
5245 | - "node_modules/lodash.isnil": { | |
5246 | - "version": "4.0.0", | |
5247 | - "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz", | |
5248 | - "integrity": "sha512-up2Mzq3545mwVnMhTDMdfoG1OurpA/s5t88JmQX809eH3C8491iu2sfKhTfhQtKY78oPNhiaHJUpT/dUDAAtng==" | |
5249 | - }, | |
5250 | - "node_modules/lodash.isplainobject": { | |
5251 | - "version": "4.0.6", | |
5252 | - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", | |
5253 | - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" | |
5254 | - }, | |
5255 | - "node_modules/lodash.isundefined": { | |
5256 | - "version": "3.0.1", | |
5257 | - "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz", | |
5258 | - "integrity": "sha512-MXB1is3s899/cD8jheYYE2V9qTHwKvt+npCwpD+1Sxm3Q3cECXCiYHjeHWXNwr6Q0SOBPrYUDxendrO6goVTEA==" | |
5259 | - }, | |
5260 | - "node_modules/lodash.merge": { | |
5261 | - "version": "4.6.2", | |
5262 | - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", | |
5263 | - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", | |
5264 | - "dev": true | |
5265 | - }, | |
5266 | - "node_modules/lodash.union": { | |
5267 | - "version": "4.6.0", | |
5268 | - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", | |
5269 | - "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==" | |
5270 | - }, | |
5271 | - "node_modules/lodash.uniq": { | |
5272 | - "version": "4.5.0", | |
5273 | - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", | |
5274 | - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" | |
5275 | - }, | |
5276 | - "node_modules/loose-envify": { | |
5277 | - "version": "1.4.0", | |
5278 | - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", | |
5279 | - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", | |
5280 | - "dependencies": { | |
5281 | - "js-tokens": "^3.0.0 || ^4.0.0" | |
5282 | - }, | |
5283 | - "bin": { | |
5284 | - "loose-envify": "cli.js" | |
5285 | - } | |
5286 | - }, | |
5287 | - "node_modules/lru-cache": { | |
5288 | - "version": "6.0.0", | |
5289 | - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", | |
5290 | - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", | |
5291 | - "peer": true, | |
5292 | - "dependencies": { | |
5293 | - "yallist": "^4.0.0" | |
5294 | - }, | |
5295 | - "engines": { | |
5296 | - "node": ">=10" | |
5297 | - } | |
5298 | - }, | |
5299 | - "node_modules/magic-string": { | |
5300 | - "version": "0.30.10", | |
5301 | - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", | |
5302 | - "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", | |
5303 | - "peer": true, | |
5304 | - "dependencies": { | |
5305 | - "@jridgewell/sourcemap-codec": "^1.4.15" | |
5306 | - } | |
5307 | - }, | |
5308 | - "node_modules/mdb-react-ui-kit": { | |
5309 | - "version": "8.0.0", | |
5310 | - "resolved": "https://registry.npmjs.org/mdb-react-ui-kit/-/mdb-react-ui-kit-8.0.0.tgz", | |
5311 | - "integrity": "sha512-4XnfUv/SkMGhKbSUcEqNgNBYz009YEPHuweodher7ba70NlbnKlfgCxXolZPX5WUPYtsV2IjDRMxu3+oLnjzTQ==", | |
5312 | - "dependencies": { | |
5313 | - "@popperjs/core": "2.11.5", | |
5314 | - "clsx": "1.1.1", | |
5315 | - "framer-motion": "^10.16.4", | |
5316 | - "react-popper": "2.3.0" | |
5317 | - }, | |
5318 | - "peerDependencies": { | |
5319 | - "@types/react": "^18.0.9", | |
5320 | - "@types/react-dom": "^18.0.3", | |
5321 | - "react": "^18.1.0", | |
5322 | - "react-dom": "^18.1.0" | |
5323 | - } | |
5324 | - }, | |
5325 | - "node_modules/mdb-react-ui-kit/node_modules/@popperjs/core": { | |
5326 | - "version": "2.11.5", | |
5327 | - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz", | |
5328 | - "integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==", | |
5329 | - "funding": { | |
5330 | - "type": "opencollective", | |
5331 | - "url": "https://opencollective.com/popperjs" | |
5332 | - } | |
5333 | - }, | |
5334 | - "node_modules/mdb-react-ui-kit/node_modules/clsx": { | |
5335 | - "version": "1.1.1", | |
5336 | - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", | |
5337 | - "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==", | |
5338 | - "engines": { | |
5339 | - "node": ">=6" | |
5340 | - } | |
5341 | - }, | |
5342 | - "node_modules/merge2": { | |
5343 | - "version": "1.4.1", | |
5344 | - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", | |
5345 | - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", | |
5346 | - "dev": true, | |
5347 | - "engines": { | |
5348 | - "node": ">= 8" | |
5349 | - } | |
5350 | - }, | |
5351 | - "node_modules/micromatch": { | |
5352 | - "version": "4.0.5", | |
5353 | - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", | |
5354 | - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", | |
5355 | - "dev": true, | |
5356 | - "dependencies": { | |
5357 | - "braces": "^3.0.2", | |
5358 | - "picomatch": "^2.3.1" | |
5359 | - }, | |
5360 | - "engines": { | |
5361 | - "node": ">=8.6" | |
5362 | - } | |
5363 | - }, | |
5364 | - "node_modules/mime-db": { | |
5365 | - "version": "1.52.0", | |
5366 | - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", | |
5367 | - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", | |
5368 | - "engines": { | |
5369 | - "node": ">= 0.6" | |
5370 | - } | |
5371 | - }, | |
5372 | - "node_modules/mime-types": { | |
5373 | - "version": "2.1.35", | |
5374 | - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", | |
5375 | - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", | |
5376 | - "dependencies": { | |
5377 | - "mime-db": "1.52.0" | |
5378 | - }, | |
5379 | - "engines": { | |
5380 | - "node": ">= 0.6" | |
5381 | - } | |
5382 | - }, | |
5383 | - "node_modules/minimatch": { | |
5384 | - "version": "3.1.2", | |
5385 | - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", | |
5386 | - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", | |
5387 | - "dependencies": { | |
5388 | - "brace-expansion": "^1.1.7" | |
5389 | - }, | |
5390 | - "engines": { | |
5391 | - "node": "*" | |
5392 | - } | |
5393 | - }, | |
5394 | - "node_modules/minimist": { | |
5395 | - "version": "1.2.8", | |
5396 | - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", | |
5397 | - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", | |
5398 | - "funding": { | |
5399 | - "url": "https://github.com/sponsors/ljharb" | |
5400 | - } | |
5401 | - }, | |
5402 | - "node_modules/minipass": { | |
5403 | - "version": "7.0.4", | |
5404 | - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", | |
5405 | - "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", | |
5406 | - "dev": true, | |
5407 | - "engines": { | |
5408 | - "node": ">=16 || 14 >=14.17" | |
5409 | - } | |
5410 | - }, | |
5411 | - "node_modules/mkdirp": { | |
5412 | - "version": "0.5.6", | |
5413 | - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", | |
5414 | - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", | |
5415 | - "dependencies": { | |
5416 | - "minimist": "^1.2.6" | |
5417 | - }, | |
5418 | - "bin": { | |
5419 | - "mkdirp": "bin/cmd.js" | |
5420 | - } | |
5421 | - }, | |
5422 | - "node_modules/ms": { | |
5423 | - "version": "2.1.2", | |
5424 | - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", | |
5425 | - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" | |
5426 | - }, | |
5427 | - "node_modules/muggle-string": { | |
5428 | - "version": "0.3.1", | |
5429 | - "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.3.1.tgz", | |
5430 | - "integrity": "sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==", | |
5431 | - "peer": true | |
5432 | - }, | |
5433 | - "node_modules/mz": { | |
5434 | - "version": "2.7.0", | |
5435 | - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", | |
5436 | - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", | |
5437 | - "dev": true, | |
5438 | - "dependencies": { | |
5439 | - "any-promise": "^1.0.0", | |
5440 | - "object-assign": "^4.0.1", | |
5441 | - "thenify-all": "^1.0.0" | |
5442 | - } | |
5443 | - }, | |
5444 | - "node_modules/nanoid": { | |
5445 | - "version": "3.3.7", | |
5446 | - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", | |
5447 | - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", | |
5448 | - "funding": [ | |
5449 | - { | |
5450 | - "type": "github", | |
5451 | - "url": "https://github.com/sponsors/ai" | |
5452 | - } | |
5453 | - ], | |
5454 | - "bin": { | |
5455 | - "nanoid": "bin/nanoid.cjs" | |
5456 | - }, | |
5457 | - "engines": { | |
5458 | - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" | |
5459 | - } | |
5460 | - }, | |
5461 | - "node_modules/natural-compare": { | |
5462 | - "version": "1.4.0", | |
5463 | - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", | |
5464 | - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", | |
5465 | - "dev": true | |
5466 | - }, | |
5467 | - "node_modules/node-releases": { | |
5468 | - "version": "2.0.14", | |
5469 | - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", | |
5470 | - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", | |
5471 | - "dev": true | |
5472 | - }, | |
5473 | - "node_modules/normalize-path": { | |
5474 | - "version": "3.0.0", | |
5475 | - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", | |
5476 | - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", | |
5477 | - "engines": { | |
5478 | - "node": ">=0.10.0" | |
5479 | - } | |
5480 | - }, | |
5481 | - "node_modules/normalize-range": { | |
5482 | - "version": "0.1.2", | |
5483 | - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", | |
5484 | - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", | |
5485 | - "dev": true, | |
5486 | - "engines": { | |
5487 | - "node": ">=0.10.0" | |
5488 | - } | |
5489 | - }, | |
5490 | - "node_modules/object-assign": { | |
5491 | - "version": "4.1.1", | |
5492 | - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", | |
5493 | - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", | |
5494 | - "engines": { | |
5495 | - "node": ">=0.10.0" | |
5496 | - } | |
5497 | - }, | |
5498 | - "node_modules/object-hash": { | |
5499 | - "version": "3.0.0", | |
5500 | - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", | |
5501 | - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", | |
5502 | - "dev": true, | |
5503 | - "engines": { | |
5504 | - "node": ">= 6" | |
5505 | - } | |
5506 | - }, | |
5507 | - "node_modules/object-inspect": { | |
5508 | - "version": "1.13.1", | |
5509 | - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", | |
5510 | - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", | |
5511 | - "dev": true, | |
5512 | - "funding": { | |
5513 | - "url": "https://github.com/sponsors/ljharb" | |
5514 | - } | |
5515 | - }, | |
5516 | - "node_modules/object-keys": { | |
5517 | - "version": "1.1.1", | |
5518 | - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", | |
5519 | - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", | |
5520 | - "dev": true, | |
5521 | - "engines": { | |
5522 | - "node": ">= 0.4" | |
5523 | - } | |
5524 | - }, | |
5525 | - "node_modules/object.assign": { | |
5526 | - "version": "4.1.5", | |
5527 | - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", | |
5528 | - "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", | |
5529 | - "dev": true, | |
5530 | - "dependencies": { | |
5531 | - "call-bind": "^1.0.5", | |
5532 | - "define-properties": "^1.2.1", | |
5533 | - "has-symbols": "^1.0.3", | |
5534 | - "object-keys": "^1.1.1" | |
5535 | - }, | |
5536 | - "engines": { | |
5537 | - "node": ">= 0.4" | |
5538 | - }, | |
5539 | - "funding": { | |
5540 | - "url": "https://github.com/sponsors/ljharb" | |
5541 | - } | |
5542 | - }, | |
5543 | - "node_modules/object.entries": { | |
5544 | - "version": "1.1.8", | |
5545 | - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.8.tgz", | |
5546 | - "integrity": "sha512-cmopxi8VwRIAw/fkijJohSfpef5PdN0pMQJN6VC/ZKvn0LIknWD8KtgY6KlQdEc4tIjcQ3HxSMmnvtzIscdaYQ==", | |
5547 | - "dev": true, | |
5548 | - "dependencies": { | |
5549 | - "call-bind": "^1.0.7", | |
5550 | - "define-properties": "^1.2.1", | |
5551 | - "es-object-atoms": "^1.0.0" | |
5552 | - }, | |
5553 | - "engines": { | |
5554 | - "node": ">= 0.4" | |
5555 | - } | |
5556 | - }, | |
5557 | - "node_modules/object.fromentries": { | |
5558 | - "version": "2.0.8", | |
5559 | - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", | |
5560 | - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", | |
5561 | - "dev": true, | |
5562 | - "dependencies": { | |
5563 | - "call-bind": "^1.0.7", | |
5564 | - "define-properties": "^1.2.1", | |
5565 | - "es-abstract": "^1.23.2", | |
5566 | - "es-object-atoms": "^1.0.0" | |
5567 | - }, | |
5568 | - "engines": { | |
5569 | - "node": ">= 0.4" | |
5570 | - }, | |
5571 | - "funding": { | |
5572 | - "url": "https://github.com/sponsors/ljharb" | |
5573 | - } | |
5574 | - }, | |
5575 | - "node_modules/object.hasown": { | |
5576 | - "version": "1.1.4", | |
5577 | - "resolved": "https://registry.npmjs.org/object.hasown/-/object.hasown-1.1.4.tgz", | |
5578 | - "integrity": "sha512-FZ9LZt9/RHzGySlBARE3VF+gE26TxR38SdmqOqliuTnl9wrKulaQs+4dee1V+Io8VfxqzAfHu6YuRgUy8OHoTg==", | |
5579 | - "dev": true, | |
5580 | - "dependencies": { | |
5581 | - "define-properties": "^1.2.1", | |
5582 | - "es-abstract": "^1.23.2", | |
5583 | - "es-object-atoms": "^1.0.0" | |
5584 | - }, | |
5585 | - "engines": { | |
5586 | - "node": ">= 0.4" | |
5587 | - }, | |
5588 | - "funding": { | |
5589 | - "url": "https://github.com/sponsors/ljharb" | |
5590 | - } | |
5591 | - }, | |
5592 | - "node_modules/object.values": { | |
5593 | - "version": "1.2.0", | |
5594 | - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.0.tgz", | |
5595 | - "integrity": "sha512-yBYjY9QX2hnRmZHAjG/f13MzmBzxzYgQhFrke06TTyKY5zSTEqkOeukBzIdVA3j3ulu8Qa3MbVFShV7T2RmGtQ==", | |
5596 | - "dev": true, | |
5597 | - "dependencies": { | |
5598 | - "call-bind": "^1.0.7", | |
5599 | - "define-properties": "^1.2.1", | |
5600 | - "es-object-atoms": "^1.0.0" | |
5601 | - }, | |
5602 | - "engines": { | |
5603 | - "node": ">= 0.4" | |
5604 | - }, | |
5605 | - "funding": { | |
5606 | - "url": "https://github.com/sponsors/ljharb" | |
5607 | - } | |
5608 | - }, | |
5609 | - "node_modules/once": { | |
5610 | - "version": "1.4.0", | |
5611 | - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", | |
5612 | - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", | |
5613 | - "dependencies": { | |
5614 | - "wrappy": "1" | |
5615 | - } | |
5616 | - }, | |
5617 | - "node_modules/optionator": { | |
5618 | - "version": "0.9.4", | |
5619 | - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", | |
5620 | - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", | |
5621 | - "dev": true, | |
5622 | - "dependencies": { | |
5623 | - "deep-is": "^0.1.3", | |
5624 | - "fast-levenshtein": "^2.0.6", | |
5625 | - "levn": "^0.4.1", | |
5626 | - "prelude-ls": "^1.2.1", | |
5627 | - "type-check": "^0.4.0", | |
5628 | - "word-wrap": "^1.2.5" | |
5629 | - }, | |
5630 | - "engines": { | |
5631 | - "node": ">= 0.8.0" | |
5632 | - } | |
5633 | - }, | |
5634 | - "node_modules/p-limit": { | |
5635 | - "version": "3.1.0", | |
5636 | - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", | |
5637 | - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", | |
5638 | - "dev": true, | |
5639 | - "dependencies": { | |
5640 | - "yocto-queue": "^0.1.0" | |
5641 | - }, | |
5642 | - "engines": { | |
5643 | - "node": ">=10" | |
5644 | - }, | |
5645 | - "funding": { | |
5646 | - "url": "https://github.com/sponsors/sindresorhus" | |
5647 | - } | |
5648 | - }, | |
5649 | - "node_modules/p-locate": { | |
5650 | - "version": "5.0.0", | |
5651 | - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", | |
5652 | - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", | |
5653 | - "dev": true, | |
5654 | - "dependencies": { | |
5655 | - "p-limit": "^3.0.2" | |
5656 | - }, | |
5657 | - "engines": { | |
5658 | - "node": ">=10" | |
5659 | - }, | |
5660 | - "funding": { | |
5661 | - "url": "https://github.com/sponsors/sindresorhus" | |
5662 | - } | |
5663 | - }, | |
5664 | - "node_modules/pako": { | |
5665 | - "version": "1.0.11", | |
5666 | - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", | |
5667 | - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" | |
5668 | - }, | |
5669 | - "node_modules/parent-module": { | |
5670 | - "version": "1.0.1", | |
5671 | - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", | |
5672 | - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", | |
5673 | - "dependencies": { | |
5674 | - "callsites": "^3.0.0" | |
5675 | - }, | |
5676 | - "engines": { | |
5677 | - "node": ">=6" | |
5678 | - } | |
5679 | - }, | |
5680 | - "node_modules/parse-json": { | |
5681 | - "version": "5.2.0", | |
5682 | - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", | |
5683 | - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", | |
5684 | - "dependencies": { | |
5685 | - "@babel/code-frame": "^7.0.0", | |
5686 | - "error-ex": "^1.3.1", | |
5687 | - "json-parse-even-better-errors": "^2.3.0", | |
5688 | - "lines-and-columns": "^1.1.6" | |
5689 | - }, | |
5690 | - "engines": { | |
5691 | - "node": ">=8" | |
5692 | - }, | |
5693 | - "funding": { | |
5694 | - "url": "https://github.com/sponsors/sindresorhus" | |
5695 | - } | |
5696 | - }, | |
5697 | - "node_modules/path-browserify": { | |
5698 | - "version": "1.0.1", | |
5699 | - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", | |
5700 | - "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", | |
5701 | - "peer": true | |
5702 | - }, | |
5703 | - "node_modules/path-exists": { | |
5704 | - "version": "4.0.0", | |
5705 | - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", | |
5706 | - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", | |
5707 | - "dev": true, | |
5708 | - "engines": { | |
5709 | - "node": ">=8" | |
5710 | - } | |
5711 | - }, | |
5712 | - "node_modules/path-is-absolute": { | |
5713 | - "version": "1.0.1", | |
5714 | - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", | |
5715 | - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", | |
5716 | - "engines": { | |
5717 | - "node": ">=0.10.0" | |
5718 | - } | |
5719 | - }, | |
5720 | - "node_modules/path-key": { | |
5721 | - "version": "3.1.1", | |
5722 | - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", | |
5723 | - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", | |
5724 | - "dev": true, | |
5725 | - "engines": { | |
5726 | - "node": ">=8" | |
5727 | - } | |
5728 | - }, | |
5729 | - "node_modules/path-parse": { | |
5730 | - "version": "1.0.7", | |
5731 | - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", | |
5732 | - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" | |
5733 | - }, | |
5734 | - "node_modules/path-scurry": { | |
5735 | - "version": "1.10.2", | |
5736 | - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", | |
5737 | - "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", | |
5738 | - "dev": true, | |
5739 | - "dependencies": { | |
5740 | - "lru-cache": "^10.2.0", | |
5741 | - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" | |
5742 | - }, | |
5743 | - "engines": { | |
5744 | - "node": ">=16 || 14 >=14.17" | |
5745 | - }, | |
5746 | - "funding": { | |
5747 | - "url": "https://github.com/sponsors/isaacs" | |
5748 | - } | |
5749 | - }, | |
5750 | - "node_modules/path-scurry/node_modules/lru-cache": { | |
5751 | - "version": "10.2.2", | |
5752 | - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", | |
5753 | - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", | |
5754 | - "dev": true, | |
5755 | - "engines": { | |
5756 | - "node": "14 || >=16.14" | |
5757 | - } | |
5758 | - }, | |
5759 | - "node_modules/path-type": { | |
5760 | - "version": "4.0.0", | |
5761 | - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", | |
5762 | - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", | |
5763 | - "engines": { | |
5764 | - "node": ">=8" | |
5765 | - } | |
5766 | - }, | |
5767 | - "node_modules/picocolors": { | |
5768 | - "version": "1.0.0", | |
5769 | - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", | |
5770 | - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" | |
5771 | - }, | |
5772 | - "node_modules/picomatch": { | |
5773 | - "version": "2.3.1", | |
5774 | - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", | |
5775 | - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", | |
5776 | - "engines": { | |
5777 | - "node": ">=8.6" | |
5778 | - }, | |
5779 | - "funding": { | |
5780 | - "url": "https://github.com/sponsors/jonschlinkert" | |
5781 | - } | |
5782 | - }, | |
5783 | - "node_modules/pify": { | |
5784 | - "version": "2.3.0", | |
5785 | - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", | |
5786 | - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", | |
5787 | - "dev": true, | |
5788 | - "engines": { | |
5789 | - "node": ">=0.10.0" | |
5790 | - } | |
5791 | - }, | |
5792 | - "node_modules/pirates": { | |
5793 | - "version": "4.0.6", | |
5794 | - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", | |
5795 | - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", | |
5796 | - "dev": true, | |
5797 | - "engines": { | |
5798 | - "node": ">= 6" | |
5799 | - } | |
5800 | - }, | |
5801 | - "node_modules/possible-typed-array-names": { | |
5802 | - "version": "1.0.0", | |
5803 | - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", | |
5804 | - "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", | |
5805 | - "dev": true, | |
5806 | - "engines": { | |
5807 | - "node": ">= 0.4" | |
5808 | - } | |
5809 | - }, | |
5810 | - "node_modules/postcss": { | |
5811 | - "version": "8.4.38", | |
5812 | - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", | |
5813 | - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", | |
5814 | - "funding": [ | |
5815 | - { | |
5816 | - "type": "opencollective", | |
5817 | - "url": "https://opencollective.com/postcss/" | |
5818 | - }, | |
5819 | - { | |
5820 | - "type": "tidelift", | |
5821 | - "url": "https://tidelift.com/funding/github/npm/postcss" | |
5822 | - }, | |
5823 | - { | |
5824 | - "type": "github", | |
5825 | - "url": "https://github.com/sponsors/ai" | |
5826 | - } | |
5827 | - ], | |
5828 | - "dependencies": { | |
5829 | - "nanoid": "^3.3.7", | |
5830 | - "picocolors": "^1.0.0", | |
5831 | - "source-map-js": "^1.2.0" | |
5832 | - }, | |
5833 | - "engines": { | |
5834 | - "node": "^10 || ^12 || >=14" | |
5835 | - } | |
5836 | - }, | |
5837 | - "node_modules/postcss-import": { | |
5838 | - "version": "15.1.0", | |
5839 | - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", | |
5840 | - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", | |
5841 | - "dev": true, | |
5842 | - "dependencies": { | |
5843 | - "postcss-value-parser": "^4.0.0", | |
5844 | - "read-cache": "^1.0.0", | |
5845 | - "resolve": "^1.1.7" | |
5846 | - }, | |
5847 | - "engines": { | |
5848 | - "node": ">=14.0.0" | |
5849 | - }, | |
5850 | - "peerDependencies": { | |
5851 | - "postcss": "^8.0.0" | |
5852 | - } | |
5853 | - }, | |
5854 | - "node_modules/postcss-import/node_modules/resolve": { | |
5855 | - "version": "1.22.8", | |
5856 | - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", | |
5857 | - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", | |
5858 | - "dev": true, | |
5859 | - "dependencies": { | |
5860 | - "is-core-module": "^2.13.0", | |
5861 | - "path-parse": "^1.0.7", | |
5862 | - "supports-preserve-symlinks-flag": "^1.0.0" | |
5863 | - }, | |
5864 | - "bin": { | |
5865 | - "resolve": "bin/resolve" | |
5866 | - }, | |
5867 | - "funding": { | |
5868 | - "url": "https://github.com/sponsors/ljharb" | |
5869 | - } | |
5870 | - }, | |
5871 | - "node_modules/postcss-js": { | |
5872 | - "version": "4.0.1", | |
5873 | - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", | |
5874 | - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", | |
5875 | - "dev": true, | |
5876 | - "dependencies": { | |
5877 | - "camelcase-css": "^2.0.1" | |
5878 | - }, | |
5879 | - "engines": { | |
5880 | - "node": "^12 || ^14 || >= 16" | |
5881 | - }, | |
5882 | - "funding": { | |
5883 | - "type": "opencollective", | |
5884 | - "url": "https://opencollective.com/postcss/" | |
5885 | - }, | |
5886 | - "peerDependencies": { | |
5887 | - "postcss": "^8.4.21" | |
5888 | - } | |
5889 | - }, | |
5890 | - "node_modules/postcss-load-config": { | |
5891 | - "version": "4.0.2", | |
5892 | - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", | |
5893 | - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", | |
5894 | - "dev": true, | |
5895 | - "funding": [ | |
5896 | - { | |
5897 | - "type": "opencollective", | |
5898 | - "url": "https://opencollective.com/postcss/" | |
5899 | - }, | |
5900 | - { | |
5901 | - "type": "github", | |
5902 | - "url": "https://github.com/sponsors/ai" | |
5903 | - } | |
5904 | - ], | |
5905 | - "dependencies": { | |
5906 | - "lilconfig": "^3.0.0", | |
5907 | - "yaml": "^2.3.4" | |
5908 | - }, | |
5909 | - "engines": { | |
5910 | - "node": ">= 14" | |
5911 | - }, | |
5912 | - "peerDependencies": { | |
5913 | - "postcss": ">=8.0.9", | |
5914 | - "ts-node": ">=9.0.0" | |
5915 | - }, | |
5916 | - "peerDependenciesMeta": { | |
5917 | - "postcss": { | |
5918 | - "optional": true | |
5919 | - }, | |
5920 | - "ts-node": { | |
5921 | - "optional": true | |
5922 | - } | |
5923 | - } | |
5924 | - }, | |
5925 | - "node_modules/postcss-load-config/node_modules/lilconfig": { | |
5926 | - "version": "3.1.1", | |
5927 | - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.1.tgz", | |
5928 | - "integrity": "sha512-O18pf7nyvHTckunPWCV1XUNXU1piu01y2b7ATJ0ppkUkk8ocqVWBrYjJBCwHDjD/ZWcfyrA0P4gKhzWGi5EINQ==", | |
5929 | - "dev": true, | |
5930 | - "engines": { | |
5931 | - "node": ">=14" | |
5932 | - }, | |
5933 | - "funding": { | |
5934 | - "url": "https://github.com/sponsors/antonk52" | |
5935 | - } | |
5936 | - }, | |
5937 | - "node_modules/postcss-load-config/node_modules/yaml": { | |
5938 | - "version": "2.4.2", | |
5939 | - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.2.tgz", | |
5940 | - "integrity": "sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==", | |
5941 | - "dev": true, | |
5942 | - "bin": { | |
5943 | - "yaml": "bin.mjs" | |
5944 | - }, | |
5945 | - "engines": { | |
5946 | - "node": ">= 14" | |
5947 | - } | |
5948 | - }, | |
5949 | - "node_modules/postcss-nested": { | |
5950 | - "version": "6.0.1", | |
5951 | - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", | |
5952 | - "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", | |
5953 | - "dev": true, | |
5954 | - "dependencies": { | |
5955 | - "postcss-selector-parser": "^6.0.11" | |
5956 | - }, | |
5957 | - "engines": { | |
5958 | - "node": ">=12.0" | |
5959 | - }, | |
5960 | - "funding": { | |
5961 | - "type": "opencollective", | |
5962 | - "url": "https://opencollective.com/postcss/" | |
5963 | - }, | |
5964 | - "peerDependencies": { | |
5965 | - "postcss": "^8.2.14" | |
5966 | - } | |
5967 | - }, | |
5968 | - "node_modules/postcss-selector-parser": { | |
5969 | - "version": "6.0.16", | |
5970 | - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.16.tgz", | |
5971 | - "integrity": "sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==", | |
5972 | - "dev": true, | |
5973 | - "dependencies": { | |
5974 | - "cssesc": "^3.0.0", | |
5975 | - "util-deprecate": "^1.0.2" | |
5976 | - }, | |
5977 | - "engines": { | |
5978 | - "node": ">=4" | |
5979 | - } | |
5980 | - }, | |
5981 | - "node_modules/postcss-value-parser": { | |
5982 | - "version": "4.2.0", | |
5983 | - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", | |
5984 | - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" | |
5985 | - }, | |
5986 | - "node_modules/prelude-ls": { | |
5987 | - "version": "1.2.1", | |
5988 | - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", | |
5989 | - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", | |
5990 | - "dev": true, | |
5991 | - "engines": { | |
5992 | - "node": ">= 0.8.0" | |
5993 | - } | |
5994 | - }, | |
5995 | - "node_modules/process-nextick-args": { | |
5996 | - "version": "2.0.1", | |
5997 | - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", | |
5998 | - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" | |
5999 | - }, | |
6000 | - "node_modules/prop-types": { | |
6001 | - "version": "15.8.1", | |
6002 | - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", | |
6003 | - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", | |
6004 | - "dependencies": { | |
6005 | - "loose-envify": "^1.4.0", | |
6006 | - "object-assign": "^4.1.1", | |
6007 | - "react-is": "^16.13.1" | |
6008 | - } | |
6009 | - }, | |
6010 | - "node_modules/proxy-from-env": { | |
6011 | - "version": "1.1.0", | |
6012 | - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", | |
6013 | - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" | |
6014 | - }, | |
6015 | - "node_modules/punycode": { | |
6016 | - "version": "2.3.1", | |
6017 | - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", | |
6018 | - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", | |
6019 | - "engines": { | |
6020 | - "node": ">=6" | |
6021 | - } | |
6022 | - }, | |
6023 | - "node_modules/queue-microtask": { | |
6024 | - "version": "1.2.3", | |
6025 | - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", | |
6026 | - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", | |
6027 | - "dev": true, | |
6028 | - "funding": [ | |
6029 | - { | |
6030 | - "type": "github", | |
6031 | - "url": "https://github.com/sponsors/feross" | |
6032 | - }, | |
6033 | - { | |
6034 | - "type": "patreon", | |
6035 | - "url": "https://www.patreon.com/feross" | |
6036 | - }, | |
6037 | - { | |
6038 | - "type": "consulting", | |
6039 | - "url": "https://feross.org/support" | |
6040 | - } | |
6041 | - ] | |
6042 | - }, | |
6043 | - "node_modules/react": { | |
6044 | - "version": "18.3.1", | |
6045 | - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", | |
6046 | - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", | |
6047 | - "dependencies": { | |
6048 | - "loose-envify": "^1.1.0" | |
6049 | - }, | |
6050 | - "engines": { | |
6051 | - "node": ">=0.10.0" | |
6052 | - } | |
6053 | - }, | |
6054 | - "node_modules/react-dom": { | |
6055 | - "version": "18.3.1", | |
6056 | - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", | |
6057 | - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", | |
6058 | - "dependencies": { | |
6059 | - "loose-envify": "^1.1.0", | |
6060 | - "scheduler": "^0.23.2" | |
6061 | - }, | |
6062 | - "peerDependencies": { | |
6063 | - "react": "^18.3.1" | |
6064 | - } | |
6065 | - }, | |
6066 | - "node_modules/react-fast-compare": { | |
6067 | - "version": "3.2.2", | |
6068 | - "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz", | |
6069 | - "integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==" | |
6070 | - }, | |
6071 | - "node_modules/react-is": { | |
6072 | - "version": "16.13.1", | |
6073 | - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", | |
6074 | - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" | |
6075 | - }, | |
6076 | - "node_modules/react-mui-sidebar": { | |
6077 | - "version": "1.3.5", | |
6078 | - "resolved": "https://registry.npmjs.org/react-mui-sidebar/-/react-mui-sidebar-1.3.5.tgz", | |
6079 | - "integrity": "sha512-pPmKJMiARFzC6SiXK0z9FhSkbion1BuBUGuQs7gzFzt7yB2LqL6JRVG7PmWGAEDt64191/M2NOkaIqg6/bsQWw==", | |
6080 | - "peerDependencies": { | |
6081 | - "@vitejs/plugin-react-swc": "^3.6.0", | |
6082 | - "react": "^18.2.0", | |
6083 | - "react-dom": "^18.2.0", | |
6084 | - "vite-plugin-dts": "^3.8.1" | |
6085 | - } | |
6086 | - }, | |
6087 | - "node_modules/react-popper": { | |
6088 | - "version": "2.3.0", | |
6089 | - "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", | |
6090 | - "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==", | |
6091 | - "dependencies": { | |
6092 | - "react-fast-compare": "^3.0.1", | |
6093 | - "warning": "^4.0.2" | |
6094 | - }, | |
6095 | - "peerDependencies": { | |
6096 | - "@popperjs/core": "^2.0.0", | |
6097 | - "react": "^16.8.0 || ^17 || ^18", | |
6098 | - "react-dom": "^16.8.0 || ^17 || ^18" | |
6099 | - } | |
6100 | - }, | |
6101 | - "node_modules/react-router": { | |
6102 | - "version": "6.23.0", | |
6103 | - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.0.tgz", | |
6104 | - "integrity": "sha512-wPMZ8S2TuPadH0sF5irFGjkNLIcRvOSaEe7v+JER8508dyJumm6XZB1u5kztlX0RVq6AzRVndzqcUh6sFIauzA==", | |
6105 | - "dependencies": { | |
6106 | - "@remix-run/router": "1.16.0" | |
6107 | - }, | |
6108 | - "engines": { | |
6109 | - "node": ">=14.0.0" | |
6110 | - }, | |
6111 | - "peerDependencies": { | |
6112 | - "react": ">=16.8" | |
6113 | - } | |
6114 | - }, | |
6115 | - "node_modules/react-router-dom": { | |
6116 | - "version": "6.23.0", | |
6117 | - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.23.0.tgz", | |
6118 | - "integrity": "sha512-Q9YaSYvubwgbal2c9DJKfx6hTNoBp3iJDsl+Duva/DwxoJH+OTXkxGpql4iUK2sla/8z4RpjAm6EWx1qUDuopQ==", | |
6119 | - "dependencies": { | |
6120 | - "@remix-run/router": "1.16.0", | |
6121 | - "react-router": "6.23.0" | |
6122 | - }, | |
6123 | - "engines": { | |
6124 | - "node": ">=14.0.0" | |
6125 | - }, | |
6126 | - "peerDependencies": { | |
6127 | - "react": ">=16.8", | |
6128 | - "react-dom": ">=16.8" | |
6129 | - } | |
6130 | - }, | |
6131 | - "node_modules/react-transition-group": { | |
6132 | - "version": "4.4.5", | |
6133 | - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", | |
6134 | - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", | |
6135 | - "dependencies": { | |
6136 | - "@babel/runtime": "^7.5.5", | |
6137 | - "dom-helpers": "^5.0.1", | |
6138 | - "loose-envify": "^1.4.0", | |
6139 | - "prop-types": "^15.6.2" | |
6140 | - }, | |
6141 | - "peerDependencies": { | |
6142 | - "react": ">=16.6.0", | |
6143 | - "react-dom": ">=16.6.0" | |
6144 | - } | |
6145 | - }, | |
6146 | - "node_modules/read-cache": { | |
6147 | - "version": "1.0.0", | |
6148 | - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", | |
6149 | - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", | |
6150 | - "dev": true, | |
6151 | - "dependencies": { | |
6152 | - "pify": "^2.3.0" | |
6153 | - } | |
6154 | - }, | |
6155 | - "node_modules/readable-stream": { | |
6156 | - "version": "3.6.2", | |
6157 | - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", | |
6158 | - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", | |
6159 | - "dependencies": { | |
6160 | - "inherits": "^2.0.3", | |
6161 | - "string_decoder": "^1.1.1", | |
6162 | - "util-deprecate": "^1.0.1" | |
6163 | - }, | |
6164 | - "engines": { | |
6165 | - "node": ">= 6" | |
6166 | - } | |
6167 | - }, | |
6168 | - "node_modules/readdir-glob": { | |
6169 | - "version": "1.1.3", | |
6170 | - "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", | |
6171 | - "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", | |
6172 | - "dependencies": { | |
6173 | - "minimatch": "^5.1.0" | |
6174 | - } | |
6175 | - }, | |
6176 | - "node_modules/readdir-glob/node_modules/brace-expansion": { | |
6177 | - "version": "2.0.1", | |
6178 | - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", | |
6179 | - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", | |
6180 | - "dependencies": { | |
6181 | - "balanced-match": "^1.0.0" | |
6182 | - } | |
6183 | - }, | |
6184 | - "node_modules/readdir-glob/node_modules/minimatch": { | |
6185 | - "version": "5.1.6", | |
6186 | - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", | |
6187 | - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", | |
6188 | - "dependencies": { | |
6189 | - "brace-expansion": "^2.0.1" | |
6190 | - }, | |
6191 | - "engines": { | |
6192 | - "node": ">=10" | |
6193 | - } | |
6194 | - }, | |
6195 | - "node_modules/readdirp": { | |
6196 | - "version": "3.6.0", | |
6197 | - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", | |
6198 | - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", | |
6199 | - "dev": true, | |
6200 | - "dependencies": { | |
6201 | - "picomatch": "^2.2.1" | |
6202 | - }, | |
6203 | - "engines": { | |
6204 | - "node": ">=8.10.0" | |
6205 | - } | |
6206 | - }, | |
6207 | - "node_modules/reflect.getprototypeof": { | |
6208 | - "version": "1.0.6", | |
6209 | - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.6.tgz", | |
6210 | - "integrity": "sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==", | |
6211 | - "dev": true, | |
6212 | - "dependencies": { | |
6213 | - "call-bind": "^1.0.7", | |
6214 | - "define-properties": "^1.2.1", | |
6215 | - "es-abstract": "^1.23.1", | |
6216 | - "es-errors": "^1.3.0", | |
6217 | - "get-intrinsic": "^1.2.4", | |
6218 | - "globalthis": "^1.0.3", | |
6219 | - "which-builtin-type": "^1.1.3" | |
6220 | - }, | |
6221 | - "engines": { | |
6222 | - "node": ">= 0.4" | |
6223 | - }, | |
6224 | - "funding": { | |
6225 | - "url": "https://github.com/sponsors/ljharb" | |
6226 | - } | |
6227 | - }, | |
6228 | - "node_modules/regenerator-runtime": { | |
6229 | - "version": "0.14.1", | |
6230 | - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", | |
6231 | - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" | |
6232 | - }, | |
6233 | - "node_modules/regexp.prototype.flags": { | |
6234 | - "version": "1.5.2", | |
6235 | - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", | |
6236 | - "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", | |
6237 | - "dev": true, | |
6238 | - "dependencies": { | |
6239 | - "call-bind": "^1.0.6", | |
6240 | - "define-properties": "^1.2.1", | |
6241 | - "es-errors": "^1.3.0", | |
6242 | - "set-function-name": "^2.0.1" | |
6243 | - }, | |
6244 | - "engines": { | |
6245 | - "node": ">= 0.4" | |
6246 | - }, | |
6247 | - "funding": { | |
6248 | - "url": "https://github.com/sponsors/ljharb" | |
6249 | - } | |
6250 | - }, | |
6251 | - "node_modules/reselect": { | |
6252 | - "version": "4.1.8", | |
6253 | - "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", | |
6254 | - "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" | |
6255 | - }, | |
6256 | - "node_modules/resolve": { | |
6257 | - "version": "2.0.0-next.5", | |
6258 | - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", | |
6259 | - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", | |
6260 | - "dev": true, | |
6261 | - "dependencies": { | |
6262 | - "is-core-module": "^2.13.0", | |
6263 | - "path-parse": "^1.0.7", | |
6264 | - "supports-preserve-symlinks-flag": "^1.0.0" | |
6265 | - }, | |
6266 | - "bin": { | |
6267 | - "resolve": "bin/resolve" | |
6268 | - }, | |
6269 | - "funding": { | |
6270 | - "url": "https://github.com/sponsors/ljharb" | |
6271 | - } | |
6272 | - }, | |
6273 | - "node_modules/resolve-from": { | |
6274 | - "version": "4.0.0", | |
6275 | - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", | |
6276 | - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", | |
6277 | - "engines": { | |
6278 | - "node": ">=4" | |
6279 | - } | |
6280 | - }, | |
6281 | - "node_modules/reusify": { | |
6282 | - "version": "1.0.4", | |
6283 | - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", | |
6284 | - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", | |
6285 | - "dev": true, | |
6286 | - "engines": { | |
6287 | - "iojs": ">=1.0.0", | |
6288 | - "node": ">=0.10.0" | |
6289 | - } | |
6290 | - }, | |
6291 | - "node_modules/rimraf": { | |
6292 | - "version": "3.0.2", | |
6293 | - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", | |
6294 | - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", | |
6295 | - "dev": true, | |
6296 | - "dependencies": { | |
6297 | - "glob": "^7.1.3" | |
6298 | - }, | |
6299 | - "bin": { | |
6300 | - "rimraf": "bin.js" | |
6301 | - }, | |
6302 | - "funding": { | |
6303 | - "url": "https://github.com/sponsors/isaacs" | |
6304 | - } | |
6305 | - }, | |
6306 | - "node_modules/rollup": { | |
6307 | - "version": "4.17.2", | |
6308 | - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.17.2.tgz", | |
6309 | - "integrity": "sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==", | |
6310 | - "dependencies": { | |
6311 | - "@types/estree": "1.0.5" | |
6312 | - }, | |
6313 | - "bin": { | |
6314 | - "rollup": "dist/bin/rollup" | |
6315 | - }, | |
6316 | - "engines": { | |
6317 | - "node": ">=18.0.0", | |
6318 | - "npm": ">=8.0.0" | |
6319 | - }, | |
6320 | - "optionalDependencies": { | |
6321 | - "@rollup/rollup-android-arm-eabi": "4.17.2", | |
6322 | - "@rollup/rollup-android-arm64": "4.17.2", | |
6323 | - "@rollup/rollup-darwin-arm64": "4.17.2", | |
6324 | - "@rollup/rollup-darwin-x64": "4.17.2", | |
6325 | - "@rollup/rollup-linux-arm-gnueabihf": "4.17.2", | |
6326 | - "@rollup/rollup-linux-arm-musleabihf": "4.17.2", | |
6327 | - "@rollup/rollup-linux-arm64-gnu": "4.17.2", | |
6328 | - "@rollup/rollup-linux-arm64-musl": "4.17.2", | |
6329 | - "@rollup/rollup-linux-powerpc64le-gnu": "4.17.2", | |
6330 | - "@rollup/rollup-linux-riscv64-gnu": "4.17.2", | |
6331 | - "@rollup/rollup-linux-s390x-gnu": "4.17.2", | |
6332 | - "@rollup/rollup-linux-x64-gnu": "4.17.2", | |
6333 | - "@rollup/rollup-linux-x64-musl": "4.17.2", | |
6334 | - "@rollup/rollup-win32-arm64-msvc": "4.17.2", | |
6335 | - "@rollup/rollup-win32-ia32-msvc": "4.17.2", | |
6336 | - "@rollup/rollup-win32-x64-msvc": "4.17.2", | |
6337 | - "fsevents": "~2.3.2" | |
6338 | - } | |
6339 | - }, | |
6340 | - "node_modules/run-parallel": { | |
6341 | - "version": "1.2.0", | |
6342 | - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", | |
6343 | - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", | |
6344 | - "dev": true, | |
6345 | - "funding": [ | |
6346 | - { | |
6347 | - "type": "github", | |
6348 | - "url": "https://github.com/sponsors/feross" | |
6349 | - }, | |
6350 | - { | |
6351 | - "type": "patreon", | |
6352 | - "url": "https://www.patreon.com/feross" | |
6353 | - }, | |
6354 | - { | |
6355 | - "type": "consulting", | |
6356 | - "url": "https://feross.org/support" | |
6357 | - } | |
6358 | - ], | |
6359 | - "dependencies": { | |
6360 | - "queue-microtask": "^1.2.2" | |
6361 | - } | |
6362 | - }, | |
6363 | - "node_modules/safe-array-concat": { | |
6364 | - "version": "1.1.2", | |
6365 | - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.2.tgz", | |
6366 | - "integrity": "sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q==", | |
6367 | - "dev": true, | |
6368 | - "dependencies": { | |
6369 | - "call-bind": "^1.0.7", | |
6370 | - "get-intrinsic": "^1.2.4", | |
6371 | - "has-symbols": "^1.0.3", | |
6372 | - "isarray": "^2.0.5" | |
6373 | - }, | |
6374 | - "engines": { | |
6375 | - "node": ">=0.4" | |
6376 | - }, | |
6377 | - "funding": { | |
6378 | - "url": "https://github.com/sponsors/ljharb" | |
6379 | - } | |
6380 | - }, | |
6381 | - "node_modules/safe-buffer": { | |
6382 | - "version": "5.2.1", | |
6383 | - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", | |
6384 | - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", | |
6385 | - "funding": [ | |
6386 | - { | |
6387 | - "type": "github", | |
6388 | - "url": "https://github.com/sponsors/feross" | |
6389 | - }, | |
6390 | - { | |
6391 | - "type": "patreon", | |
6392 | - "url": "https://www.patreon.com/feross" | |
6393 | - }, | |
6394 | - { | |
6395 | - "type": "consulting", | |
6396 | - "url": "https://feross.org/support" | |
6397 | - } | |
6398 | - ] | |
6399 | - }, | |
6400 | - "node_modules/safe-regex-test": { | |
6401 | - "version": "1.0.3", | |
6402 | - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz", | |
6403 | - "integrity": "sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==", | |
6404 | - "dev": true, | |
6405 | - "dependencies": { | |
6406 | - "call-bind": "^1.0.6", | |
6407 | - "es-errors": "^1.3.0", | |
6408 | - "is-regex": "^1.1.4" | |
6409 | - }, | |
6410 | - "engines": { | |
6411 | - "node": ">= 0.4" | |
6412 | - }, | |
6413 | - "funding": { | |
6414 | - "url": "https://github.com/sponsors/ljharb" | |
6415 | - } | |
6416 | - }, | |
6417 | - "node_modules/saxes": { | |
6418 | - "version": "5.0.1", | |
6419 | - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", | |
6420 | - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", | |
6421 | - "dependencies": { | |
6422 | - "xmlchars": "^2.2.0" | |
6423 | - }, | |
6424 | - "engines": { | |
6425 | - "node": ">=10" | |
6426 | - } | |
6427 | - }, | |
6428 | - "node_modules/scheduler": { | |
6429 | - "version": "0.23.2", | |
6430 | - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", | |
6431 | - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", | |
6432 | - "dependencies": { | |
6433 | - "loose-envify": "^1.1.0" | |
6434 | - } | |
6435 | - }, | |
6436 | - "node_modules/semver": { | |
6437 | - "version": "6.3.1", | |
6438 | - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", | |
6439 | - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", | |
6440 | - "dev": true, | |
6441 | - "bin": { | |
6442 | - "semver": "bin/semver.js" | |
6443 | - } | |
6444 | - }, | |
6445 | - "node_modules/set-function-length": { | |
6446 | - "version": "1.2.2", | |
6447 | - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", | |
6448 | - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", | |
6449 | - "dev": true, | |
6450 | - "dependencies": { | |
6451 | - "define-data-property": "^1.1.4", | |
6452 | - "es-errors": "^1.3.0", | |
6453 | - "function-bind": "^1.1.2", | |
6454 | - "get-intrinsic": "^1.2.4", | |
6455 | - "gopd": "^1.0.1", | |
6456 | - "has-property-descriptors": "^1.0.2" | |
6457 | - }, | |
6458 | - "engines": { | |
6459 | - "node": ">= 0.4" | |
6460 | - } | |
6461 | - }, | |
6462 | - "node_modules/set-function-name": { | |
6463 | - "version": "2.0.2", | |
6464 | - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", | |
6465 | - "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", | |
6466 | - "dev": true, | |
6467 | - "dependencies": { | |
6468 | - "define-data-property": "^1.1.4", | |
6469 | - "es-errors": "^1.3.0", | |
6470 | - "functions-have-names": "^1.2.3", | |
6471 | - "has-property-descriptors": "^1.0.2" | |
6472 | - }, | |
6473 | - "engines": { | |
6474 | - "node": ">= 0.4" | |
6475 | - } | |
6476 | - }, | |
6477 | - "node_modules/setimmediate": { | |
6478 | - "version": "1.0.5", | |
6479 | - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", | |
6480 | - "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" | |
6481 | - }, | |
6482 | - "node_modules/shallowequal": { | |
6483 | - "version": "1.1.0", | |
6484 | - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", | |
6485 | - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" | |
6486 | - }, | |
6487 | - "node_modules/shebang-command": { | |
6488 | - "version": "2.0.0", | |
6489 | - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", | |
6490 | - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", | |
6491 | - "dev": true, | |
6492 | - "dependencies": { | |
6493 | - "shebang-regex": "^3.0.0" | |
6494 | - }, | |
6495 | - "engines": { | |
6496 | - "node": ">=8" | |
6497 | - } | |
6498 | - }, | |
6499 | - "node_modules/shebang-regex": { | |
6500 | - "version": "3.0.0", | |
6501 | - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", | |
6502 | - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", | |
6503 | - "dev": true, | |
6504 | - "engines": { | |
6505 | - "node": ">=8" | |
6506 | - } | |
6507 | - }, | |
6508 | - "node_modules/side-channel": { | |
6509 | - "version": "1.0.6", | |
6510 | - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", | |
6511 | - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", | |
6512 | - "dev": true, | |
6513 | - "dependencies": { | |
6514 | - "call-bind": "^1.0.7", | |
6515 | - "es-errors": "^1.3.0", | |
6516 | - "get-intrinsic": "^1.2.4", | |
6517 | - "object-inspect": "^1.13.1" | |
6518 | - }, | |
6519 | - "engines": { | |
6520 | - "node": ">= 0.4" | |
6521 | - }, | |
6522 | - "funding": { | |
6523 | - "url": "https://github.com/sponsors/ljharb" | |
6524 | - } | |
6525 | - }, | |
6526 | - "node_modules/signal-exit": { | |
6527 | - "version": "4.1.0", | |
6528 | - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", | |
6529 | - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", | |
6530 | - "dev": true, | |
6531 | - "engines": { | |
6532 | - "node": ">=14" | |
6533 | - }, | |
6534 | - "funding": { | |
6535 | - "url": "https://github.com/sponsors/isaacs" | |
6536 | - } | |
6537 | - }, | |
6538 | - "node_modules/source-map": { | |
6539 | - "version": "0.6.1", | |
6540 | - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", | |
6541 | - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", | |
6542 | - "peer": true, | |
6543 | - "engines": { | |
6544 | - "node": ">=0.10.0" | |
6545 | - } | |
6546 | - }, | |
6547 | - "node_modules/source-map-js": { | |
6548 | - "version": "1.2.0", | |
6549 | - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", | |
6550 | - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", | |
6551 | - "engines": { | |
6552 | - "node": ">=0.10.0" | |
6553 | - } | |
6554 | - }, | |
6555 | - "node_modules/sprintf-js": { | |
6556 | - "version": "1.0.3", | |
6557 | - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", | |
6558 | - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", | |
6559 | - "peer": true | |
6560 | - }, | |
6561 | - "node_modules/string_decoder": { | |
6562 | - "version": "1.3.0", | |
6563 | - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", | |
6564 | - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", | |
6565 | - "dependencies": { | |
6566 | - "safe-buffer": "~5.2.0" | |
6567 | - } | |
6568 | - }, | |
6569 | - "node_modules/string-argv": { | |
6570 | - "version": "0.3.2", | |
6571 | - "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", | |
6572 | - "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", | |
6573 | - "peer": true, | |
6574 | - "engines": { | |
6575 | - "node": ">=0.6.19" | |
6576 | - } | |
6577 | - }, | |
6578 | - "node_modules/string-width": { | |
6579 | - "version": "5.1.2", | |
6580 | - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", | |
6581 | - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", | |
6582 | - "dev": true, | |
6583 | - "dependencies": { | |
6584 | - "eastasianwidth": "^0.2.0", | |
6585 | - "emoji-regex": "^9.2.2", | |
6586 | - "strip-ansi": "^7.0.1" | |
6587 | - }, | |
6588 | - "engines": { | |
6589 | - "node": ">=12" | |
6590 | - }, | |
6591 | - "funding": { | |
6592 | - "url": "https://github.com/sponsors/sindresorhus" | |
6593 | - } | |
6594 | - }, | |
6595 | - "node_modules/string-width-cjs": { | |
6596 | - "name": "string-width", | |
6597 | - "version": "4.2.3", | |
6598 | - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", | |
6599 | - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", | |
6600 | - "dev": true, | |
6601 | - "dependencies": { | |
6602 | - "emoji-regex": "^8.0.0", | |
6603 | - "is-fullwidth-code-point": "^3.0.0", | |
6604 | - "strip-ansi": "^6.0.1" | |
6605 | - }, | |
6606 | - "engines": { | |
6607 | - "node": ">=8" | |
6608 | - } | |
6609 | - }, | |
6610 | - "node_modules/string-width-cjs/node_modules/emoji-regex": { | |
6611 | - "version": "8.0.0", | |
6612 | - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", | |
6613 | - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", | |
6614 | - "dev": true | |
6615 | - }, | |
6616 | - "node_modules/string-width/node_modules/ansi-regex": { | |
6617 | - "version": "6.0.1", | |
6618 | - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", | |
6619 | - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", | |
6620 | - "dev": true, | |
6621 | - "engines": { | |
6622 | - "node": ">=12" | |
6623 | - }, | |
6624 | - "funding": { | |
6625 | - "url": "https://github.com/chalk/ansi-regex?sponsor=1" | |
6626 | - } | |
6627 | - }, | |
6628 | - "node_modules/string-width/node_modules/strip-ansi": { | |
6629 | - "version": "7.1.0", | |
6630 | - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", | |
6631 | - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", | |
6632 | - "dev": true, | |
6633 | - "dependencies": { | |
6634 | - "ansi-regex": "^6.0.1" | |
6635 | - }, | |
6636 | - "engines": { | |
6637 | - "node": ">=12" | |
6638 | - }, | |
6639 | - "funding": { | |
6640 | - "url": "https://github.com/chalk/strip-ansi?sponsor=1" | |
6641 | - } | |
6642 | - }, | |
6643 | - "node_modules/string.prototype.matchall": { | |
6644 | - "version": "4.0.11", | |
6645 | - "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.11.tgz", | |
6646 | - "integrity": "sha512-NUdh0aDavY2og7IbBPenWqR9exH+E26Sv8e0/eTe1tltDGZL+GtBkDAnnyBtmekfK6/Dq3MkcGtzXFEd1LQrtg==", | |
6647 | - "dev": true, | |
6648 | - "dependencies": { | |
6649 | - "call-bind": "^1.0.7", | |
6650 | - "define-properties": "^1.2.1", | |
6651 | - "es-abstract": "^1.23.2", | |
6652 | - "es-errors": "^1.3.0", | |
6653 | - "es-object-atoms": "^1.0.0", | |
6654 | - "get-intrinsic": "^1.2.4", | |
6655 | - "gopd": "^1.0.1", | |
6656 | - "has-symbols": "^1.0.3", | |
6657 | - "internal-slot": "^1.0.7", | |
6658 | - "regexp.prototype.flags": "^1.5.2", | |
6659 | - "set-function-name": "^2.0.2", | |
6660 | - "side-channel": "^1.0.6" | |
6661 | - }, | |
6662 | - "engines": { | |
6663 | - "node": ">= 0.4" | |
6664 | - }, | |
6665 | - "funding": { | |
6666 | - "url": "https://github.com/sponsors/ljharb" | |
6667 | - } | |
6668 | - }, | |
6669 | - "node_modules/string.prototype.trim": { | |
6670 | - "version": "1.2.9", | |
6671 | - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz", | |
6672 | - "integrity": "sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw==", | |
6673 | - "dev": true, | |
6674 | - "dependencies": { | |
6675 | - "call-bind": "^1.0.7", | |
6676 | - "define-properties": "^1.2.1", | |
6677 | - "es-abstract": "^1.23.0", | |
6678 | - "es-object-atoms": "^1.0.0" | |
6679 | - }, | |
6680 | - "engines": { | |
6681 | - "node": ">= 0.4" | |
6682 | - }, | |
6683 | - "funding": { | |
6684 | - "url": "https://github.com/sponsors/ljharb" | |
6685 | - } | |
6686 | - }, | |
6687 | - "node_modules/string.prototype.trimend": { | |
6688 | - "version": "1.0.8", | |
6689 | - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz", | |
6690 | - "integrity": "sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ==", | |
6691 | - "dev": true, | |
6692 | - "dependencies": { | |
6693 | - "call-bind": "^1.0.7", | |
6694 | - "define-properties": "^1.2.1", | |
6695 | - "es-object-atoms": "^1.0.0" | |
6696 | - }, | |
6697 | - "funding": { | |
6698 | - "url": "https://github.com/sponsors/ljharb" | |
6699 | - } | |
6700 | - }, | |
6701 | - "node_modules/string.prototype.trimstart": { | |
6702 | - "version": "1.0.8", | |
6703 | - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", | |
6704 | - "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", | |
6705 | - "dev": true, | |
6706 | - "dependencies": { | |
6707 | - "call-bind": "^1.0.7", | |
6708 | - "define-properties": "^1.2.1", | |
6709 | - "es-object-atoms": "^1.0.0" | |
6710 | - }, | |
6711 | - "engines": { | |
6712 | - "node": ">= 0.4" | |
6713 | - }, | |
6714 | - "funding": { | |
6715 | - "url": "https://github.com/sponsors/ljharb" | |
6716 | - } | |
6717 | - }, | |
6718 | - "node_modules/strip-ansi": { | |
6719 | - "version": "6.0.1", | |
6720 | - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", | |
6721 | - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", | |
6722 | - "dev": true, | |
6723 | - "dependencies": { | |
6724 | - "ansi-regex": "^5.0.1" | |
6725 | - }, | |
6726 | - "engines": { | |
6727 | - "node": ">=8" | |
6728 | - } | |
6729 | - }, | |
6730 | - "node_modules/strip-ansi-cjs": { | |
6731 | - "name": "strip-ansi", | |
6732 | - "version": "6.0.1", | |
6733 | - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", | |
6734 | - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", | |
6735 | - "dev": true, | |
6736 | - "dependencies": { | |
6737 | - "ansi-regex": "^5.0.1" | |
6738 | - }, | |
6739 | - "engines": { | |
6740 | - "node": ">=8" | |
6741 | - } | |
6742 | - }, | |
6743 | - "node_modules/strip-json-comments": { | |
6744 | - "version": "3.1.1", | |
6745 | - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", | |
6746 | - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", | |
6747 | - "engines": { | |
6748 | - "node": ">=8" | |
6749 | - }, | |
6750 | - "funding": { | |
6751 | - "url": "https://github.com/sponsors/sindresorhus" | |
6752 | - } | |
6753 | - }, | |
6754 | - "node_modules/styled-components": { | |
6755 | - "version": "6.1.11", | |
6756 | - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.11.tgz", | |
6757 | - "integrity": "sha512-Ui0jXPzbp1phYij90h12ksljKGqF8ncGx+pjrNPsSPhbUUjWT2tD1FwGo2LF6USCnbrsIhNngDfodhxbegfEOA==", | |
6758 | - "dependencies": { | |
6759 | - "@emotion/is-prop-valid": "1.2.2", | |
6760 | - "@emotion/unitless": "0.8.1", | |
6761 | - "@types/stylis": "4.2.5", | |
6762 | - "css-to-react-native": "3.2.0", | |
6763 | - "csstype": "3.1.3", | |
6764 | - "postcss": "8.4.38", | |
6765 | - "shallowequal": "1.1.0", | |
6766 | - "stylis": "4.3.2", | |
6767 | - "tslib": "2.6.2" | |
6768 | - }, | |
6769 | - "engines": { | |
6770 | - "node": ">= 16" | |
6771 | - }, | |
6772 | - "funding": { | |
6773 | - "type": "opencollective", | |
6774 | - "url": "https://opencollective.com/styled-components" | |
6775 | - }, | |
6776 | - "peerDependencies": { | |
6777 | - "react": ">= 16.8.0", | |
6778 | - "react-dom": ">= 16.8.0" | |
6779 | - } | |
6780 | - }, | |
6781 | - "node_modules/styled-components/node_modules/stylis": { | |
6782 | - "version": "4.3.2", | |
6783 | - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", | |
6784 | - "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==" | |
6785 | - }, | |
6786 | - "node_modules/stylis": { | |
6787 | - "version": "4.2.0", | |
6788 | - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", | |
6789 | - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" | |
6790 | - }, | |
6791 | - "node_modules/sucrase": { | |
6792 | - "version": "3.35.0", | |
6793 | - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", | |
6794 | - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", | |
6795 | - "dev": true, | |
6796 | - "dependencies": { | |
6797 | - "@jridgewell/gen-mapping": "^0.3.2", | |
6798 | - "commander": "^4.0.0", | |
6799 | - "glob": "^10.3.10", | |
6800 | - "lines-and-columns": "^1.1.6", | |
6801 | - "mz": "^2.7.0", | |
6802 | - "pirates": "^4.0.1", | |
6803 | - "ts-interface-checker": "^0.1.9" | |
6804 | - }, | |
6805 | - "bin": { | |
6806 | - "sucrase": "bin/sucrase", | |
6807 | - "sucrase-node": "bin/sucrase-node" | |
6808 | - }, | |
6809 | - "engines": { | |
6810 | - "node": ">=16 || 14 >=14.17" | |
6811 | - } | |
6812 | - }, | |
6813 | - "node_modules/sucrase/node_modules/brace-expansion": { | |
6814 | - "version": "2.0.1", | |
6815 | - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", | |
6816 | - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", | |
6817 | - "dev": true, | |
6818 | - "dependencies": { | |
6819 | - "balanced-match": "^1.0.0" | |
6820 | - } | |
6821 | - }, | |
6822 | - "node_modules/sucrase/node_modules/commander": { | |
6823 | - "version": "4.1.1", | |
6824 | - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", | |
6825 | - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", | |
6826 | - "dev": true, | |
6827 | - "engines": { | |
6828 | - "node": ">= 6" | |
6829 | - } | |
6830 | - }, | |
6831 | - "node_modules/sucrase/node_modules/glob": { | |
6832 | - "version": "10.3.12", | |
6833 | - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", | |
6834 | - "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", | |
6835 | - "dev": true, | |
6836 | - "dependencies": { | |
6837 | - "foreground-child": "^3.1.0", | |
6838 | - "jackspeak": "^2.3.6", | |
6839 | - "minimatch": "^9.0.1", | |
6840 | - "minipass": "^7.0.4", | |
6841 | - "path-scurry": "^1.10.2" | |
6842 | - }, | |
6843 | - "bin": { | |
6844 | - "glob": "dist/esm/bin.mjs" | |
6845 | - }, | |
6846 | - "engines": { | |
6847 | - "node": ">=16 || 14 >=14.17" | |
6848 | - }, | |
6849 | - "funding": { | |
6850 | - "url": "https://github.com/sponsors/isaacs" | |
6851 | - } | |
6852 | - }, | |
6853 | - "node_modules/sucrase/node_modules/minimatch": { | |
6854 | - "version": "9.0.4", | |
6855 | - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", | |
6856 | - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", | |
6857 | - "dev": true, | |
6858 | - "dependencies": { | |
6859 | - "brace-expansion": "^2.0.1" | |
6860 | - }, | |
6861 | - "engines": { | |
6862 | - "node": ">=16 || 14 >=14.17" | |
6863 | - }, | |
6864 | - "funding": { | |
6865 | - "url": "https://github.com/sponsors/isaacs" | |
6866 | - } | |
6867 | - }, | |
6868 | - "node_modules/supports-color": { | |
6869 | - "version": "7.2.0", | |
6870 | - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", | |
6871 | - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", | |
6872 | - "dev": true, | |
6873 | - "dependencies": { | |
6874 | - "has-flag": "^4.0.0" | |
6875 | - }, | |
6876 | - "engines": { | |
6877 | - "node": ">=8" | |
6878 | - } | |
6879 | - }, | |
6880 | - "node_modules/supports-preserve-symlinks-flag": { | |
6881 | - "version": "1.0.0", | |
6882 | - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", | |
6883 | - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", | |
6884 | - "engines": { | |
6885 | - "node": ">= 0.4" | |
6886 | - }, | |
6887 | - "funding": { | |
6888 | - "url": "https://github.com/sponsors/ljharb" | |
6889 | - } | |
6890 | - }, | |
6891 | - "node_modules/tailwindcss": { | |
6892 | - "version": "3.4.3", | |
6893 | - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz", | |
6894 | - "integrity": "sha512-U7sxQk/n397Bmx4JHbJx/iSOOv5G+II3f1kpLpY2QeUv5DcPdcTsYLlusZfq1NthHS1c1cZoyFmmkex1rzke0A==", | |
6895 | - "dev": true, | |
6896 | - "dependencies": { | |
6897 | - "@alloc/quick-lru": "^5.2.0", | |
6898 | - "arg": "^5.0.2", | |
6899 | - "chokidar": "^3.5.3", | |
6900 | - "didyoumean": "^1.2.2", | |
6901 | - "dlv": "^1.1.3", | |
6902 | - "fast-glob": "^3.3.0", | |
6903 | - "glob-parent": "^6.0.2", | |
6904 | - "is-glob": "^4.0.3", | |
6905 | - "jiti": "^1.21.0", | |
6906 | - "lilconfig": "^2.1.0", | |
6907 | - "micromatch": "^4.0.5", | |
6908 | - "normalize-path": "^3.0.0", | |
6909 | - "object-hash": "^3.0.0", | |
6910 | - "picocolors": "^1.0.0", | |
6911 | - "postcss": "^8.4.23", | |
6912 | - "postcss-import": "^15.1.0", | |
6913 | - "postcss-js": "^4.0.1", | |
6914 | - "postcss-load-config": "^4.0.1", | |
6915 | - "postcss-nested": "^6.0.1", | |
6916 | - "postcss-selector-parser": "^6.0.11", | |
6917 | - "resolve": "^1.22.2", | |
6918 | - "sucrase": "^3.32.0" | |
6919 | - }, | |
6920 | - "bin": { | |
6921 | - "tailwind": "lib/cli.js", | |
6922 | - "tailwindcss": "lib/cli.js" | |
6923 | - }, | |
6924 | - "engines": { | |
6925 | - "node": ">=14.0.0" | |
6926 | - } | |
6927 | - }, | |
6928 | - "node_modules/tailwindcss/node_modules/resolve": { | |
6929 | - "version": "1.22.8", | |
6930 | - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", | |
6931 | - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", | |
6932 | - "dev": true, | |
6933 | - "dependencies": { | |
6934 | - "is-core-module": "^2.13.0", | |
6935 | - "path-parse": "^1.0.7", | |
6936 | - "supports-preserve-symlinks-flag": "^1.0.0" | |
6937 | - }, | |
6938 | - "bin": { | |
6939 | - "resolve": "bin/resolve" | |
6940 | - }, | |
6941 | - "funding": { | |
6942 | - "url": "https://github.com/sponsors/ljharb" | |
6943 | - } | |
6944 | - }, | |
6945 | - "node_modules/tar-stream": { | |
6946 | - "version": "2.2.0", | |
6947 | - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", | |
6948 | - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", | |
6949 | - "dependencies": { | |
6950 | - "bl": "^4.0.3", | |
6951 | - "end-of-stream": "^1.4.1", | |
6952 | - "fs-constants": "^1.0.0", | |
6953 | - "inherits": "^2.0.3", | |
6954 | - "readable-stream": "^3.1.1" | |
6955 | - }, | |
6956 | - "engines": { | |
6957 | - "node": ">=6" | |
6958 | - } | |
6959 | - }, | |
6960 | - "node_modules/text-table": { | |
6961 | - "version": "0.2.0", | |
6962 | - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", | |
6963 | - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", | |
6964 | - "dev": true | |
6965 | - }, | |
6966 | - "node_modules/thenify": { | |
6967 | - "version": "3.3.1", | |
6968 | - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", | |
6969 | - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", | |
6970 | - "dev": true, | |
6971 | - "dependencies": { | |
6972 | - "any-promise": "^1.0.0" | |
6973 | - } | |
6974 | - }, | |
6975 | - "node_modules/thenify-all": { | |
6976 | - "version": "1.6.0", | |
6977 | - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", | |
6978 | - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", | |
6979 | - "dev": true, | |
6980 | - "dependencies": { | |
6981 | - "thenify": ">= 3.1.0 < 4" | |
6982 | - }, | |
6983 | - "engines": { | |
6984 | - "node": ">=0.8" | |
6985 | - } | |
6986 | - }, | |
6987 | - "node_modules/tmp": { | |
6988 | - "version": "0.2.3", | |
6989 | - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", | |
6990 | - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", | |
6991 | - "engines": { | |
6992 | - "node": ">=14.14" | |
6993 | - } | |
6994 | - }, | |
6995 | - "node_modules/to-fast-properties": { | |
6996 | - "version": "2.0.0", | |
6997 | - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", | |
6998 | - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", | |
6999 | - "engines": { | |
7000 | - "node": ">=4" | |
7001 | - } | |
7002 | - }, | |
7003 | - "node_modules/to-regex-range": { | |
7004 | - "version": "5.0.1", | |
7005 | - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", | |
7006 | - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", | |
7007 | - "dev": true, | |
7008 | - "dependencies": { | |
7009 | - "is-number": "^7.0.0" | |
7010 | - }, | |
7011 | - "engines": { | |
7012 | - "node": ">=8.0" | |
7013 | - } | |
7014 | - }, | |
7015 | - "node_modules/traverse": { | |
7016 | - "version": "0.3.9", | |
7017 | - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", | |
7018 | - "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", | |
7019 | - "engines": { | |
7020 | - "node": "*" | |
7021 | - } | |
7022 | - }, | |
7023 | - "node_modules/ts-interface-checker": { | |
7024 | - "version": "0.1.13", | |
7025 | - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", | |
7026 | - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", | |
7027 | - "dev": true | |
7028 | - }, | |
7029 | - "node_modules/tslib": { | |
7030 | - "version": "2.6.2", | |
7031 | - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", | |
7032 | - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" | |
7033 | - }, | |
7034 | - "node_modules/type-check": { | |
7035 | - "version": "0.4.0", | |
7036 | - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", | |
7037 | - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", | |
7038 | - "dev": true, | |
7039 | - "dependencies": { | |
7040 | - "prelude-ls": "^1.2.1" | |
7041 | - }, | |
7042 | - "engines": { | |
7043 | - "node": ">= 0.8.0" | |
7044 | - } | |
7045 | - }, | |
7046 | - "node_modules/type-fest": { | |
7047 | - "version": "0.20.2", | |
7048 | - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", | |
7049 | - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", | |
7050 | - "dev": true, | |
7051 | - "engines": { | |
7052 | - "node": ">=10" | |
7053 | - }, | |
7054 | - "funding": { | |
7055 | - "url": "https://github.com/sponsors/sindresorhus" | |
7056 | - } | |
7057 | - }, | |
7058 | - "node_modules/typed-array-buffer": { | |
7059 | - "version": "1.0.2", | |
7060 | - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz", | |
7061 | - "integrity": "sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ==", | |
7062 | - "dev": true, | |
7063 | - "dependencies": { | |
7064 | - "call-bind": "^1.0.7", | |
7065 | - "es-errors": "^1.3.0", | |
7066 | - "is-typed-array": "^1.1.13" | |
7067 | - }, | |
7068 | - "engines": { | |
7069 | - "node": ">= 0.4" | |
7070 | - } | |
7071 | - }, | |
7072 | - "node_modules/typed-array-byte-length": { | |
7073 | - "version": "1.0.1", | |
7074 | - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz", | |
7075 | - "integrity": "sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw==", | |
7076 | - "dev": true, | |
7077 | - "dependencies": { | |
7078 | - "call-bind": "^1.0.7", | |
7079 | - "for-each": "^0.3.3", | |
7080 | - "gopd": "^1.0.1", | |
7081 | - "has-proto": "^1.0.3", | |
7082 | - "is-typed-array": "^1.1.13" | |
7083 | - }, | |
7084 | - "engines": { | |
7085 | - "node": ">= 0.4" | |
7086 | - }, | |
7087 | - "funding": { | |
7088 | - "url": "https://github.com/sponsors/ljharb" | |
7089 | - } | |
7090 | - }, | |
7091 | - "node_modules/typed-array-byte-offset": { | |
7092 | - "version": "1.0.2", | |
7093 | - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz", | |
7094 | - "integrity": "sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA==", | |
7095 | - "dev": true, | |
7096 | - "dependencies": { | |
7097 | - "available-typed-arrays": "^1.0.7", | |
7098 | - "call-bind": "^1.0.7", | |
7099 | - "for-each": "^0.3.3", | |
7100 | - "gopd": "^1.0.1", | |
7101 | - "has-proto": "^1.0.3", | |
7102 | - "is-typed-array": "^1.1.13" | |
7103 | - }, | |
7104 | - "engines": { | |
7105 | - "node": ">= 0.4" | |
7106 | - }, | |
7107 | - "funding": { | |
7108 | - "url": "https://github.com/sponsors/ljharb" | |
7109 | - } | |
7110 | - }, | |
7111 | - "node_modules/typed-array-length": { | |
7112 | - "version": "1.0.6", | |
7113 | - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz", | |
7114 | - "integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==", | |
7115 | - "dev": true, | |
7116 | - "dependencies": { | |
7117 | - "call-bind": "^1.0.7", | |
7118 | - "for-each": "^0.3.3", | |
7119 | - "gopd": "^1.0.1", | |
7120 | - "has-proto": "^1.0.3", | |
7121 | - "is-typed-array": "^1.1.13", | |
7122 | - "possible-typed-array-names": "^1.0.0" | |
7123 | - }, | |
7124 | - "engines": { | |
7125 | - "node": ">= 0.4" | |
7126 | - }, | |
7127 | - "funding": { | |
7128 | - "url": "https://github.com/sponsors/ljharb" | |
7129 | - } | |
7130 | - }, | |
7131 | - "node_modules/typescript": { | |
7132 | - "version": "5.4.5", | |
7133 | - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", | |
7134 | - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", | |
7135 | - "peer": true, | |
7136 | - "bin": { | |
7137 | - "tsc": "bin/tsc", | |
7138 | - "tsserver": "bin/tsserver" | |
7139 | - }, | |
7140 | - "engines": { | |
7141 | - "node": ">=14.17" | |
7142 | - } | |
7143 | - }, | |
7144 | - "node_modules/unbox-primitive": { | |
7145 | - "version": "1.0.2", | |
7146 | - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", | |
7147 | - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", | |
7148 | - "dev": true, | |
7149 | - "dependencies": { | |
7150 | - "call-bind": "^1.0.2", | |
7151 | - "has-bigints": "^1.0.2", | |
7152 | - "has-symbols": "^1.0.3", | |
7153 | - "which-boxed-primitive": "^1.0.2" | |
7154 | - }, | |
7155 | - "funding": { | |
7156 | - "url": "https://github.com/sponsors/ljharb" | |
7157 | - } | |
7158 | - }, | |
7159 | - "node_modules/undici-types": { | |
7160 | - "version": "5.26.5", | |
7161 | - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", | |
7162 | - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", | |
7163 | - "optional": true, | |
7164 | - "peer": true | |
7165 | - }, | |
7166 | - "node_modules/universalify": { | |
7167 | - "version": "0.1.2", | |
7168 | - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", | |
7169 | - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", | |
7170 | - "peer": true, | |
7171 | - "engines": { | |
7172 | - "node": ">= 4.0.0" | |
7173 | - } | |
7174 | - }, | |
7175 | - "node_modules/unzipper": { | |
7176 | - "version": "0.10.14", | |
7177 | - "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.14.tgz", | |
7178 | - "integrity": "sha512-ti4wZj+0bQTiX2KmKWuwj7lhV+2n//uXEotUmGuQqrbVZSEGFMbI68+c6JCQ8aAmUWYvtHEz2A8K6wXvueR/6g==", | |
7179 | - "dependencies": { | |
7180 | - "big-integer": "^1.6.17", | |
7181 | - "binary": "~0.3.0", | |
7182 | - "bluebird": "~3.4.1", | |
7183 | - "buffer-indexof-polyfill": "~1.0.0", | |
7184 | - "duplexer2": "~0.1.4", | |
7185 | - "fstream": "^1.0.12", | |
7186 | - "graceful-fs": "^4.2.2", | |
7187 | - "listenercount": "~1.0.1", | |
7188 | - "readable-stream": "~2.3.6", | |
7189 | - "setimmediate": "~1.0.4" | |
7190 | - } | |
7191 | - }, | |
7192 | - "node_modules/unzipper/node_modules/isarray": { | |
7193 | - "version": "1.0.0", | |
7194 | - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", | |
7195 | - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" | |
7196 | - }, | |
7197 | - "node_modules/unzipper/node_modules/readable-stream": { | |
7198 | - "version": "2.3.8", | |
7199 | - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", | |
7200 | - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", | |
7201 | - "dependencies": { | |
7202 | - "core-util-is": "~1.0.0", | |
7203 | - "inherits": "~2.0.3", | |
7204 | - "isarray": "~1.0.0", | |
7205 | - "process-nextick-args": "~2.0.0", | |
7206 | - "safe-buffer": "~5.1.1", | |
7207 | - "string_decoder": "~1.1.1", | |
7208 | - "util-deprecate": "~1.0.1" | |
7209 | - } | |
7210 | - }, | |
7211 | - "node_modules/unzipper/node_modules/safe-buffer": { | |
7212 | - "version": "5.1.2", | |
7213 | - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", | |
7214 | - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" | |
7215 | - }, | |
7216 | - "node_modules/unzipper/node_modules/string_decoder": { | |
7217 | - "version": "1.1.1", | |
7218 | - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", | |
7219 | - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", | |
7220 | - "dependencies": { | |
7221 | - "safe-buffer": "~5.1.0" | |
7222 | - } | |
7223 | - }, | |
7224 | - "node_modules/update-browserslist-db": { | |
7225 | - "version": "1.0.13", | |
7226 | - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", | |
7227 | - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", | |
7228 | - "dev": true, | |
7229 | - "funding": [ | |
7230 | - { | |
7231 | - "type": "opencollective", | |
7232 | - "url": "https://opencollective.com/browserslist" | |
7233 | - }, | |
7234 | - { | |
7235 | - "type": "tidelift", | |
7236 | - "url": "https://tidelift.com/funding/github/npm/browserslist" | |
7237 | - }, | |
7238 | - { | |
7239 | - "type": "github", | |
7240 | - "url": "https://github.com/sponsors/ai" | |
7241 | - } | |
7242 | - ], | |
7243 | - "dependencies": { | |
7244 | - "escalade": "^3.1.1", | |
7245 | - "picocolors": "^1.0.0" | |
7246 | - }, | |
7247 | - "bin": { | |
7248 | - "update-browserslist-db": "cli.js" | |
7249 | - }, | |
7250 | - "peerDependencies": { | |
7251 | - "browserslist": ">= 4.21.0" | |
7252 | - } | |
7253 | - }, | |
7254 | - "node_modules/uri-js": { | |
7255 | - "version": "4.4.1", | |
7256 | - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", | |
7257 | - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", | |
7258 | - "dependencies": { | |
7259 | - "punycode": "^2.1.0" | |
7260 | - } | |
7261 | - }, | |
7262 | - "node_modules/util-deprecate": { | |
7263 | - "version": "1.0.2", | |
7264 | - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", | |
7265 | - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" | |
7266 | - }, | |
7267 | - "node_modules/uuid": { | |
7268 | - "version": "8.3.2", | |
7269 | - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", | |
7270 | - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", | |
7271 | - "bin": { | |
7272 | - "uuid": "dist/bin/uuid" | |
7273 | - } | |
7274 | - }, | |
7275 | - "node_modules/validator": { | |
7276 | - "version": "13.11.0", | |
7277 | - "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", | |
7278 | - "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", | |
7279 | - "peer": true, | |
7280 | - "engines": { | |
7281 | - "node": ">= 0.10" | |
7282 | - } | |
7283 | - }, | |
7284 | - "node_modules/vite": { | |
7285 | - "version": "5.2.10", | |
7286 | - "resolved": "https://registry.npmjs.org/vite/-/vite-5.2.10.tgz", | |
7287 | - "integrity": "sha512-PAzgUZbP7msvQvqdSD+ErD5qGnSFiGOoWmV5yAKUEI0kdhjbH6nMWVyZQC/hSc4aXwc0oJ9aEdIiF9Oje0JFCw==", | |
7288 | - "dependencies": { | |
7289 | - "esbuild": "^0.20.1", | |
7290 | - "postcss": "^8.4.38", | |
7291 | - "rollup": "^4.13.0" | |
7292 | - }, | |
7293 | - "bin": { | |
7294 | - "vite": "bin/vite.js" | |
7295 | - }, | |
7296 | - "engines": { | |
7297 | - "node": "^18.0.0 || >=20.0.0" | |
7298 | - }, | |
7299 | - "funding": { | |
7300 | - "url": "https://github.com/vitejs/vite?sponsor=1" | |
7301 | - }, | |
7302 | - "optionalDependencies": { | |
7303 | - "fsevents": "~2.3.3" | |
7304 | - }, | |
7305 | - "peerDependencies": { | |
7306 | - "@types/node": "^18.0.0 || >=20.0.0", | |
7307 | - "less": "*", | |
7308 | - "lightningcss": "^1.21.0", | |
7309 | - "sass": "*", | |
7310 | - "stylus": "*", | |
7311 | - "sugarss": "*", | |
7312 | - "terser": "^5.4.0" | |
7313 | - }, | |
7314 | - "peerDependenciesMeta": { | |
7315 | - "@types/node": { | |
7316 | - "optional": true | |
7317 | - }, | |
7318 | - "less": { | |
7319 | - "optional": true | |
7320 | - }, | |
7321 | - "lightningcss": { | |
7322 | - "optional": true | |
7323 | - }, | |
7324 | - "sass": { | |
7325 | - "optional": true | |
7326 | - }, | |
7327 | - "stylus": { | |
7328 | - "optional": true | |
7329 | - }, | |
7330 | - "sugarss": { | |
7331 | - "optional": true | |
7332 | - }, | |
7333 | - "terser": { | |
7334 | - "optional": true | |
7335 | - } | |
7336 | - } | |
7337 | - }, | |
7338 | - "node_modules/vite-plugin-dts": { | |
7339 | - "version": "3.9.0", | |
7340 | - "resolved": "https://registry.npmjs.org/vite-plugin-dts/-/vite-plugin-dts-3.9.0.tgz", | |
7341 | - "integrity": "sha512-pwFIEYQ3LZvMafkEGvNnileb6af5JuyZsBfYQrTDYxdeGEy0OS4B4hCsLPo5YGnhK5k9EzyO6BXVO6y+Lt5T2A==", | |
7342 | - "peer": true, | |
7343 | - "dependencies": { | |
7344 | - "@microsoft/api-extractor": "7.43.0", | |
7345 | - "@rollup/pluginutils": "^5.1.0", | |
7346 | - "@vue/language-core": "^1.8.27", | |
7347 | - "debug": "^4.3.4", | |
7348 | - "kolorist": "^1.8.0", | |
7349 | - "magic-string": "^0.30.8", | |
7350 | - "vue-tsc": "^1.8.27" | |
7351 | - }, | |
7352 | - "engines": { | |
7353 | - "node": "^14.18.0 || >=16.0.0" | |
7354 | - }, | |
7355 | - "peerDependencies": { | |
7356 | - "typescript": "*", | |
7357 | - "vite": "*" | |
7358 | - }, | |
7359 | - "peerDependenciesMeta": { | |
7360 | - "vite": { | |
7361 | - "optional": true | |
7362 | - } | |
7363 | - } | |
7364 | - }, | |
7365 | - "node_modules/vue-template-compiler": { | |
7366 | - "version": "2.7.16", | |
7367 | - "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", | |
7368 | - "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", | |
7369 | - "peer": true, | |
7370 | - "dependencies": { | |
7371 | - "de-indent": "^1.0.2", | |
7372 | - "he": "^1.2.0" | |
7373 | - } | |
7374 | - }, | |
7375 | - "node_modules/vue-tsc": { | |
7376 | - "version": "1.8.27", | |
7377 | - "resolved": "https://registry.npmjs.org/vue-tsc/-/vue-tsc-1.8.27.tgz", | |
7378 | - "integrity": "sha512-WesKCAZCRAbmmhuGl3+VrdWItEvfoFIPXOvUJkjULi+x+6G/Dy69yO3TBRJDr9eUlmsNAwVmxsNZxvHKzbkKdg==", | |
7379 | - "peer": true, | |
7380 | - "dependencies": { | |
7381 | - "@volar/typescript": "~1.11.1", | |
7382 | - "@vue/language-core": "1.8.27", | |
7383 | - "semver": "^7.5.4" | |
7384 | - }, | |
7385 | - "bin": { | |
7386 | - "vue-tsc": "bin/vue-tsc.js" | |
7387 | - }, | |
7388 | - "peerDependencies": { | |
7389 | - "typescript": "*" | |
7390 | - } | |
7391 | - }, | |
7392 | - "node_modules/vue-tsc/node_modules/semver": { | |
7393 | - "version": "7.6.0", | |
7394 | - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", | |
7395 | - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", | |
7396 | - "peer": true, | |
7397 | - "dependencies": { | |
7398 | - "lru-cache": "^6.0.0" | |
7399 | - }, | |
7400 | - "bin": { | |
7401 | - "semver": "bin/semver.js" | |
7402 | - }, | |
7403 | - "engines": { | |
7404 | - "node": ">=10" | |
7405 | - } | |
7406 | - }, | |
7407 | - "node_modules/warning": { | |
7408 | - "version": "4.0.3", | |
7409 | - "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", | |
7410 | - "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", | |
7411 | - "dependencies": { | |
7412 | - "loose-envify": "^1.0.0" | |
7413 | - } | |
7414 | - }, | |
7415 | - "node_modules/which": { | |
7416 | - "version": "2.0.2", | |
7417 | - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", | |
7418 | - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", | |
7419 | - "dev": true, | |
7420 | - "dependencies": { | |
7421 | - "isexe": "^2.0.0" | |
7422 | - }, | |
7423 | - "bin": { | |
7424 | - "node-which": "bin/node-which" | |
7425 | - }, | |
7426 | - "engines": { | |
7427 | - "node": ">= 8" | |
7428 | - } | |
7429 | - }, | |
7430 | - "node_modules/which-boxed-primitive": { | |
7431 | - "version": "1.0.2", | |
7432 | - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", | |
7433 | - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", | |
7434 | - "dev": true, | |
7435 | - "dependencies": { | |
7436 | - "is-bigint": "^1.0.1", | |
7437 | - "is-boolean-object": "^1.1.0", | |
7438 | - "is-number-object": "^1.0.4", | |
7439 | - "is-string": "^1.0.5", | |
7440 | - "is-symbol": "^1.0.3" | |
7441 | - }, | |
7442 | - "funding": { | |
7443 | - "url": "https://github.com/sponsors/ljharb" | |
7444 | - } | |
7445 | - }, | |
7446 | - "node_modules/which-builtin-type": { | |
7447 | - "version": "1.1.3", | |
7448 | - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", | |
7449 | - "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", | |
7450 | - "dev": true, | |
7451 | - "dependencies": { | |
7452 | - "function.prototype.name": "^1.1.5", | |
7453 | - "has-tostringtag": "^1.0.0", | |
7454 | - "is-async-function": "^2.0.0", | |
7455 | - "is-date-object": "^1.0.5", | |
7456 | - "is-finalizationregistry": "^1.0.2", | |
7457 | - "is-generator-function": "^1.0.10", | |
7458 | - "is-regex": "^1.1.4", | |
7459 | - "is-weakref": "^1.0.2", | |
7460 | - "isarray": "^2.0.5", | |
7461 | - "which-boxed-primitive": "^1.0.2", | |
7462 | - "which-collection": "^1.0.1", | |
7463 | - "which-typed-array": "^1.1.9" | |
7464 | - }, | |
7465 | - "engines": { | |
7466 | - "node": ">= 0.4" | |
7467 | - }, | |
7468 | - "funding": { | |
7469 | - "url": "https://github.com/sponsors/ljharb" | |
7470 | - } | |
7471 | - }, | |
7472 | - "node_modules/which-collection": { | |
7473 | - "version": "1.0.2", | |
7474 | - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", | |
7475 | - "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", | |
7476 | - "dev": true, | |
7477 | - "dependencies": { | |
7478 | - "is-map": "^2.0.3", | |
7479 | - "is-set": "^2.0.3", | |
7480 | - "is-weakmap": "^2.0.2", | |
7481 | - "is-weakset": "^2.0.3" | |
7482 | - }, | |
7483 | - "engines": { | |
7484 | - "node": ">= 0.4" | |
7485 | - }, | |
7486 | - "funding": { | |
7487 | - "url": "https://github.com/sponsors/ljharb" | |
7488 | - } | |
7489 | - }, | |
7490 | - "node_modules/which-typed-array": { | |
7491 | - "version": "1.1.15", | |
7492 | - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", | |
7493 | - "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", | |
7494 | - "dev": true, | |
7495 | - "dependencies": { | |
7496 | - "available-typed-arrays": "^1.0.7", | |
7497 | - "call-bind": "^1.0.7", | |
7498 | - "for-each": "^0.3.3", | |
7499 | - "gopd": "^1.0.1", | |
7500 | - "has-tostringtag": "^1.0.2" | |
7501 | - }, | |
7502 | - "engines": { | |
7503 | - "node": ">= 0.4" | |
7504 | - }, | |
7505 | - "funding": { | |
7506 | - "url": "https://github.com/sponsors/ljharb" | |
7507 | - } | |
7508 | - }, | |
7509 | - "node_modules/word-wrap": { | |
7510 | - "version": "1.2.5", | |
7511 | - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", | |
7512 | - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", | |
7513 | - "dev": true, | |
7514 | - "engines": { | |
7515 | - "node": ">=0.10.0" | |
7516 | - } | |
7517 | - }, | |
7518 | - "node_modules/wrap-ansi": { | |
7519 | - "version": "8.1.0", | |
7520 | - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", | |
7521 | - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", | |
7522 | - "dev": true, | |
7523 | - "dependencies": { | |
7524 | - "ansi-styles": "^6.1.0", | |
7525 | - "string-width": "^5.0.1", | |
7526 | - "strip-ansi": "^7.0.1" | |
7527 | - }, | |
7528 | - "engines": { | |
7529 | - "node": ">=12" | |
7530 | - }, | |
7531 | - "funding": { | |
7532 | - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" | |
7533 | - } | |
7534 | - }, | |
7535 | - "node_modules/wrap-ansi-cjs": { | |
7536 | - "name": "wrap-ansi", | |
7537 | - "version": "7.0.0", | |
7538 | - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", | |
7539 | - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", | |
7540 | - "dev": true, | |
7541 | - "dependencies": { | |
7542 | - "ansi-styles": "^4.0.0", | |
7543 | - "string-width": "^4.1.0", | |
7544 | - "strip-ansi": "^6.0.0" | |
7545 | - }, | |
7546 | - "engines": { | |
7547 | - "node": ">=10" | |
7548 | - }, | |
7549 | - "funding": { | |
7550 | - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" | |
7551 | - } | |
7552 | - }, | |
7553 | - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { | |
7554 | - "version": "8.0.0", | |
7555 | - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", | |
7556 | - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", | |
7557 | - "dev": true | |
7558 | - }, | |
7559 | - "node_modules/wrap-ansi-cjs/node_modules/string-width": { | |
7560 | - "version": "4.2.3", | |
7561 | - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", | |
7562 | - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", | |
7563 | - "dev": true, | |
7564 | - "dependencies": { | |
7565 | - "emoji-regex": "^8.0.0", | |
7566 | - "is-fullwidth-code-point": "^3.0.0", | |
7567 | - "strip-ansi": "^6.0.1" | |
7568 | - }, | |
7569 | - "engines": { | |
7570 | - "node": ">=8" | |
7571 | - } | |
7572 | - }, | |
7573 | - "node_modules/wrap-ansi/node_modules/ansi-regex": { | |
7574 | - "version": "6.0.1", | |
7575 | - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", | |
7576 | - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", | |
7577 | - "dev": true, | |
7578 | - "engines": { | |
7579 | - "node": ">=12" | |
7580 | - }, | |
7581 | - "funding": { | |
7582 | - "url": "https://github.com/chalk/ansi-regex?sponsor=1" | |
7583 | - } | |
7584 | - }, | |
7585 | - "node_modules/wrap-ansi/node_modules/ansi-styles": { | |
7586 | - "version": "6.2.1", | |
7587 | - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", | |
7588 | - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", | |
7589 | - "dev": true, | |
7590 | - "engines": { | |
7591 | - "node": ">=12" | |
7592 | - }, | |
7593 | - "funding": { | |
7594 | - "url": "https://github.com/chalk/ansi-styles?sponsor=1" | |
7595 | - } | |
7596 | - }, | |
7597 | - "node_modules/wrap-ansi/node_modules/strip-ansi": { | |
7598 | - "version": "7.1.0", | |
7599 | - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", | |
7600 | - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", | |
7601 | - "dev": true, | |
7602 | - "dependencies": { | |
7603 | - "ansi-regex": "^6.0.1" | |
7604 | - }, | |
7605 | - "engines": { | |
7606 | - "node": ">=12" | |
7607 | - }, | |
7608 | - "funding": { | |
7609 | - "url": "https://github.com/chalk/strip-ansi?sponsor=1" | |
7610 | - } | |
7611 | - }, | |
7612 | - "node_modules/wrappy": { | |
7613 | - "version": "1.0.2", | |
7614 | - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", | |
7615 | - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" | |
7616 | - }, | |
7617 | - "node_modules/xmlchars": { | |
7618 | - "version": "2.2.0", | |
7619 | - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", | |
7620 | - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==" | |
7621 | - }, | |
7622 | - "node_modules/yallist": { | |
7623 | - "version": "4.0.0", | |
7624 | - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", | |
7625 | - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", | |
7626 | - "peer": true | |
7627 | - }, | |
7628 | - "node_modules/yaml": { | |
7629 | - "version": "1.10.2", | |
7630 | - "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", | |
7631 | - "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", | |
7632 | - "engines": { | |
7633 | - "node": ">= 6" | |
7634 | - } | |
7635 | - }, | |
7636 | - "node_modules/yocto-queue": { | |
7637 | - "version": "0.1.0", | |
7638 | - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", | |
7639 | - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", | |
7640 | - "dev": true, | |
7641 | - "engines": { | |
7642 | - "node": ">=10" | |
7643 | - }, | |
7644 | - "funding": { | |
7645 | - "url": "https://github.com/sponsors/sindresorhus" | |
7646 | - } | |
7647 | - }, | |
7648 | - "node_modules/z-schema": { | |
7649 | - "version": "5.0.5", | |
7650 | - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", | |
7651 | - "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", | |
7652 | - "peer": true, | |
7653 | - "dependencies": { | |
7654 | - "lodash.get": "^4.4.2", | |
7655 | - "lodash.isequal": "^4.5.0", | |
7656 | - "validator": "^13.7.0" | |
7657 | - }, | |
7658 | - "bin": { | |
7659 | - "z-schema": "bin/z-schema" | |
7660 | - }, | |
7661 | - "engines": { | |
7662 | - "node": ">=8.0.0" | |
7663 | - }, | |
7664 | - "optionalDependencies": { | |
7665 | - "commander": "^9.4.1" | |
7666 | - } | |
7667 | - }, | |
7668 | - "node_modules/zip-stream": { | |
7669 | - "version": "4.1.1", | |
7670 | - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", | |
7671 | - "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", | |
7672 | - "dependencies": { | |
7673 | - "archiver-utils": "^3.0.4", | |
7674 | - "compress-commons": "^4.1.2", | |
7675 | - "readable-stream": "^3.6.0" | |
7676 | - }, | |
7677 | - "engines": { | |
7678 | - "node": ">= 10" | |
7679 | - } | |
7680 | - }, | |
7681 | - "node_modules/zip-stream/node_modules/archiver-utils": { | |
7682 | - "version": "3.0.4", | |
7683 | - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", | |
7684 | - "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", | |
7685 | - "dependencies": { | |
7686 | - "glob": "^7.2.3", | |
7687 | - "graceful-fs": "^4.2.0", | |
7688 | - "lazystream": "^1.0.0", | |
7689 | - "lodash.defaults": "^4.2.0", | |
7690 | - "lodash.difference": "^4.5.0", | |
7691 | - "lodash.flatten": "^4.4.0", | |
7692 | - "lodash.isplainobject": "^4.0.6", | |
7693 | - "lodash.union": "^4.6.0", | |
7694 | - "normalize-path": "^3.0.0", | |
7695 | - "readable-stream": "^3.6.0" | |
7696 | - }, | |
7697 | - "engines": { | |
7698 | - "node": ">= 10" | |
7699 | - } | |
7700 | - } | |
7701 | - } | |
7702 | -} | |
7703 | 0 | |