swadhinbiswas

[email protected]
2026-08-09 02:56:18 +0600 committed NaN years ago
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
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
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
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
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
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
-# liveattendence_with_anti_cheat
2
-[![wakatime](https://wakatime.com/badge/user/d9081c06-f3fd-422d-981c-cd2acc46a3c7/project/fa4105ec-a9a9-4e3e-9e2b-d0d13e165c81.svg)](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
+[![Python 3.12](https://img.shields.io/badge/python-3.12-3670A0?style=for-the-badge&logo=python&logoColor=white)](https://www.python.org/)
12
+[![FastAPI](https://img.shields.io/badge/FastAPI-0.141-009688.svg?style=for-the-badge&logo=fastapi&logoColor=white)](https://fastapi.tiangolo.com/)
13
+[![Tailwind CSS](https://img.shields.io/badge/Tailwind%20CSS-4-06B6D4.svg?style=for-the-badge&logo=tailwindcss&logoColor=white)](https://tailwindcss.com/)
14
+[![Runs](https://img.shields.io/badge/pytest-51%20passing-4EA94B.svg?style=for-the-badge&logo=pytest&logoColor=white)](tests/)
15
+[![Docker](https://img.shields.io/badge/Docker-Compose-2496ED.svg?style=for-the-badge&logo=docker&logoColor=white)](docker-compose.yml)
16
+[![License](https://img.shields.io/badge/license-MIT-4BA31F.svg?style=for-the-badge)](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
-![LoginPage](photos/loginpage.gif)
38
+| Login & onboarding | Live recognition |
39
+|---|---|
40
+| ![Login](photos/loginpage.gif) | ![Known & UNKNOWN tagging](photos/faceapp.gif) |
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
+![AegisVision system architecture](docs/diagrams/architecture.svg)
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
-![ImageBackend](photos/faceaoo.jpeg)
58
+## Face recognition pipeline
55
59
56
-## working process of Face recognition
60
+![Face recognition pipeline](docs/diagrams/recognition-pipeline.svg)
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
-![faceapp](photos/faceapp.gif)
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
-![Anti spoofing](photos/85498505-6ff1-4110-b67c-bac030223467.jpeg)
74
+## Data model
67
75
68
-### example:
69
-![example](photos/-video-to-gif-converter.gif)
76
+![AegisVision data model](docs/diagrams/data-model.svg)
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
+![AegisVision deployment](docs/diagrams/deployment.svg)
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
-# 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
-
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
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
-from app.functions.encodeGenarator import EndcodeGenarator
2
0
▼backend/requirements.txt
+33-0
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
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
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 &amp; 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 &amp; 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 &amp; 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 &amp; 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
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
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
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 &amp; 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 &gt; 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 &lt; 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
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
-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
-# 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
-# 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
-<!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
-{
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