diff --git a/.claude/worktrees/agent-a24062da b/.claude/worktrees/agent-a24062da new file mode 160000 index 0000000..4c32694 --- /dev/null +++ b/.claude/worktrees/agent-a24062da @@ -0,0 +1 @@ +Subproject commit 4c32694a52625252175a95aca70aa703f8e6d09e diff --git a/.claude/worktrees/agent-a32f4756 b/.claude/worktrees/agent-a32f4756 new file mode 160000 index 0000000..ab5a5c7 --- /dev/null +++ b/.claude/worktrees/agent-a32f4756 @@ -0,0 +1 @@ +Subproject commit ab5a5c7ae2dd869c14189edff3976051981d3739 diff --git a/.claude/worktrees/agent-a36e902e b/.claude/worktrees/agent-a36e902e new file mode 160000 index 0000000..e8ed013 --- /dev/null +++ b/.claude/worktrees/agent-a36e902e @@ -0,0 +1 @@ +Subproject commit e8ed0139b31cea4237b72c354cd11d835a06e505 diff --git a/.claude/worktrees/agent-a866869e b/.claude/worktrees/agent-a866869e new file mode 160000 index 0000000..5179f5a --- /dev/null +++ b/.claude/worktrees/agent-a866869e @@ -0,0 +1 @@ +Subproject commit 5179f5ab7650c2a919c41e3d08310a6ba463ed70 diff --git a/.claude/worktrees/agent-a9763668 b/.claude/worktrees/agent-a9763668 new file mode 160000 index 0000000..4de4735 --- /dev/null +++ b/.claude/worktrees/agent-a9763668 @@ -0,0 +1 @@ +Subproject commit 4de47352cdf7bb5dbd8fce43d25ce8823f1b7f88 diff --git a/.claude/worktrees/agent-adaf01c0 b/.claude/worktrees/agent-adaf01c0 new file mode 160000 index 0000000..4161358 --- /dev/null +++ b/.claude/worktrees/agent-adaf01c0 @@ -0,0 +1 @@ +Subproject commit 41613586c503b24033e25a8a5f1943ee3527b184 diff --git a/.env.example b/.env.example deleted file mode 100644 index cba0585..0000000 --- a/.env.example +++ /dev/null @@ -1,3 +0,0 @@ -DB_PASSWORD=changeme -JWT_SECRET=generate-a-64-char-hex-string -ENCRYPTION_KEY=generate-a-64-char-hex-string diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 93ced44..0000000 --- a/Dockerfile +++ /dev/null @@ -1,30 +0,0 @@ -# Stage 1: Frontend build -FROM node:20-alpine AS frontend -WORKDIR /app/frontend -COPY frontend/package*.json ./ -RUN npm install -COPY frontend/ ./ -RUN npx nuxi generate - -# Stage 2: Backend build -FROM node:20-alpine AS backend -WORKDIR /app/backend -COPY backend/package*.json ./ -RUN npm install -COPY backend/ ./ -RUN npx prisma generate -RUN npm run build - -# Stage 3: Production -FROM node:20-alpine -WORKDIR /app -COPY --from=backend /app/backend/dist ./dist -COPY --from=backend /app/backend/node_modules ./node_modules -COPY --from=backend /app/backend/package.json ./ -COPY --from=backend /app/backend/prisma ./prisma -COPY --from=backend /app/backend/seed.js ./seed.js -COPY --from=frontend /app/frontend/.output/public ./public -RUN addgroup -S wraith && adduser -S wraith -G wraith && chown -R wraith:wraith /app -USER wraith -EXPOSE 3000 -CMD ["sh", "-c", "ls -la prisma/migrations/ && ls -la prisma/migrations/*/ && npx prisma migrate deploy --schema prisma/schema.prisma && node seed.js; node dist/src/main.js"] diff --git a/README.md b/README.md deleted file mode 100644 index 5020a56..0000000 --- a/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Wraith - -Self-hosted MobaXterm replacement — SSH + SFTP + RDP in a browser. - -## Stack - -- **Backend:** NestJS 10, Prisma 6, PostgreSQL 16, ssh2, guacd -- **Frontend:** Nuxt 3 (SPA), PrimeVue 4, Tailwind CSS, xterm.js 5 - -## Quick Start - -```bash -cp .env.example .env -# Edit .env with real secrets - -docker compose up -d -``` - -Default credentials: `admin@wraith.local` / `wraith` - -## Development - -```bash -# Backend -cd backend && npm install && npm run dev - -# Frontend -cd frontend && npm install && npm run dev -``` diff --git a/Remote-Spec.md b/Remote-Spec.md deleted file mode 100644 index f5d8f3e..0000000 --- a/Remote-Spec.md +++ /dev/null @@ -1,284 +0,0 @@ -# Planned Remote — Web-Based Terminal & Remote Desktop Client - -## Product Spec Sheet - -> **Concept**: A modern, self-hosted web application combining the best features of Termius (SSH/SFTP) and MobaXterm (SSH + RDP + SFTP browser) — accessible from any browser, no desktop client required. -> -> **Stack**: Nuxt 3 (Vue 3 SSR) + NestJS backend + PostgreSQL -> -> **Target Users**: MSP technicians, sysadmins, and IT teams who need unified remote access to SSH and RDP endpoints from any device - ---- - -## 1. Feature Comparison — What We're Building Against - -### Termius (Desktop/Mobile SSH Client) - -| Feature | Termius Free | Termius Pro ($14.99/mo) | -| ------------------------- | ------------ | ---------------------------- | -| SSH / Mosh / Telnet | ✅ | ✅ | -| SFTP file transfer | ✅ | ✅ | -| Port forwarding | ✅ | ✅ | -| Multi-tab sessions | ✅ | ✅ | -| Split panes | ❌ | ✅ | -| Encrypted cloud vault | ❌ | ✅ | -| Cross-device sync | ❌ | ✅ | -| Team sharing | ❌ | ✅ (Team plan $29.99/user/mo) | -| Saved snippets/macros | ❌ | ✅ | -| FIDO2 / hardware key auth | ✅ | ✅ | -| RDP | ❌ | ❌ | -| SFTP browser (sidebar) | ❌ | ❌ | - -**Key Termius strength**: Beautiful cross-platform UI, encrypted credential sync. -**Key Termius weakness**: No RDP. No SFTP sidebar browser. No web-based option. - ---- - -### MobaXterm (Windows Desktop Client) - -| Feature | MobaXterm Free | MobaXterm Pro ($69/license) | -| ------------------------------------------------ | ---------------- | --------------------------- | -| SSH / Mosh / Telnet / rlogin | ✅ | ✅ | -| RDP (Remote Desktop) | ✅ | ✅ | -| VNC | ✅ | ✅ | -| SFTP sidebar browser (auto-opens on SSH connect) | ✅ | ✅ | -| X11 server | ✅ | ✅ | -| Multi-tab sessions | ✅ | ✅ | -| Split panes | ✅ | ✅ | -| SSH tunnels (graphical manager) | ✅ | ✅ | -| Macros / saved commands | ❌ (max 4) | ✅ (unlimited) | -| Session limit | 12 max | Unlimited | -| Customizable / brandable | ❌ | ✅ | -| Portable (USB stick) | ✅ | ✅ | -| Web-based | ❌ | ❌ | -| Cross-platform | ❌ (Windows only) | ❌ (Windows only) | - -**Key MobaXterm strength**: All-in-one (SSH + RDP + VNC + SFTP + X11). The SFTP sidebar that auto-opens on SSH connect is killer UX. -**Key MobaXterm weakness**: Windows only. Not web-based. Dated UI. - ---- - -## 2. Vigilance Remote — Our Feature Set - -### Core Principle - -**Everything MobaXterm does for SSH + RDP + SFTP, but in a modern web browser with Termius-level UI polish.** - -### 2.1 SSH Terminal - -| Feature | Implementation | -| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| SSH connections | **xterm.js** (MIT) — the industry standard web terminal. Used by VS Code, Tabby, Theia, and hundreds of production applications. GPU-accelerated rendering, full Unicode/CJK/emoji support. | -| Backend proxy | **NestJS WebSocket gateway** + **ssh2** (npm) — Node.js SSH client library. Browser connects via WebSocket to NestJS, which proxies to the SSH target. No direct SSH from browser. | -| Authentication | Password, SSH key (stored encrypted), SSH agent forwarding, FIDO2/hardware key | -| Multi-tab sessions | Tab bar with session labels, color-coded by host group | -| Split panes | Horizontal and vertical splits within a single tab (xterm.js instances in a flex grid) | -| Session recording | Record terminal sessions as asciinema-compatible casts. Replay in browser. Audit trail for MSP compliance. | -| Saved snippets | Quick-execute saved commands/scripts. Click to paste into active terminal. | -| Terminal theming | Dark/light modes, custom color schemes, font selection, font size | -| Search in terminal | Ctrl+F search through terminal scrollback buffer (xterm.js `SearchAddon`) | -| Copy/paste | Ctrl+Shift+C / Ctrl+Shift+V, or right-click context menu | - -### 2.2 SFTP File Browser (MobaXterm's Killer Feature) - -| Feature | Implementation | -| ------------------------ | ------------------------------------------------------------------------------------------------------------------------- | -| Auto-open on SSH connect | When an SSH session connects, the SFTP sidebar automatically opens showing the remote filesystem. Exactly like MobaXterm. | -| Sidebar layout | Left sidebar panel (resizable) showing remote filesystem as a tree. Main panel is the terminal. | -| File operations | Browse, upload (drag-and-drop from desktop), download, rename, delete, chmod, create directory | -| Dual-pane mode | Optional second SFTP panel for server-to-server file operations (drag between panels) | -| File editing | Click a text file to open in an embedded code editor (Monaco Editor — same as VS Code). Save pushes back via SFTP. | -| Transfer queue | Background upload/download queue with progress bars, pause/resume, retry | -| Backend | **ssh2-sftp-client** (npm) or raw **ssh2** SFTP subsystem. All file operations proxied through NestJS. | - -### 2.3 RDP (Remote Desktop) - -| Feature | Implementation | -| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| RDP connections | **Apache Guacamole** (`guacd` daemon + `guacamole-common-js` client library). Industry-standard, Apache-licensed, battle-tested web RDP. | -| Architecture | Browser → WebSocket → NestJS → Guacamole protocol → `guacd` daemon → RDP to target. The NestJS backend acts as the tunnel between the JavaScript client and guacd. | -| Display | HTML5 Canvas rendering via `guacamole-common-js`. Keyboard, mouse, and touch input forwarded. | -| Multi-monitor | Support for multiple virtual displays | -| Clipboard sync | Bidirectional clipboard between browser and remote desktop | -| File transfer | Upload/download via Guacamole's built-in file transfer (drive redirection) | -| Audio | Remote audio playback in browser | -| Resolution | Auto-detect browser window size, or set fixed resolution | -| RDP settings | Color depth, security mode (NLA/TLS/RDP), console session, admin mode, load balancing info | -| Session recording | Guacamole native session recording (video-like playback of RDP sessions) | - -### 2.4 Connection Manager (Termius-style) - -| Feature | Details | -| -------------------- | ----------------------------------------------------------------------------------------------------- | -| Host database | Store hosts with: name, hostname/IP, port, protocol (SSH/RDP), credentials, group, tags, notes, color | -| Groups/folders | Organize hosts into hierarchical groups (e.g., "RSM > Servers", "Filters Fast > Switches") | -| Quick connect | Top bar with hostname input — type and connect without saving | -| Search | Full-text search across all hosts, tags, and notes | -| Credential vault | AES-256-GCM encrypted storage for passwords and SSH keys. Master password or Entra ID auth. | -| SSH key management | Generate, import, export SSH keys. Associate keys with hosts. | -| Jump hosts / bastion | Configure SSH proxy/jump hosts for reaching targets behind firewalls | -| Port forwarding | Graphical SSH tunnel manager — local, remote, and dynamic forwarding | -| Tags & labels | Color-coded tags for categorization (production, staging, dev, client-name) | - -### 2.5 Team & MSP Features - -| Feature | Details | -| -------------------- | ----------------------------------------------------------------------------------- | -| Multi-user | User accounts with RBAC. Admin, Technician, Read-Only roles. | -| Entra ID SSO | One-click Microsoft Entra ID integration (same pattern as Vigilance HQ and RSM ERP) | -| Shared connections | Admins define connection templates. Technicians connect without seeing credentials. | -| Audit logging | Every connection, command, file transfer logged with user, timestamp, duration. | -| Session sharing | Share a live terminal session with a colleague (read-only or collaborative) | -| Client-scoped access | MSP multi-tenancy — technicians see only the hosts for clients they're assigned to | - ---- - -## 3. Technology Stack - -### Frontend - -| Component | Technology | License | -| ------------------ | ----------------------------------------------------------------------------------------- | ---------- | -| Framework | Nuxt 3 (Vue 3 SSR) | MIT | -| Terminal emulator | xterm.js 5.x | MIT | -| Terminal addons | `@xterm/addon-fit`, `@xterm/addon-search`, `@xterm/addon-web-links`, `@xterm/addon-webgl` | MIT | -| Code editor (SFTP) | Monaco Editor | MIT | -| RDP client | guacamole-common-js | Apache 2.0 | -| UI library | PrimeVue 4 or Naive UI | MIT | -| State management | Pinia | MIT | -| CSS | Tailwind CSS | MIT | -| File upload | Drag-and-drop with progress (native File API) | — | - -### Backend - -| Component | Technology | License | -| --------------------- | ----------------------------------------------------- | ------------------ | -| Framework | NestJS 10 | MIT | -| SSH proxy | ssh2 (npm) | MIT | -| SFTP operations | ssh2 SFTP subsystem (built into ssh2) | MIT | -| RDP proxy | guacd (Apache Guacamole daemon) | Apache 2.0 | -| Guacamole tunnel | Custom NestJS WebSocket gateway → guacd TCP | Apache 2.0 | -| Database | PostgreSQL 16 (hosts, users, credentials, audit logs) | PostgreSQL License | -| Credential encryption | AES-256-GCM (same pattern as Vigilance HQ) | — | -| WebSocket | NestJS `@WebSocketGateway` (socket.io or ws) | MIT | -| Auth | JWT + Microsoft Entra ID (one-click setup) | — | -| Session recording | asciinema format for SSH, Guacamole native for RDP | MIT / Apache 2.0 | - -### Infrastructure - -| Component | Technology | -| ------------- | -------------------------------------------------------------------------- | -| Deployment | Docker Compose | -| Services | `app` (Nuxt SSR + NestJS), `guacd` (Guacamole daemon), `postgres`, `redis` | -| Reverse proxy | Nginx (WebSocket upgrade support required) | -| `guacd` | Docker image `guacamole/guacd` — handles RDP/VNC protocol translation | - ---- - -## 4. Architecture - -``` -┌─────────────────────────────────────────────────────────────┐ -│ Browser (Any device, any OS) │ -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ -│ │ xterm.js │ │ SFTP Browser │ │ guac-client │ │ -│ │ (SSH term) │ │ (file tree) │ │ (RDP canvas) │ │ -│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ -│ │ WebSocket │ REST/WS │ WebSocket │ -└─────────┼──────────────────┼─────────────────┼──────────────┘ - │ │ │ -┌─────────┼──────────────────┼─────────────────┼──────────────┐ -│ NestJS Backend (Docker) │ │ │ -│ ┌──────▼───────┐ ┌──────▼───────┐ ┌──────▼───────┐ │ -│ │ SSH Gateway │ │ SFTP Service │ │ Guac Tunnel │ │ -│ │ (ssh2 lib) │ │ (ssh2 sftp) │ │ (TCP→guacd) │ │ -│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │ -│ │ SSH │ SFTP │ Guac Protocol │ -└─────────┼──────────────────┼─────────────────┼──────────────┘ - │ │ │ - ▼ ▼ ▼ - ┌───────────────┐ ┌───────────────┐ ┌─────────────┐ - │ SSH Server │ │ SSH Server │ │ guacd │ - │ (Linux/Unix) │ │ (same host) │ │ (Docker) │ - └───────────────┘ └───────────────┘ └──────┬──────┘ - │ RDP - ▼ - ┌───────────────┐ - │ RDP Server │ - │ (Windows) │ - └───────────────┘ -``` - ---- - -## 5. Key Open Source Components - -| Component | GitHub | Stars | License | Purpose | -| ----------------------- | ----------------------- | ----- | ---------- | ------------------------------------------------------------------------------------------ | -| **xterm.js** | xtermjs/xterm.js | 18K+ | MIT | Web terminal emulator — the industry standard. Used by VS Code. | -| **ssh2** | mscdex/ssh2 | 5.5K+ | MIT | Pure JavaScript SSH2 client/server. Powers the SSH proxy layer. | -| **guacamole-common-js** | apache/guacamole-client | 3.2K+ | Apache 2.0 | JavaScript RDP/VNC client. Renders remote desktop in HTML5 Canvas. | -| **guacd** | apache/guacamole-server | 3.2K+ | Apache 2.0 | Native daemon that translates RDP/VNC protocols to Guacamole protocol. | -| **Monaco Editor** | microsoft/monaco-editor | 42K+ | MIT | VS Code's editor component. For in-browser file editing via SFTP. | -| **Tabby** (reference) | Eugeny/tabby | 62K+ | MIT | Formerly Terminus — reference for SSH/SFTP web client architecture. Includes web app mode. | - -All components are **MIT or Apache 2.0 licensed** — zero GPL contamination, fully commercial-viable. - ---- - -## 6. Competitive Positioning - -| Feature | Termius Pro | MobaXterm Pro | Apache Guacamole | **Vigilance Remote** | -| ---------------------- | --------------- | ------------------ | ---------------- | -------------------------- | -| SSH Terminal | ✅ | ✅ | ✅ | ✅ | -| RDP | ❌ | ✅ | ✅ | ✅ | -| SFTP sidebar browser | ❌ | ✅ (killer feature) | ❌ | ✅ | -| Web-based (no install) | ❌ | ❌ | ✅ | ✅ | -| Cross-platform | ✅ (native apps) | ❌ (Windows only) | ✅ (web) | ✅ (web) | -| Modern UI | ✅ | ❌ (dated) | ❌ (basic) | ✅ | -| Team/MSP features | ✅ (Team plan) | ❌ | ✅ (basic) | ✅ | -| Entra ID SSO | ❌ | ❌ | ❌ | ✅ | -| Credential vault | ✅ | ✅ (master pw) | ✅ (DB) | ✅ (AES-256-GCM) | -| Session recording | ❌ | ❌ | ✅ | ✅ | -| Audit logging | ❌ | ❌ | ✅ (basic) | ✅ (comprehensive) | -| Multi-tenant (MSP) | ❌ | ❌ | ❌ | ✅ | -| Self-hosted | ❌ | N/A (desktop) | ✅ | ✅ | -| Embedded code editor | ❌ | ✅ (MobaTextEditor) | ❌ | ✅ (Monaco) | -| Price | $14.99/mo/user | $69 one-time | Free | Self-hosted (free) or SaaS | - -**Vigilance Remote is the only solution that combines**: web-based access + RDP + SSH + SFTP sidebar browser + modern UI + MSP multi-tenancy + Entra ID SSO + session recording + audit logging in a single self-hosted application. - ---- - -## 7. Database Schema (High Level) - -``` -users — id, email, name, role, entra_id, created_at -hosts — id, name, hostname, port, protocol (ssh/rdp), group_id, tags, notes, color -host_groups — id, name, parent_id (hierarchical) -credentials — id, host_id, type (password/key/entra), encrypted_value, key_passphrase -ssh_keys — id, user_id, name, public_key, encrypted_private_key, passphrase -sessions — id, user_id, host_id, protocol, started_at, ended_at, recording_path -audit_logs — id, user_id, action, target, details, ip_address, timestamp -port_forwards — id, host_id, type (local/remote/dynamic), local_port, remote_host, remote_port -snippets — id, user_id, name, command, tags -client_access — id, user_id, client_id (MSP multi-tenant scoping) -settings — id, key, value (system-wide config) -``` - ---- - -## 8. Build Estimate - -Given the existing open-source components (xterm.js, guacd, ssh2, Monaco), the heavy lifting is integration, not invention. The core SSH terminal + SFTP browser + RDP via Guacamole + connection manager could be built as a focused 3-4 week project using the Commander doctrine. - -| Phase | Duration | Deliverables | -| ------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------- | -| Foundation | Week 1 | Nuxt 3 scaffold, NestJS backend, Docker Compose (app + guacd + postgres + redis), auth (Entra ID + local), connection manager CRUD | -| SSH + SFTP | Week 2 | xterm.js terminal with WebSocket proxy, multi-tab, split panes, SFTP sidebar browser with drag-drop upload/download, Monaco file editor | -| RDP | Week 3 | guacd integration, guacamole-common-js client, RDP canvas rendering, clipboard sync, session settings | -| Polish & MSP | Week 4 | Session recording/playback, audit logging, team features, MSP multi-tenant scoping, theming, keyboard shortcuts, snippets | - ---- - -*This spec is ready for Claude Code. The open-source components are proven, the architecture is clean, and the integration patterns are well-documented. Point the XO at this spec and the result is a self-hosted MobaXterm replacement that runs in any browser.* diff --git a/backend/nest-cli.json b/backend/nest-cli.json deleted file mode 100644 index f9aa683..0000000 --- a/backend/nest-cli.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/nest-cli", - "collection": "@nestjs/schematics", - "sourceRoot": "src", - "compilerOptions": { - "deleteOutDir": true - } -} diff --git a/backend/package-lock.json b/backend/package-lock.json deleted file mode 100644 index 45a5788..0000000 --- a/backend/package-lock.json +++ /dev/null @@ -1,9614 +0,0 @@ -{ - "name": "wraith-backend", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "wraith-backend", - "version": "0.1.0", - "dependencies": { - "@nestjs/common": "^10.0.0", - "@nestjs/core": "^10.0.0", - "@nestjs/jwt": "^10.0.0", - "@nestjs/mapped-types": "^2.0.0", - "@nestjs/passport": "^10.0.0", - "@nestjs/platform-express": "^10.0.0", - "@nestjs/platform-ws": "^10.0.0", - "@nestjs/serve-static": "^4.0.0", - "@nestjs/websockets": "^10.0.0", - "@prisma/client": "^6.0.0", - "argon2": "^0.44.0", - "bcrypt": "^5.1.0", - "class-transformer": "^0.5.1", - "class-validator": "^0.14.0", - "passport": "^0.7.0", - "passport-jwt": "^4.0.1", - "qrcode": "^1.5.4", - "reflect-metadata": "^0.2.0", - "rxjs": "^7.8.0", - "speakeasy": "^2.0.0", - "ssh2": "^1.15.0", - "uuid": "^13.0.0", - "ws": "^8.16.0" - }, - "devDependencies": { - "@nestjs/cli": "^10.0.0", - "@nestjs/testing": "^10.0.0", - "@types/bcrypt": "^5.0.0", - "@types/jest": "^30.0.0", - "@types/node": "^20.0.0", - "@types/passport-jwt": "^4.0.0", - "@types/qrcode": "^1.5.6", - "@types/speakeasy": "^2.0.10", - "@types/ssh2": "^1.15.0", - "@types/uuid": "^10.0.0", - "@types/ws": "^8.5.0", - "jest": "^29.0.0", - "prisma": "^6.0.0", - "ts-jest": "^29.0.0", - "ts-node": "^10.9.0", - "typescript": "^5.3.0" - } - }, - "node_modules/@angular-devkit/core": { - "version": "17.3.11", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-17.3.11.tgz", - "integrity": "sha512-vTNDYNsLIWpYk2I969LMQFH29GTsLzxNk/0cLw5q56ARF0v5sIWfHYwGTS88jdDqIpuuettcSczbxeA7EuAmqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "8.12.0", - "ajv-formats": "2.1.1", - "jsonc-parser": "3.2.1", - "picomatch": "4.0.1", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^3.5.2" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@angular-devkit/core/node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@angular-devkit/schematics": { - "version": "17.3.11", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-17.3.11.tgz", - "integrity": "sha512-I5wviiIqiFwar9Pdk30Lujk8FczEEc18i22A5c6Z9lbmhPQdTroDnEQdsfXjy404wPe8H62s0I15o4pmMGfTYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "17.3.11", - "jsonc-parser": "3.2.1", - "magic-string": "0.30.8", - "ora": "5.4.1", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/schematics-cli": { - "version": "17.3.11", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics-cli/-/schematics-cli-17.3.11.tgz", - "integrity": "sha512-kcOMqp+PHAKkqRad7Zd7PbpqJ0LqLaNZdY1+k66lLWmkEBozgq8v4ASn/puPWf9Bo0HpCiK+EzLf0VHE8Z/y6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "17.3.11", - "@angular-devkit/schematics": "17.3.11", - "ansi-colors": "4.1.3", - "inquirer": "9.2.15", - "symbol-observable": "4.0.0", - "yargs-parser": "21.1.1" - }, - "bin": { - "schematics": "bin/schematics.js" - }, - "engines": { - "node": "^18.13.0 || >=20.9.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/schematics-cli/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@angular-devkit/schematics-cli/node_modules/cli-width": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 12" - } - }, - "node_modules/@angular-devkit/schematics-cli/node_modules/inquirer": { - "version": "9.2.15", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.2.15.tgz", - "integrity": "sha512-vI2w4zl/mDluHt9YEQ/543VTCwPKWiHzKtm9dM2V0NdFcqEexDAjUHzO1oA60HRNaVifGXXM1tRRNluLVHa0Kg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ljharb/through": "^2.3.12", - "ansi-escapes": "^4.3.2", - "chalk": "^5.3.0", - "cli-cursor": "^3.1.0", - "cli-width": "^4.1.0", - "external-editor": "^3.1.0", - "figures": "^3.2.0", - "lodash": "^4.17.21", - "mute-stream": "1.0.0", - "ora": "^5.4.1", - "run-async": "^3.0.0", - "rxjs": "^7.8.1", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^6.2.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@angular-devkit/schematics-cli/node_modules/mute-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", - "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@angular-devkit/schematics-cli/node_modules/run-async": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz", - "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/@angular-devkit/schematics/node_modules/rxjs": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", - "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", - "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@babel/core/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", - "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", - "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", - "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", - "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", - "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", - "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@babel/traverse/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@borewit/text-codec": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", - "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@cspotcode/source-map-support": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "0.3.9" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.0.3", - "@jridgewell/sourcemap-codec": "^1.4.10" - } - }, - "node_modules/@epic-web/invariant": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@epic-web/invariant/-/invariant-1.0.0.tgz", - "integrity": "sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==", - "license": "MIT" - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", - "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/diff-sequences": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.3.0.tgz", - "integrity": "sha512-cG51MVnLq1ecVUaQ3fr6YuuAOitHK1S4WUJHnsPFE/quQr33ADUx1FfrTCpMCRxvy0Yr9BThKpDjSlcTi91tMA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/get-type": { - "version": "30.1.0", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz", - "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/pattern": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", - "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-regex-util": "30.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/pattern/node_modules/jest-regex-util": { - "version": "30.0.1", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", - "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.11", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", - "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@ljharb/through": { - "version": "2.3.14", - "resolved": "https://registry.npmjs.org/@ljharb/through/-/through-2.3.14.tgz", - "integrity": "sha512-ajBvlKpWucBB17FuQYUShqpqy8GRgYEpJW0vWJbUu1CV9lWyrDCapy0lScU8T8Z6qn49sSwJB3+M+evYIdGg+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/@lukeed/csprng": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", - "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@mapbox/node-pre-gyp": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", - "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", - "license": "BSD-3-Clause", - "dependencies": { - "detect-libc": "^2.0.0", - "https-proxy-agent": "^5.0.0", - "make-dir": "^3.1.0", - "node-fetch": "^2.6.7", - "nopt": "^5.0.0", - "npmlog": "^5.0.1", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.11" - }, - "bin": { - "node-pre-gyp": "bin/node-pre-gyp" - } - }, - "node_modules/@nestjs/cli": { - "version": "10.4.9", - "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-10.4.9.tgz", - "integrity": "sha512-s8qYd97bggqeK7Op3iD49X2MpFtW4LVNLAwXFkfbRxKME6IYT7X0muNTJ2+QfI8hpbNx9isWkrLWIp+g5FOhiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "17.3.11", - "@angular-devkit/schematics": "17.3.11", - "@angular-devkit/schematics-cli": "17.3.11", - "@nestjs/schematics": "^10.0.1", - "chalk": "4.1.2", - "chokidar": "3.6.0", - "cli-table3": "0.6.5", - "commander": "4.1.1", - "fork-ts-checker-webpack-plugin": "9.0.2", - "glob": "10.4.5", - "inquirer": "8.2.6", - "node-emoji": "1.11.0", - "ora": "5.4.1", - "tree-kill": "1.2.2", - "tsconfig-paths": "4.2.0", - "tsconfig-paths-webpack-plugin": "4.2.0", - "typescript": "5.7.2", - "webpack": "5.97.1", - "webpack-node-externals": "3.0.0" - }, - "bin": { - "nest": "bin/nest.js" - }, - "engines": { - "node": ">= 16.14" - }, - "peerDependencies": { - "@swc/cli": "^0.1.62 || ^0.3.0 || ^0.4.0 || ^0.5.0", - "@swc/core": "^1.3.62" - }, - "peerDependenciesMeta": { - "@swc/cli": { - "optional": true - }, - "@swc/core": { - "optional": true - } - } - }, - "node_modules/@nestjs/cli/node_modules/typescript": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", - "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/@nestjs/common": { - "version": "10.4.22", - "resolved": "https://registry.npmjs.org/@nestjs/common/-/common-10.4.22.tgz", - "integrity": "sha512-fxJ4v85nDHaqT1PmfNCQ37b/jcv2OojtXTaK1P2uAXhzLf9qq6WNUOFvxBrV4fhQek1EQoT1o9oj5xAZmv3NRw==", - "license": "MIT", - "dependencies": { - "file-type": "20.4.1", - "iterare": "1.2.1", - "tslib": "2.8.1", - "uid": "2.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "class-transformer": "*", - "class-validator": "*", - "reflect-metadata": "^0.1.12 || ^0.2.0", - "rxjs": "^7.1.0" - }, - "peerDependenciesMeta": { - "class-transformer": { - "optional": true - }, - "class-validator": { - "optional": true - } - } - }, - "node_modules/@nestjs/core": { - "version": "10.4.22", - "resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.22.tgz", - "integrity": "sha512-6IX9+VwjiKtCjx+mXVPncpkQ5ZjKfmssOZPFexmT+6T9H9wZ3svpYACAo7+9e7Nr9DZSoRZw3pffkJP7Z0UjaA==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@nuxtjs/opencollective": "0.3.2", - "fast-safe-stringify": "2.1.1", - "iterare": "1.2.1", - "path-to-regexp": "3.3.0", - "tslib": "2.8.1", - "uid": "2.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "@nestjs/common": "^10.0.0", - "@nestjs/microservices": "^10.0.0", - "@nestjs/platform-express": "^10.0.0", - "@nestjs/websockets": "^10.0.0", - "reflect-metadata": "^0.1.12 || ^0.2.0", - "rxjs": "^7.1.0" - }, - "peerDependenciesMeta": { - "@nestjs/microservices": { - "optional": true - }, - "@nestjs/platform-express": { - "optional": true - }, - "@nestjs/websockets": { - "optional": true - } - } - }, - "node_modules/@nestjs/jwt": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-10.2.0.tgz", - "integrity": "sha512-x8cG90SURkEiLOehNaN2aRlotxT0KZESUliOPKKnjWiyJOcWurkF3w345WOX0P4MgFzUjGoZ1Sy0aZnxeihT0g==", - "license": "MIT", - "dependencies": { - "@types/jsonwebtoken": "9.0.5", - "jsonwebtoken": "9.0.2" - }, - "peerDependencies": { - "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0" - } - }, - "node_modules/@nestjs/mapped-types": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-2.1.0.tgz", - "integrity": "sha512-W+n+rM69XsFdwORF11UqJahn4J3xi4g/ZEOlJNL6KoW5ygWSmBB2p0S2BZ4FQeS/NDH72e6xIcu35SfJnE8bXw==", - "license": "MIT", - "peerDependencies": { - "@nestjs/common": "^10.0.0 || ^11.0.0", - "class-transformer": "^0.4.0 || ^0.5.0", - "class-validator": "^0.13.0 || ^0.14.0", - "reflect-metadata": "^0.1.12 || ^0.2.0" - }, - "peerDependenciesMeta": { - "class-transformer": { - "optional": true - }, - "class-validator": { - "optional": true - } - } - }, - "node_modules/@nestjs/passport": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-10.0.3.tgz", - "integrity": "sha512-znJ9Y4S8ZDVY+j4doWAJ8EuuVO7SkQN3yOBmzxbGaXbvcSwFDAdGJ+OMCg52NdzIO4tQoN4pYKx8W6M0ArfFRQ==", - "license": "MIT", - "peerDependencies": { - "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", - "passport": "^0.4.0 || ^0.5.0 || ^0.6.0 || ^0.7.0" - } - }, - "node_modules/@nestjs/platform-express": { - "version": "10.4.22", - "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-10.4.22.tgz", - "integrity": "sha512-ySSq7Py/DFozzZdNDH67m/vHoeVdphDniWBnl6q5QVoXldDdrZIHLXLRMPayTDh5A95nt7jjJzmD4qpTbNQ6tA==", - "license": "MIT", - "dependencies": { - "body-parser": "1.20.4", - "cors": "2.8.5", - "express": "4.22.1", - "multer": "2.0.2", - "tslib": "2.8.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "@nestjs/common": "^10.0.0", - "@nestjs/core": "^10.0.0" - } - }, - "node_modules/@nestjs/platform-ws": { - "version": "10.4.22", - "resolved": "https://registry.npmjs.org/@nestjs/platform-ws/-/platform-ws-10.4.22.tgz", - "integrity": "sha512-ZBL66p8axCyvQw6lP6R5uMAamVGfDb0/LtbdxDjMjbWb5/wi070P0MWrjzTudEA3ThsDMNOsfawZlsFUkSfCzg==", - "license": "MIT", - "dependencies": { - "tslib": "2.8.1", - "ws": "8.18.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "@nestjs/common": "^10.0.0", - "@nestjs/websockets": "^10.0.0", - "rxjs": "^7.1.0" - } - }, - "node_modules/@nestjs/platform-ws/node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/@nestjs/schematics": { - "version": "10.2.3", - "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.2.3.tgz", - "integrity": "sha512-4e8gxaCk7DhBxVUly2PjYL4xC2ifDFexCqq1/u4TtivLGXotVk0wHdYuPYe1tHTHuR1lsOkRbfOCpkdTnigLVg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "17.3.11", - "@angular-devkit/schematics": "17.3.11", - "comment-json": "4.2.5", - "jsonc-parser": "3.3.1", - "pluralize": "8.0.0" - }, - "peerDependencies": { - "typescript": ">=4.8.2" - } - }, - "node_modules/@nestjs/schematics/node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@nestjs/serve-static": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@nestjs/serve-static/-/serve-static-4.0.2.tgz", - "integrity": "sha512-cT0vdWN5ar7jDI2NKbhf4LcwJzU4vS5sVpMkVrHuyLcltbrz6JdGi1TfIMMatP2pNiq5Ie/uUdPSFDVaZX/URQ==", - "license": "MIT", - "dependencies": { - "path-to-regexp": "0.2.5" - }, - "peerDependencies": { - "@fastify/static": "^6.5.0 || ^7.0.0", - "@nestjs/common": "^9.0.0 || ^10.0.0", - "@nestjs/core": "^9.0.0 || ^10.0.0", - "express": "^4.18.1", - "fastify": "^4.7.0" - }, - "peerDependenciesMeta": { - "@fastify/static": { - "optional": true - }, - "express": { - "optional": true - }, - "fastify": { - "optional": true - } - } - }, - "node_modules/@nestjs/serve-static/node_modules/path-to-regexp": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.2.5.tgz", - "integrity": "sha512-l6qtdDPIkmAmzEO6egquYDfqQGPMRNGjYtrU13HAXb3YSRrt7HSb1sJY0pKp6o2bAa86tSB6iwaW2JbthPKr7Q==", - "license": "MIT" - }, - "node_modules/@nestjs/testing": { - "version": "10.4.22", - "resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-10.4.22.tgz", - "integrity": "sha512-HO9aPus3bAedAC+jKVAA8jTdaj4fs5M9fing4giHrcYV2txe9CvC1l1WAjwQ9RDhEHdugjY4y+FZA/U/YqPZrA==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "2.8.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nest" - }, - "peerDependencies": { - "@nestjs/common": "^10.0.0", - "@nestjs/core": "^10.0.0", - "@nestjs/microservices": "^10.0.0", - "@nestjs/platform-express": "^10.0.0" - }, - "peerDependenciesMeta": { - "@nestjs/microservices": { - "optional": true - }, - "@nestjs/platform-express": { - "optional": true - } - } - }, - "node_modules/@nestjs/websockets": { - "version": "10.4.22", - "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-10.4.22.tgz", - "integrity": "sha512-OLd4i0Faq7vgdtB5vVUrJ54hWEtcXy9poJ6n7kbbh/5ms+KffUl+wwGsbe7uSXLrkoyI8xXU6fZPkFArI+XiRg==", - "license": "MIT", - "dependencies": { - "iterare": "1.2.1", - "object-hash": "3.0.0", - "tslib": "2.8.1" - }, - "peerDependencies": { - "@nestjs/common": "^10.0.0", - "@nestjs/core": "^10.0.0", - "@nestjs/platform-socket.io": "^10.0.0", - "reflect-metadata": "^0.1.12 || ^0.2.0", - "rxjs": "^7.1.0" - }, - "peerDependenciesMeta": { - "@nestjs/platform-socket.io": { - "optional": true - } - } - }, - "node_modules/@nuxtjs/opencollective": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@nuxtjs/opencollective/-/opencollective-0.3.2.tgz", - "integrity": "sha512-um0xL3fO7Mf4fDxcqx9KryrB7zgRM5JSlvGN5AGkP6JLM5XEKyjeAiPbNxdXVXQ16isuAhYpvP88NgL2BGd6aA==", - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "consola": "^2.15.0", - "node-fetch": "^2.6.1" - }, - "bin": { - "opencollective": "bin/opencollective.js" - }, - "engines": { - "node": ">=8.0.0", - "npm": ">=5.0.0" - } - }, - "node_modules/@phc/format": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@phc/format/-/format-1.0.0.tgz", - "integrity": "sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@prisma/client": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.19.2.tgz", - "integrity": "sha512-gR2EMvfK/aTxsuooaDA32D8v+us/8AAet+C3J1cc04SW35FPdZYgLF+iN4NDLUgAaUGTKdAB0CYenu1TAgGdMg==", - "hasInstallScript": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "peerDependencies": { - "prisma": "*", - "typescript": ">=5.1.0" - }, - "peerDependenciesMeta": { - "prisma": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/@prisma/config": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.19.2.tgz", - "integrity": "sha512-kadBGDl+aUswv/zZMk9Mx0C8UZs1kjao8H9/JpI4Wh4SHZaM7zkTwiKn/iFLfRg+XtOAo/Z/c6pAYhijKl0nzQ==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "c12": "3.1.0", - "deepmerge-ts": "7.1.5", - "effect": "3.18.4", - "empathic": "2.0.0" - } - }, - "node_modules/@prisma/debug": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.19.2.tgz", - "integrity": "sha512-lFnEZsLdFLmEVCVNdskLDCL8Uup41GDfU0LUfquw+ercJC8ODTuL0WNKgOKmYxCJVvFwf0OuZBzW99DuWmoH2A==", - "devOptional": true, - "license": "Apache-2.0" - }, - "node_modules/@prisma/engines": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.19.2.tgz", - "integrity": "sha512-TTkJ8r+uk/uqczX40wb+ODG0E0icVsMgwCTyTHXehaEfb0uo80M9g1aW1tEJrxmFHeOZFXdI2sTA1j1AgcHi4A==", - "devOptional": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@prisma/debug": "6.19.2", - "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", - "@prisma/fetch-engine": "6.19.2", - "@prisma/get-platform": "6.19.2" - } - }, - "node_modules/@prisma/engines-version": { - "version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7.tgz", - "integrity": "sha512-03bgb1VD5gvuumNf+7fVGBzfpJPjmqV423l/WxsWk2cNQ42JD0/SsFBPhN6z8iAvdHs07/7ei77SKu7aZfq8bA==", - "devOptional": true, - "license": "Apache-2.0" - }, - "node_modules/@prisma/fetch-engine": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.19.2.tgz", - "integrity": "sha512-h4Ff4Pho+SR1S8XerMCC12X//oY2bG3Iug/fUnudfcXEUnIeRiBdXHFdGlGOgQ3HqKgosTEhkZMvGM9tWtYC+Q==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "@prisma/debug": "6.19.2", - "@prisma/engines-version": "7.1.1-3.c2990dca591cba766e3b7ef5d9e8a84796e47ab7", - "@prisma/get-platform": "6.19.2" - } - }, - "node_modules/@prisma/get-platform": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.19.2.tgz", - "integrity": "sha512-PGLr06JUSTqIvztJtAzIxOwtWKtJm5WwOG6xpsgD37Rc84FpfUBGLKz65YpJBGtkRQGXTYEFie7pYALocC3MtA==", - "devOptional": true, - "license": "Apache-2.0", - "dependencies": { - "@prisma/debug": "6.19.2" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.10", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", - "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@standard-schema/spec": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", - "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/@tokenizer/inflate": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.2.7.tgz", - "integrity": "sha512-MADQgmZT1eKjp06jpI2yozxaU9uVs4GzzgSL+uEq7bVcJ9V1ZXQkeGNql1fsSI0gMy1vhvNTNbUqrx+pZfJVmg==", - "license": "MIT", - "dependencies": { - "debug": "^4.4.0", - "fflate": "^0.8.2", - "token-types": "^6.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/@tokenizer/inflate/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@tokenizer/inflate/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/@tokenizer/token": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", - "license": "MIT" - }, - "node_modules/@tsconfig/node10": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", - "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node12": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", - "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node14": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", - "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", - "dev": true, - "license": "MIT" - }, - "node_modules/@tsconfig/node16": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", - "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/bcrypt": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", - "integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/express": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", - "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "^2" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", - "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-30.0.0.tgz", - "integrity": "sha512-XTYugzhuwqWjws0CVz8QpM36+T+Dz5mTEBKhNs/esGLnCIlGdRy+Dq78NRjd7ls7r8BC8ZRMOrKlkO1hU0JOwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^30.0.0", - "pretty-format": "^30.0.0" - } - }, - "node_modules/@types/jest/node_modules/@jest/expect-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.3.0.tgz", - "integrity": "sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/@jest/schemas": { - "version": "30.0.5", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.5.tgz", - "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/@jest/types": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.3.0.tgz", - "integrity": "sha512-JHm87k7bA33hpBngtU8h6UBub/fqqA9uXfw+21j5Hmk7ooPHlboRNxHq0JcMtC+n8VJGP1mcfnD3Mk+XKe1oSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.1", - "@jest/schemas": "30.0.5", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/@sinclair/typebox": { - "version": "0.34.48", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", - "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/jest/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@types/jest/node_modules/ci-info": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", - "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@types/jest/node_modules/expect": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.3.0.tgz", - "integrity": "sha512-1zQrciTiQfRdo7qJM1uG4navm8DayFa2TgCSRlzUyNkhcJ6XUZF3hjnpkyr3VhAqPH7i/9GkG7Tv5abz6fqz0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "30.3.0", - "@jest/get-type": "30.1.0", - "jest-matcher-utils": "30.3.0", - "jest-message-util": "30.3.0", - "jest-mock": "30.3.0", - "jest-util": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/jest-diff": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.3.0.tgz", - "integrity": "sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/diff-sequences": "30.3.0", - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "pretty-format": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/jest-matcher-utils": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.3.0.tgz", - "integrity": "sha512-HEtc9uFQgaUHkC7nLSlQL3Tph4Pjxt/yiPvkIrrDCt9jhoLIgxaubo1G+CFOnmHYMxHwwdaSN7mkIFs6ZK8OhA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.1.0", - "chalk": "^4.1.2", - "jest-diff": "30.3.0", - "pretty-format": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/jest-message-util": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.3.0.tgz", - "integrity": "sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.3.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.3", - "pretty-format": "30.3.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/jest-mock": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.3.0.tgz", - "integrity": "sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.3.0", - "@types/node": "*", - "jest-util": "30.3.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/jest-util": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.3.0.tgz", - "integrity": "sha512-/jZDa00a3Sz7rdyu55NLrQCIrbyIkbBxareejQI315f/i8HjYN+ZWsDLLpoQSiUIEIyZF/R8fDg3BmB8AtHttg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.3.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.3" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/jest/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/@types/jest/node_modules/pretty-format": { - "version": "30.3.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.3.0.tgz", - "integrity": "sha512-oG4T3wCbfeuvljnyAzhBvpN45E8iOTXCU/TD3zXW80HA3dQ4ahdqMkWGiPWZvjpQwlbyHrPTWUAqUzGzv4l1JQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.5", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/jsonwebtoken": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", - "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/node": { - "version": "20.19.37", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.37.tgz", - "integrity": "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/passport": { - "version": "1.0.17", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.17.tgz", - "integrity": "sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/passport-jwt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-4.0.1.tgz", - "integrity": "sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/jsonwebtoken": "*", - "@types/passport-strategy": "*" - } - }, - "node_modules/@types/passport-strategy": { - "version": "0.2.38", - "resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.38.tgz", - "integrity": "sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/express": "*", - "@types/passport": "*" - } - }, - "node_modules/@types/qrcode": { - "version": "1.5.6", - "resolved": "https://registry.npmjs.org/@types/qrcode/-/qrcode-1.5.6.tgz", - "integrity": "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/qs": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", - "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/send": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", - "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", - "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*" - } - }, - "node_modules/@types/speakeasy": { - "version": "2.0.10", - "resolved": "https://registry.npmjs.org/@types/speakeasy/-/speakeasy-2.0.10.tgz", - "integrity": "sha512-QVRlDW5r4yl7p7xkNIbAIC/JtyOcClDIIdKfuG7PWdDT1MmyhtXSANsildohy0K+Lmvf/9RUtLbNLMacvrVwxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/ssh2": { - "version": "1.15.5", - "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.5.tgz", - "integrity": "sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "^18.11.18" - } - }, - "node_modules/@types/ssh2/node_modules/@types/node": { - "version": "18.19.130", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", - "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/ssh2/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/uuid": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", - "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/validator": { - "version": "13.15.10", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", - "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", - "license": "MIT" - }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yargs": { - "version": "17.0.35", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", - "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", - "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", - "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", - "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", - "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", - "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", - "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", - "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", - "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", - "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", - "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", - "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", - "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", - "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", - "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", - "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "license": "ISC" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.5", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", - "integrity": "sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agent-base/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/agent-base/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", - "license": "MIT" - }, - "node_modules/aproba": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", - "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", - "license": "ISC" - }, - "node_modules/are-we-there-yet": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", - "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true, - "license": "MIT" - }, - "node_modules/argon2": { - "version": "0.44.0", - "resolved": "https://registry.npmjs.org/argon2/-/argon2-0.44.0.tgz", - "integrity": "sha512-zHPGN3S55sihSQo0dBbK0A5qpi2R31z7HZDZnry3ifOyj8bZZnpZND2gpmhnRGO1V/d555RwBqIK5W4Mrmv3ig==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@phc/format": "^1.0.0", - "cross-env": "^10.0.0", - "node-addon-api": "^8.5.0", - "node-gyp-build": "^4.8.4" - }, - "engines": { - "node": ">=16.17.0" - } - }, - "node_modules/argon2/node_modules/node-addon-api": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.6.0.tgz", - "integrity": "sha512-gBVjCaqDlRUk0EwoPNKzIr9KkS9041G/q31IBShPs1Xz6UTA+EXdZADbzqAJQrpDRq71CIMnOP5VMut3SL0z5Q==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/array-timsort": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", - "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", - "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/base32.js": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/base32.js/-/base32.js-0.0.1.tgz", - "integrity": "sha512-EGHIRiegFa62/SsA1J+Xs2tIzludPdzM064N9wjbiEgHnGnJ1V0WEpA4pEwCYT5nDvZk3ubf0shqaCS7k6xeUQ==", - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/baseline-browser-mapping": { - "version": "2.10.7", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.7.tgz", - "integrity": "sha512-1ghYO3HnxGec0TCGBXiDLVns4eCSx4zJpxnHrlqFQajmhfKMQBzUGDdkMK7fUW7PTHTeLf+j87aTuKuuwWzMGw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/bcrypt": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", - "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "@mapbox/node-pre-gyp": "^1.0.11", - "node-addon-api": "^5.0.0" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "license": "BSD-3-Clause", - "dependencies": { - "tweetnacl": "^0.14.3" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/body-parser": { - "version": "1.20.4", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", - "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "~1.2.0", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "on-finished": "~2.4.1", - "qs": "~6.14.0", - "raw-body": "~2.5.3", - "type-is": "~1.6.18", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause" - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/buildcheck": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.7.tgz", - "integrity": "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==", - "optional": true, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/c12": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", - "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "chokidar": "^4.0.3", - "confbox": "^0.2.2", - "defu": "^6.1.4", - "dotenv": "^16.6.1", - "exsolve": "^1.0.7", - "giget": "^2.0.0", - "jiti": "^2.4.2", - "ohash": "^2.0.11", - "pathe": "^2.0.3", - "perfect-debounce": "^1.0.0", - "pkg-types": "^2.2.0", - "rc9": "^2.1.2" - }, - "peerDependencies": { - "magicast": "^0.3.5" - }, - "peerDependenciesMeta": { - "magicast": { - "optional": true - } - } - }, - "node_modules/c12/node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/c12/node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001778", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001778.tgz", - "integrity": "sha512-PN7uxFL+ExFJO61aVmP1aIEG4i9whQd4eoSCebav62UwDyp5OHh06zN4jqKSMePVgxHifCw1QJxdRkA1Pisekg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true, - "license": "MIT" - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/citty": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", - "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "consola": "^3.2.3" - } - }, - "node_modules/citty/node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/class-transformer": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", - "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", - "license": "MIT" - }, - "node_modules/class-validator": { - "version": "0.14.4", - "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.4.tgz", - "integrity": "sha512-AwNusCCam51q703dW82x95tOqQp6oC9HNUl724KxJJOfnKscI8dOloXFgyez7LbTTKWuRBA37FScqVbJEoq8Yw==", - "license": "MIT", - "dependencies": { - "@types/validator": "^13.15.3", - "libphonenumber-js": "^1.11.1", - "validator": "^13.15.22" - } - }, - "node_modules/cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "restore-cursor": "^3.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-spinners": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/cli-table3": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", - "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "string-width": "^4.2.0" - }, - "engines": { - "node": "10.* || >= 12.*" - }, - "optionalDependencies": { - "@colors/colors": "1.5.0" - } - }, - "node_modules/cli-width": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", - "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">= 10" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cliui/node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", - "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", - "dev": true, - "license": "MIT" - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "license": "ISC", - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/comment-json": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.2.5.tgz", - "integrity": "sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-timsort": "^1.0.3", - "core-util-is": "^1.0.3", - "esprima": "^4.0.1", - "has-own-prop": "^2.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "license": "MIT" - }, - "node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "engines": [ - "node >= 6.0" - ], - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/confbox": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", - "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/consola": { - "version": "2.15.3", - "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", - "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==", - "license": "MIT" - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "license": "ISC" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", - "license": "MIT" - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/cosmiconfig": { - "version": "8.3.6", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz", - "integrity": "sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0", - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/cpu-features": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", - "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", - "hasInstallScript": true, - "optional": true, - "dependencies": { - "buildcheck": "~0.0.6", - "nan": "^2.19.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/create-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", - "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/cross-env": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-10.1.0.tgz", - "integrity": "sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==", - "license": "MIT", - "dependencies": { - "@epic-web/invariant": "^1.0.0", - "cross-spawn": "^7.0.6" - }, - "bin": { - "cross-env": "dist/bin/cross-env.js", - "cross-env-shell": "dist/bin/cross-env-shell.js" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/dedent": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.2.tgz", - "integrity": "sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/deepmerge-ts": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-7.1.5.tgz", - "integrity": "sha512-HOJkrhaYsweh+W+e74Yn7YStZOilkoPb6fycpwNLKzSPtruFs48nYis0zy5yJz1+ktUhHxoRDJ27RQAWLIJVJw==", - "devOptional": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/defaults": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", - "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/defu": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", - "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "license": "MIT" - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", - "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/diff": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", - "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dijkstrajs": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz", - "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==", - "license": "MIT" - }, - "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "devOptional": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/effect": { - "version": "3.18.4", - "resolved": "https://registry.npmjs.org/effect/-/effect-3.18.4.tgz", - "integrity": "sha512-b1LXQJLe9D11wfnOKAk3PKxuqYshQ0Heez+y5pnkd3jLj1yx9QhM72zZ9uUrOQyNvrs2GZZd/3maL0ZV18YuDA==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@standard-schema/spec": "^1.0.0", - "fast-check": "^3.23.1" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.313", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.313.tgz", - "integrity": "sha512-QBMrTWEf00GXZmJyx2lbYD45jpI3TUFnNIzJ5BBc8piGUDwMPa1GV6HJWTZVvY/eiN3fSopl7NRbgGp9sZ9LTA==", - "dev": true, - "license": "ISC" - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/empathic": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.0.0.tgz", - "integrity": "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.20.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.0.tgz", - "integrity": "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/error-ex": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", - "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/execa/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/express": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", - "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "~1.20.3", - "content-disposition": "~0.5.4", - "content-type": "~1.0.4", - "cookie": "~0.7.1", - "cookie-signature": "~1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "~1.3.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "~0.1.12", - "proxy-addr": "~2.0.7", - "qs": "~6.14.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "~0.19.0", - "serve-static": "~1.16.2", - "setprototypeof": "1.2.0", - "statuses": "~2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express/node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" - }, - "node_modules/exsolve": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", - "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, - "license": "MIT", - "dependencies": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/fast-check": { - "version": "3.23.2", - "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.23.2.tgz", - "integrity": "sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==", - "devOptional": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT", - "dependencies": { - "pure-rand": "^6.1.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "license": "MIT" - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/fflate": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", - "license": "MIT" - }, - "node_modules/figures": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", - "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^1.0.5" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/file-type": { - "version": "20.4.1", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-20.4.1.tgz", - "integrity": "sha512-hw9gNZXUfZ02Jo0uafWLaFVPter5/k2rfcrjFJJHX/77xtSDOfJuEFb6oKlFV86FLP1SuyHMW1PSk0U9M5tKkQ==", - "license": "MIT", - "dependencies": { - "@tokenizer/inflate": "^0.2.6", - "strtok3": "^10.2.0", - "token-types": "^6.0.0", - "uint8array-extras": "^1.4.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sindresorhus/file-type?sponsor=1" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", - "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "~2.4.1", - "parseurl": "~1.3.3", - "statuses": "~2.0.2", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/fork-ts-checker-webpack-plugin": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.0.2.tgz", - "integrity": "sha512-Uochze2R8peoN1XqlSi/rGUkDQpRogtLFocP9+PGu68zk1BDAKXfdeCdyVZpgTk8V8WFVQXdEz426VKjXLO1Gg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.16.7", - "chalk": "^4.1.2", - "chokidar": "^3.5.3", - "cosmiconfig": "^8.2.0", - "deepmerge": "^4.2.2", - "fs-extra": "^10.0.0", - "memfs": "^3.4.1", - "minimatch": "^3.0.4", - "node-abort-controller": "^3.0.1", - "schema-utils": "^3.1.1", - "semver": "^7.3.5", - "tapable": "^2.2.1" - }, - "engines": { - "node": ">=12.13.0", - "yarn": ">=1.0.0" - }, - "peerDependencies": { - "typescript": ">3.6.0", - "webpack": "^5.11.0" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-extra": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", - "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fs-minipass/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, - "node_modules/fs-monkey": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.1.0.tgz", - "integrity": "sha512-QMUezzXWII9EV5aTFXW1UBVUO77wYPpjqIF8/AviUCThNeSYZykpoTixUeaNNBwmCev0AMDWMAni+f8Hxb1IFw==", - "dev": true, - "license": "Unlicense" - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gauge": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", - "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.2", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.1", - "object-assign": "^4.1.1", - "signal-exit": "^3.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/gauge/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC" - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/giget": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", - "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "citty": "^0.1.6", - "consola": "^3.4.0", - "defu": "^6.1.4", - "node-fetch-native": "^1.6.6", - "nypm": "^0.6.0", - "pathe": "^2.0.3" - }, - "bin": { - "giget": "dist/cli.mjs" - } - }, - "node_modules/giget/node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, - "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "9.0.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", - "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.2" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/handlebars/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-own-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-own-prop/-/has-own-prop-2.0.0.tgz", - "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "license": "ISC" - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/http-errors": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", - "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/inquirer": { - "version": "8.2.6", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", - "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-escapes": "^4.2.1", - "chalk": "^4.1.1", - "cli-cursor": "^3.1.0", - "cli-width": "^3.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.21", - "mute-stream": "0.0.8", - "ora": "^5.4.1", - "run-async": "^2.4.0", - "rxjs": "^7.5.5", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0", - "through": "^2.3.6", - "wrap-ansi": "^6.0.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-interactive": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", - "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-unicode-supported": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", - "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-reports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", - "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/iterare": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/iterare/-/iterare-1.2.1.tgz", - "integrity": "sha512-RKYVTCjAnRthyJes037NX/IiqeidgN1xc3j1RjFfECFp28A1GVwK9nA+i0rJPaHqSZwygLzRnFlzUuHFoWWy+Q==", - "license": "ISC", - "engines": { - "node": ">=6" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-config/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jiti": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", - "devOptional": true, - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonc-parser": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", - "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==", - "dev": true, - "license": "MIT" - }, - "node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "license": "MIT", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jsonwebtoken/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/jwa": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", - "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", - "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", - "license": "MIT", - "dependencies": { - "jwa": "^1.4.2", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/libphonenumber-js": { - "version": "1.12.39", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.39.tgz", - "integrity": "sha512-MW79m7HuOqBk8mwytiXYTMELJiBbV3Zl9Y39dCCn1yC8K+WGNSq1QGvzywbylp5vGShEztMScCWHX/XFOS0rXg==", - "license": "MIT" - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/loader-runner": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", - "integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.11.5" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "license": "MIT" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "license": "MIT" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "license": "MIT" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", - "license": "MIT" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "license": "MIT" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "license": "MIT" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "license": "MIT" - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/magic-string": { - "version": "0.30.8", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.8.tgz", - "integrity": "sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "license": "MIT", - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memfs": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", - "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", - "dev": true, - "license": "Unlicense", - "dependencies": { - "fs-monkey": "^1.0.4" - }, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/multer": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.2.tgz", - "integrity": "sha512-u7f2xaZ/UG8oLXHvtF/oWTRvT44p9ecwBBqTwgJVq0+4BW1g8OW01TyMEGWBHbyMOYVHXslaut7qEQ1meATXgw==", - "license": "MIT", - "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.6.0", - "concat-stream": "^2.0.0", - "mkdirp": "^0.5.6", - "object-assign": "^4.1.1", - "type-is": "^1.6.18", - "xtend": "^4.0.2" - }, - "engines": { - "node": ">= 10.16.0" - } - }, - "node_modules/mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true, - "license": "ISC" - }, - "node_modules/nan": { - "version": "2.25.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.25.0.tgz", - "integrity": "sha512-0M90Ag7Xn5KMLLZ7zliPWP3rT90P6PN+IzVFS0VqmnPktBk3700xUVv8Ikm9EUaUE5SDWdp/BIxdENzVznpm1g==", - "license": "MIT", - "optional": true - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-addon-api": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", - "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==", - "license": "MIT" - }, - "node_modules/node-emoji": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", - "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.21" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch-native": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", - "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/node-gyp-build": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", - "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", - "license": "MIT", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.36", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", - "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "license": "ISC", - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "dependencies": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" - } - }, - "node_modules/nypm": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.5.tgz", - "integrity": "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "citty": "^0.2.0", - "pathe": "^2.0.3", - "tinyexec": "^1.0.2" - }, - "bin": { - "nypm": "dist/cli.mjs" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/nypm/node_modules/citty": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.1.tgz", - "integrity": "sha512-kEV95lFBhQgtogAPlQfJJ0WGVSokvLr/UEoFPiKKOXF7pl98HfUVUD0ejsuTCld/9xH9vogSywZ5KqHzXrZpqg==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ohash": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", - "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ora": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", - "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "bl": "^4.1.0", - "chalk": "^4.1.0", - "cli-cursor": "^3.1.0", - "cli-spinners": "^2.5.0", - "is-interactive": "^1.0.0", - "is-unicode-supported": "^0.1.0", - "log-symbols": "^4.1.0", - "strip-ansi": "^6.0.0", - "wcwidth": "^1.0.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/passport": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", - "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", - "license": "MIT", - "dependencies": { - "passport-strategy": "1.x.x", - "pause": "0.0.1", - "utils-merge": "^1.0.1" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/passport-jwt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", - "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", - "license": "MIT", - "dependencies": { - "jsonwebtoken": "^9.0.0", - "passport-strategy": "^1.0.0" - } - }, - "node_modules/passport-strategy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/path-to-regexp": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-3.3.0.tgz", - "integrity": "sha512-qyCH421YQPS2WFDxDjftfc1ZR5WKQzVzqsp4n9M2kQhVOo/ByahFoUNJfl58kOcEGfQ//7weFTDhm+ss8Ecxgw==", - "license": "MIT" - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/pathe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/pause": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" - }, - "node_modules/perfect-debounce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", - "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.1.tgz", - "integrity": "sha512-xUXwsxNjwTQ8K3GnT4pCJm+xq3RUPQbmkYJTP5aFIfNIvbcc/4MUxgBaaRSZJ6yGJZiGSyYlM6MzwTsRk8SYCg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-types": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", - "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "confbox": "^0.2.2", - "exsolve": "^1.0.7", - "pathe": "^2.0.3" - } - }, - "node_modules/pluralize": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", - "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/pngjs": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz", - "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==", - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/prisma": { - "version": "6.19.2", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.19.2.tgz", - "integrity": "sha512-XTKeKxtQElcq3U9/jHyxSPgiRgeYDKxWTPOf6NkXA0dNj5j40MfEsZkMbyNpwDWCUv7YBFUl7I2VK/6ALbmhEg==", - "devOptional": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@prisma/config": "6.19.2", - "@prisma/engines": "6.19.2" - }, - "bin": { - "prisma": "build/index.js" - }, - "engines": { - "node": ">=18.18" - }, - "peerDependencies": { - "typescript": ">=5.1.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "devOptional": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/qrcode": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz", - "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==", - "license": "MIT", - "dependencies": { - "dijkstrajs": "^1.0.1", - "pngjs": "^5.0.0", - "yargs": "^15.3.1" - }, - "bin": { - "qrcode": "bin/qrcode" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/qrcode/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/qrcode/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "license": "ISC" - }, - "node_modules/qrcode/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "license": "MIT", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/qrcode/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", - "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", - "license": "MIT", - "dependencies": { - "bytes": "~3.1.2", - "http-errors": "~2.0.1", - "iconv-lite": "~0.4.24", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/rc9": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", - "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "defu": "^6.1.4", - "destr": "^2.0.3" - } - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/readdirp/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/reflect-metadata": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", - "license": "Apache-2.0" - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "license": "ISC" - }, - "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/restore-cursor/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-async": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", - "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/schema-utils": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", - "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.8", - "ajv": "^6.12.5", - "ajv-keywords": "^3.5.2" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/schema-utils/node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/schema-utils/node_modules/ajv-keywords": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "ajv": "^6.9.1" - } - }, - "node_modules/schema-utils/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", - "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "~0.5.2", - "http-errors": "~2.0.1", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "~2.4.1", - "range-parser": "~1.2.1", - "statuses": "~2.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/serve-static": { - "version": "1.16.3", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", - "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "~0.19.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "license": "ISC" - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">= 8" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/speakeasy": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/speakeasy/-/speakeasy-2.0.0.tgz", - "integrity": "sha512-lW2A2s5LKi8rwu77ewisuUOtlCydF/hmQSOJjpTqTj1gZLkNgTaYnyvfxy2WBr4T/h+9c4g8HIITfj83OkFQFw==", - "license": "MIT", - "dependencies": { - "base32.js": "0.0.1" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/ssh2": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.17.0.tgz", - "integrity": "sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==", - "hasInstallScript": true, - "dependencies": { - "asn1": "^0.2.6", - "bcrypt-pbkdf": "^1.0.2" - }, - "engines": { - "node": ">=10.16.0" - }, - "optionalDependencies": { - "cpu-features": "~0.0.10", - "nan": "^2.23.0" - } - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strtok3": { - "version": "10.3.4", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", - "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", - "license": "MIT", - "dependencies": { - "@tokenizer/token": "^0.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/symbol-observable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", - "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/tapable": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", - "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "license": "ISC", - "engines": { - "node": ">=8" - } - }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, - "node_modules/terser": { - "version": "5.46.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz", - "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.15.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.4.0.tgz", - "integrity": "sha512-Bn5vxm48flOIfkdl5CaD2+1CiUVbonWQ3KQPyP7/EuIl9Gbzq/gQFOzaMFUEgVjB1396tcK0SG8XcNJ/2kDH8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "terser": "^5.31.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/schema-utils": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", - "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/terser-webpack-plugin/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/terser/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/terser/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/terser/node_modules/source-map-support": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", - "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", - "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "os-tmpdir": "~1.0.2" - }, - "engines": { - "node": ">=0.6.0" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/token-types": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", - "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", - "license": "MIT", - "dependencies": { - "@borewit/text-codec": "^0.2.1", - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, - "license": "MIT", - "bin": { - "tree-kill": "cli.js" - } - }, - "node_modules/ts-jest": { - "version": "29.4.6", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.6.tgz", - "integrity": "sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "^0.2.6", - "fast-json-stable-stringify": "^2.1.0", - "handlebars": "^4.7.8", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.3", - "type-fest": "^4.41.0", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0 || ^30.0.0", - "@jest/types": "^29.0.0 || ^30.0.0", - "babel-jest": "^29.0.0 || ^30.0.0", - "jest": "^29.0.0 || ^30.0.0", - "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jest-util": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ts-node": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", - "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@cspotcode/source-map-support": "^0.8.0", - "@tsconfig/node10": "^1.0.7", - "@tsconfig/node12": "^1.0.7", - "@tsconfig/node14": "^1.0.0", - "@tsconfig/node16": "^1.0.2", - "acorn": "^8.4.1", - "acorn-walk": "^8.1.1", - "arg": "^4.1.0", - "create-require": "^1.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "v8-compile-cache-lib": "^3.0.1", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-node-cwd": "dist/bin-cwd.js", - "ts-node-esm": "dist/bin-esm.js", - "ts-node-script": "dist/bin-script.js", - "ts-node-transpile-only": "dist/bin-transpile.js", - "ts-script": "dist/bin-script-deprecated.js" - }, - "peerDependencies": { - "@swc/core": ">=1.2.50", - "@swc/wasm": ">=1.2.50", - "@types/node": "*", - "typescript": ">=2.7" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "@swc/wasm": { - "optional": true - } - } - }, - "node_modules/tsconfig-paths": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz", - "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "json5": "^2.2.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tsconfig-paths-webpack-plugin": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-4.2.0.tgz", - "integrity": "sha512-zbem3rfRS8BgeNK50Zz5SIQgXzLafiHjOwUAvk/38/o1jHn/V5QAgVUcz884or7WYcPaH3N2CIfUc2u0ul7UcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.7.0", - "tapable": "^2.2.1", - "tsconfig-paths": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/tsconfig-paths/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", - "license": "Unlicense" - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "license": "MIT" - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "devOptional": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/uid": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", - "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", - "license": "MIT", - "dependencies": { - "@lukeed/csprng": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/uint8array-extras": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", - "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT" - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", - "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", - "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist-node/bin/uuid" - } - }, - "node_modules/v8-compile-cache-lib": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", - "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", - "dev": true, - "license": "MIT" - }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/validator": { - "version": "13.15.26", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.26.tgz", - "integrity": "sha512-spH26xU080ydGggxRyR1Yhcbgx+j3y5jbNXk/8L+iRvdIEQ4uTRH2Sgf2dokud6Q4oAtsbNvJ1Ft+9xmm6IZcA==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/watchpack": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz", - "integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wcwidth": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", - "dev": true, - "license": "MIT", - "dependencies": { - "defaults": "^1.0.3" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/webpack": { - "version": "5.97.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.97.1.tgz", - "integrity": "sha512-EksG6gFY3L1eFMROS/7Wzgrii5mBAFe4rIr3r2BTfo7bcc+DWwFZ4OJ/miOuHJO/A85HwyI4eQ0F6IKXesO7Fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.14.0", - "browserslist": "^4.24.0", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^3.2.0", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.10", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-node-externals": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/webpack-node-externals/-/webpack-node-externals-3.0.0.tgz", - "integrity": "sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/webpack-sources": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz", - "integrity": "sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "license": "ISC" - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "license": "ISC", - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/write-file-atomic/node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/ws": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - } -} diff --git a/backend/package.json b/backend/package.json deleted file mode 100644 index 109ca22..0000000 --- a/backend/package.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "name": "wraith-backend", - "version": "0.1.0", - "private": true, - "scripts": { - "build": "nest build", - "start": "node dist/main.js", - "dev": "nest start --watch", - "test": "jest", - "test:watch": "jest --watch", - "prisma:migrate": "prisma migrate dev", - "prisma:generate": "prisma generate", - "prisma:seed": "ts-node prisma/seed.ts" - }, - "dependencies": { - "@nestjs/common": "^10.0.0", - "@nestjs/core": "^10.0.0", - "@nestjs/jwt": "^10.0.0", - "@nestjs/mapped-types": "^2.0.0", - "@nestjs/passport": "^10.0.0", - "@nestjs/throttler": "^6.0.0", - "@nestjs/platform-express": "^10.0.0", - "@nestjs/platform-ws": "^10.0.0", - "@nestjs/serve-static": "^4.0.0", - "@nestjs/websockets": "^10.0.0", - "@prisma/client": "^6.0.0", - "argon2": "^0.44.0", - "helmet": "^8.0.0", - "bcrypt": "^5.1.0", - "cookie-parser": "^1.4.7", - "class-transformer": "^0.5.1", - "class-validator": "^0.14.0", - "passport": "^0.7.0", - "passport-jwt": "^4.0.1", - "qrcode": "^1.5.4", - "reflect-metadata": "^0.2.0", - "rxjs": "^7.8.0", - "speakeasy": "^2.0.0", - "ssh2": "^1.15.0", - "uuid": "^13.0.0", - "ws": "^8.16.0" - }, - "devDependencies": { - "@nestjs/cli": "^10.0.0", - "@nestjs/testing": "^10.0.0", - "@types/bcrypt": "^5.0.0", - "@types/cookie-parser": "^1.4.8", - "@types/jest": "^30.0.0", - "@types/node": "^20.0.0", - "@types/passport-jwt": "^4.0.0", - "@types/qrcode": "^1.5.6", - "@types/speakeasy": "^2.0.10", - "@types/ssh2": "^1.15.0", - "@types/uuid": "^10.0.0", - "@types/ws": "^8.5.0", - "jest": "^29.0.0", - "prisma": "^6.0.0", - "ts-jest": "^29.0.0", - "ts-node": "^10.9.0", - "typescript": "^5.3.0" - }, - "prisma": { - "seed": "ts-node prisma/seed.ts" - }, - "jest": { - "moduleFileExtensions": [ - "js", - "json", - "ts" - ], - "rootDir": ".", - "testRegex": ".*\\.spec\\.ts$", - "transform": { - "^.+\\.ts$": "ts-jest" - }, - "testEnvironment": "node", - "moduleNameMapper": { - "^@/(.*)$": "/src/$1" - } - } -} diff --git a/backend/prisma/migrations/20260313000000_init/migration.sql b/backend/prisma/migrations/20260313000000_init/migration.sql deleted file mode 100644 index f6e6372..0000000 --- a/backend/prisma/migrations/20260313000000_init/migration.sql +++ /dev/null @@ -1,121 +0,0 @@ --- CreateSchema -CREATE SCHEMA IF NOT EXISTS "public"; - --- CreateEnum -CREATE TYPE "Protocol" AS ENUM ('ssh', 'rdp'); - --- CreateEnum -CREATE TYPE "CredentialType" AS ENUM ('password', 'ssh_key'); - --- CreateTable -CREATE TABLE "users" ( - "id" SERIAL NOT NULL, - "email" TEXT NOT NULL, - "password_hash" TEXT NOT NULL, - "display_name" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "users_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "host_groups" ( - "id" SERIAL NOT NULL, - "name" TEXT NOT NULL, - "parent_id" INTEGER, - "sort_order" INTEGER NOT NULL DEFAULT 0, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "host_groups_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "hosts" ( - "id" SERIAL NOT NULL, - "name" TEXT NOT NULL, - "hostname" TEXT NOT NULL, - "port" INTEGER NOT NULL DEFAULT 22, - "protocol" "Protocol" NOT NULL DEFAULT 'ssh', - "group_id" INTEGER, - "credential_id" INTEGER, - "tags" TEXT[] DEFAULT ARRAY[]::TEXT[], - "notes" TEXT, - "color" VARCHAR(7), - "sort_order" INTEGER NOT NULL DEFAULT 0, - "host_fingerprint" TEXT, - "last_connected_at" TIMESTAMP(3), - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "hosts_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "credentials" ( - "id" SERIAL NOT NULL, - "name" TEXT NOT NULL, - "username" TEXT, - "domain" TEXT, - "type" "CredentialType" NOT NULL, - "encrypted_value" TEXT, - "ssh_key_id" INTEGER, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "credentials_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "ssh_keys" ( - "id" SERIAL NOT NULL, - "name" TEXT NOT NULL, - "key_type" VARCHAR(20) NOT NULL, - "fingerprint" TEXT, - "public_key" TEXT, - "encrypted_private_key" TEXT NOT NULL, - "passphrase_encrypted" TEXT, - "created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updated_at" TIMESTAMP(3) NOT NULL, - - CONSTRAINT "ssh_keys_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "connection_logs" ( - "id" SERIAL NOT NULL, - "host_id" INTEGER NOT NULL, - "protocol" "Protocol" NOT NULL, - "connected_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, - "disconnected_at" TIMESTAMP(3), - - CONSTRAINT "connection_logs_pkey" PRIMARY KEY ("id") -); - --- CreateTable -CREATE TABLE "settings" ( - "key" TEXT NOT NULL, - "value" TEXT NOT NULL, - - CONSTRAINT "settings_pkey" PRIMARY KEY ("key") -); - --- CreateIndex -CREATE UNIQUE INDEX "users_email_key" ON "users"("email"); - --- AddForeignKey -ALTER TABLE "host_groups" ADD CONSTRAINT "host_groups_parent_id_fkey" FOREIGN KEY ("parent_id") REFERENCES "host_groups"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "hosts" ADD CONSTRAINT "hosts_group_id_fkey" FOREIGN KEY ("group_id") REFERENCES "host_groups"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "hosts" ADD CONSTRAINT "hosts_credential_id_fkey" FOREIGN KEY ("credential_id") REFERENCES "credentials"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "credentials" ADD CONSTRAINT "credentials_ssh_key_id_fkey" FOREIGN KEY ("ssh_key_id") REFERENCES "ssh_keys"("id") ON DELETE SET NULL ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "connection_logs" ADD CONSTRAINT "connection_logs_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts"("id") ON DELETE CASCADE ON UPDATE CASCADE; - diff --git a/backend/prisma/migrations/20260313120000_add_totp/migration.sql b/backend/prisma/migrations/20260313120000_add_totp/migration.sql deleted file mode 100644 index ba8187f..0000000 --- a/backend/prisma/migrations/20260313120000_add_totp/migration.sql +++ /dev/null @@ -1,3 +0,0 @@ --- AlterTable -ALTER TABLE "users" ADD COLUMN "totp_secret" TEXT; -ALTER TABLE "users" ADD COLUMN "totp_enabled" BOOLEAN NOT NULL DEFAULT false; diff --git a/backend/prisma/migrations/20260314000000_multi_user_isolation/migration.sql b/backend/prisma/migrations/20260314000000_multi_user_isolation/migration.sql deleted file mode 100644 index d0d6b8f..0000000 --- a/backend/prisma/migrations/20260314000000_multi_user_isolation/migration.sql +++ /dev/null @@ -1,36 +0,0 @@ --- Delete duplicate admin users first (keep the one with lowest id) -DELETE FROM "users" WHERE "email" = 'admin@wraith.local' AND "id" != (SELECT MIN("id") FROM "users" WHERE "email" = 'admin@wraith.local'); - --- Add role to users -ALTER TABLE "users" ADD COLUMN "role" TEXT NOT NULL DEFAULT 'user'; - --- Backfill admin@wraith.local as admin -UPDATE "users" SET "role" = 'admin' WHERE "email" = 'admin@wraith.local'; - --- Add user_id to all data tables -ALTER TABLE "hosts" ADD COLUMN "user_id" INTEGER; -ALTER TABLE "host_groups" ADD COLUMN "user_id" INTEGER; -ALTER TABLE "credentials" ADD COLUMN "user_id" INTEGER; -ALTER TABLE "ssh_keys" ADD COLUMN "user_id" INTEGER; -ALTER TABLE "connection_logs" ADD COLUMN "user_id" INTEGER; - --- Backfill existing data to the admin user -UPDATE "hosts" SET "user_id" = (SELECT "id" FROM "users" WHERE "email" = 'admin@wraith.local'); -UPDATE "host_groups" SET "user_id" = (SELECT "id" FROM "users" WHERE "email" = 'admin@wraith.local'); -UPDATE "credentials" SET "user_id" = (SELECT "id" FROM "users" WHERE "email" = 'admin@wraith.local'); -UPDATE "ssh_keys" SET "user_id" = (SELECT "id" FROM "users" WHERE "email" = 'admin@wraith.local'); -UPDATE "connection_logs" SET "user_id" = (SELECT "id" FROM "users" WHERE "email" = 'admin@wraith.local'); - --- Make user_id NOT NULL after backfill -ALTER TABLE "hosts" ALTER COLUMN "user_id" SET NOT NULL; -ALTER TABLE "host_groups" ALTER COLUMN "user_id" SET NOT NULL; -ALTER TABLE "credentials" ALTER COLUMN "user_id" SET NOT NULL; -ALTER TABLE "ssh_keys" ALTER COLUMN "user_id" SET NOT NULL; -ALTER TABLE "connection_logs" ALTER COLUMN "user_id" SET NOT NULL; - --- Add foreign keys -ALTER TABLE "hosts" ADD CONSTRAINT "hosts_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE; -ALTER TABLE "host_groups" ADD CONSTRAINT "host_groups_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE; -ALTER TABLE "credentials" ADD CONSTRAINT "credentials_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE; -ALTER TABLE "ssh_keys" ADD CONSTRAINT "ssh_keys_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE; -ALTER TABLE "connection_logs" ADD CONSTRAINT "connection_logs_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE; diff --git a/backend/prisma/migrations/migration_lock.toml b/backend/prisma/migrations/migration_lock.toml deleted file mode 100644 index 99e4f20..0000000 --- a/backend/prisma/migrations/migration_lock.toml +++ /dev/null @@ -1,3 +0,0 @@ -# Please do not edit this file manually -# It should be added in your version-control system (i.e. Git) -provider = "postgresql" diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma deleted file mode 100644 index 8c69314..0000000 --- a/backend/prisma/schema.prisma +++ /dev/null @@ -1,133 +0,0 @@ -generator client { - provider = "prisma-client-js" -} - -datasource db { - provider = "postgresql" - url = env("DATABASE_URL") -} - -model User { - id Int @id @default(autoincrement()) - email String @unique - passwordHash String @map("password_hash") - displayName String? @map("display_name") - role String @default("user") - totpSecret String? @map("totp_secret") - totpEnabled Boolean @default(false) @map("totp_enabled") - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") - hosts Host[] - hostGroups HostGroup[] - credentials Credential[] - sshKeys SshKey[] - connectionLogs ConnectionLog[] - - @@map("users") -} - -model HostGroup { - id Int @id @default(autoincrement()) - name String - parentId Int? @map("parent_id") - userId Int @map("user_id") - sortOrder Int @default(0) @map("sort_order") - parent HostGroup? @relation("GroupTree", fields: [parentId], references: [id], onDelete: SetNull) - children HostGroup[] @relation("GroupTree") - hosts Host[] - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") - - @@map("host_groups") -} - -model Host { - id Int @id @default(autoincrement()) - name String - hostname String - port Int @default(22) - protocol Protocol @default(ssh) - groupId Int? @map("group_id") - credentialId Int? @map("credential_id") - userId Int @map("user_id") - tags String[] @default([]) - notes String? - color String? @db.VarChar(7) - sortOrder Int @default(0) @map("sort_order") - hostFingerprint String? @map("host_fingerprint") - lastConnectedAt DateTime? @map("last_connected_at") - group HostGroup? @relation(fields: [groupId], references: [id], onDelete: SetNull) - credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull) - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - connectionLogs ConnectionLog[] - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") - - @@map("hosts") -} - -model Credential { - id Int @id @default(autoincrement()) - name String - username String? - domain String? - type CredentialType - userId Int @map("user_id") - encryptedValue String? @map("encrypted_value") - sshKeyId Int? @map("ssh_key_id") - sshKey SshKey? @relation(fields: [sshKeyId], references: [id], onDelete: SetNull) - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - hosts Host[] - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") - - @@map("credentials") -} - -model SshKey { - id Int @id @default(autoincrement()) - name String - keyType String @map("key_type") @db.VarChar(20) - fingerprint String? - publicKey String? @map("public_key") - userId Int @map("user_id") - encryptedPrivateKey String @map("encrypted_private_key") - passphraseEncrypted String? @map("passphrase_encrypted") - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - credentials Credential[] - createdAt DateTime @default(now()) @map("created_at") - updatedAt DateTime @updatedAt @map("updated_at") - - @@map("ssh_keys") -} - -model ConnectionLog { - id Int @id @default(autoincrement()) - hostId Int @map("host_id") - userId Int @map("user_id") - protocol Protocol - connectedAt DateTime @default(now()) @map("connected_at") - disconnectedAt DateTime? @map("disconnected_at") - host Host @relation(fields: [hostId], references: [id], onDelete: Cascade) - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - - @@map("connection_logs") -} - -model Setting { - key String @id - value String - - @@map("settings") -} - -enum Protocol { - ssh - rdp -} - -enum CredentialType { - password - ssh_key -} diff --git a/backend/prisma/seed.ts b/backend/prisma/seed.ts deleted file mode 100644 index 334e653..0000000 --- a/backend/prisma/seed.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { PrismaClient } from '@prisma/client'; -import * as bcrypt from 'bcrypt'; - -const prisma = new PrismaClient(); - -async function main() { - const hash = await bcrypt.hash('wraith', 10); - await prisma.user.upsert({ - where: { email: 'admin@wraith.local' }, - update: {}, - create: { - email: 'admin@wraith.local', - passwordHash: hash, - displayName: 'Admin', - role: 'admin', - }, - }); - console.log('Seed complete: admin@wraith.local / wraith (role: admin)'); -} - -main() - .catch(console.error) - .finally(() => prisma.$disconnect()); diff --git a/backend/seed.js b/backend/seed.js deleted file mode 100644 index 8b11427..0000000 --- a/backend/seed.js +++ /dev/null @@ -1,26 +0,0 @@ -const { PrismaClient } = require('@prisma/client'); -const bcrypt = require('bcrypt'); - -const prisma = new PrismaClient(); - -async function main() { - const anyAdmin = await prisma.user.findFirst({ where: { role: 'admin' } }); - if (anyAdmin) { - console.log(`Seed: admin account exists (${anyAdmin.email}), skipping default seed`); - return; - } - const hash = await bcrypt.hash('wraith', 10); - await prisma.user.create({ - data: { - email: 'admin@wraith.local', - passwordHash: hash, - displayName: 'Admin', - role: 'admin', - }, - }); - console.log('Seed complete: admin@wraith.local / wraith (role: admin)'); -} - -main() - .catch(console.error) - .finally(() => prisma.$disconnect()); diff --git a/backend/src/__mocks__/prisma.mock.ts b/backend/src/__mocks__/prisma.mock.ts deleted file mode 100644 index af73948..0000000 --- a/backend/src/__mocks__/prisma.mock.ts +++ /dev/null @@ -1,43 +0,0 @@ -// backend/src/__mocks__/prisma.mock.ts -export const mockPrismaService = { - user: { - findUnique: jest.fn(), - findFirst: jest.fn(), - findMany: jest.fn(), - create: jest.fn(), - update: jest.fn(), - delete: jest.fn(), - }, - credential: { - findUnique: jest.fn(), - findMany: jest.fn(), - create: jest.fn(), - update: jest.fn(), - delete: jest.fn(), - }, - sshKey: { - findUnique: jest.fn(), - findMany: jest.fn(), - create: jest.fn(), - update: jest.fn(), - delete: jest.fn(), - }, - connectionLog: { - create: jest.fn(), - updateMany: jest.fn(), - }, -}; - -export function createMockPrisma() { - // Deep clone to prevent test bleed — re-attach jest.fn() since JSON.parse loses functions - const models = Object.keys(mockPrismaService) as Array; - const mock: any = {}; - for (const model of models) { - mock[model] = {}; - const methods = Object.keys(mockPrismaService[model]); - for (const method of methods) { - mock[model][method] = jest.fn(); - } - } - return mock; -} diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts deleted file mode 100644 index c7bdb25..0000000 --- a/backend/src/app.module.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ServeStaticModule } from '@nestjs/serve-static'; -import { ThrottlerModule } from '@nestjs/throttler'; -import { join } from 'path'; -import { PrismaModule } from './prisma/prisma.module'; -import { AuthModule } from './auth/auth.module'; -import { VaultModule } from './vault/vault.module'; -import { ConnectionsModule } from './connections/connections.module'; -import { SettingsModule } from './settings/settings.module'; -import { TerminalModule } from './terminal/terminal.module'; -import { RdpModule } from './rdp/rdp.module'; - -@Module({ - imports: [ - PrismaModule, - AuthModule, - VaultModule, - ConnectionsModule, - SettingsModule, - TerminalModule, - RdpModule, - // Rate limiting (H-6) — permissive global default, tightened on auth endpoints - ThrottlerModule.forRoot([{ ttl: 60000, limit: 60 }]), - ServeStaticModule.forRoot({ - rootPath: join(__dirname, '..', '..', 'public'), - exclude: ['/api/(.*)', '/ws/(.*)'], - }), - ], -}) -export class AppModule {} diff --git a/backend/src/auth/admin.guard.spec.ts b/backend/src/auth/admin.guard.spec.ts deleted file mode 100644 index a07034b..0000000 --- a/backend/src/auth/admin.guard.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -// backend/src/auth/admin.guard.spec.ts -import { AdminGuard } from './admin.guard'; -import { ExecutionContext, ForbiddenException } from '@nestjs/common'; - -function createMockContext(user: any): ExecutionContext { - return { - switchToHttp: () => ({ - getRequest: () => ({ user }), - }), - } as any; -} - -describe('AdminGuard', () => { - const guard = new AdminGuard(); - - it('should allow request when user has admin role', () => { - const ctx = createMockContext({ role: 'admin' }); - expect(guard.canActivate(ctx)).toBe(true); - }); - - it('should throw ForbiddenException for non-admin role', () => { - const ctx = createMockContext({ role: 'user' }); - expect(() => guard.canActivate(ctx)).toThrow(ForbiddenException); - }); - - it('should throw ForbiddenException when user is undefined (unauthenticated)', () => { - const ctx = createMockContext(undefined); - expect(() => guard.canActivate(ctx)).toThrow(ForbiddenException); - }); -}); diff --git a/backend/src/auth/admin.guard.ts b/backend/src/auth/admin.guard.ts deleted file mode 100644 index 7d58269..0000000 --- a/backend/src/auth/admin.guard.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common'; - -@Injectable() -export class AdminGuard implements CanActivate { - canActivate(context: ExecutionContext): boolean { - const req = context.switchToHttp().getRequest(); - if (req.user?.role !== 'admin') { - throw new ForbiddenException('Admin access required'); - } - return true; - } -} diff --git a/backend/src/auth/auth.controller.spec.ts b/backend/src/auth/auth.controller.spec.ts deleted file mode 100644 index b5434ae..0000000 --- a/backend/src/auth/auth.controller.spec.ts +++ /dev/null @@ -1,99 +0,0 @@ -// backend/src/auth/auth.controller.spec.ts -import { Test, TestingModule } from '@nestjs/testing'; -import { AuthController } from './auth.controller'; -import { AuthService } from './auth.service'; -import { JwtService } from '@nestjs/jwt'; - -describe('AuthController', () => { - let controller: AuthController; - let authService: any; - - beforeEach(async () => { - authService = { - login: jest.fn(), - getProfile: jest.fn(), - totpSetup: jest.fn(), - listUsers: jest.fn(), - }; - - const module: TestingModule = await Test.createTestingModule({ - controllers: [AuthController], - providers: [ - { provide: AuthService, useValue: authService }, - { provide: JwtService, useValue: { sign: jest.fn(), verify: jest.fn() } }, - ], - }).compile(); - - controller = module.get(AuthController); - }); - - describe('login', () => { - it('should set httpOnly cookie and return user without access_token in body', async () => { - authService.login.mockResolvedValue({ - access_token: 'jwt-token', - user: { id: 1, email: 'test@test.com', displayName: 'Test', role: 'admin' }, - }); - const res = { cookie: jest.fn() }; - - const result = await controller.login( - { email: 'test@test.com', password: 'pass' } as any, - res, - ); - - expect(res.cookie).toHaveBeenCalledWith( - 'wraith_token', - 'jwt-token', - expect.objectContaining({ - httpOnly: true, - sameSite: 'strict', - path: '/', - }), - ); - expect(result).toEqual({ user: expect.objectContaining({ email: 'test@test.com' }) }); - // Token must NOT be in the response body (C-2 security requirement) - expect(result).not.toHaveProperty('access_token'); - }); - - it('should pass through requires_totp without setting cookie', async () => { - authService.login.mockResolvedValue({ requires_totp: true }); - const res = { cookie: jest.fn() }; - - const result = await controller.login( - { email: 'test@test.com', password: 'pass' } as any, - res, - ); - - expect(res.cookie).not.toHaveBeenCalled(); - expect(result).toEqual({ requires_totp: true }); - }); - }); - - describe('logout', () => { - it('should clear the wraith_token cookie', () => { - const res = { clearCookie: jest.fn() }; - const result = controller.logout(res); - expect(res.clearCookie).toHaveBeenCalledWith('wraith_token', { path: '/' }); - expect(result).toEqual({ message: 'Logged out' }); - }); - }); - - describe('ws-ticket', () => { - it('should issue a 64-char hex ticket', () => { - const req = { user: { sub: 1, email: 'test@test.com', role: 'admin' } }; - const result = controller.issueWsTicket(req); - expect(result).toHaveProperty('ticket'); - expect(result.ticket).toHaveLength(64); // 32 random bytes → 64 hex chars - }); - - it('should consume ticket exactly once (single-use)', () => { - const req = { user: { sub: 5, email: 'once@test.com', role: 'user' } }; - const { ticket } = controller.issueWsTicket(req); - - const first = AuthController.consumeWsTicket(ticket); - expect(first).toEqual(expect.objectContaining({ sub: 5, email: 'once@test.com' })); - - const second = AuthController.consumeWsTicket(ticket); - expect(second).toBeNull(); // Ticket is deleted after first use - }); - }); -}); diff --git a/backend/src/auth/auth.controller.ts b/backend/src/auth/auth.controller.ts deleted file mode 100644 index ad3f85e..0000000 --- a/backend/src/auth/auth.controller.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { Controller, Post, Get, Put, Delete, Body, Param, Request, Response, UseGuards, ParseIntPipe, InternalServerErrorException } from '@nestjs/common'; -import { AuthService } from './auth.service'; -import { JwtAuthGuard } from './jwt-auth.guard'; -import { AdminGuard } from './admin.guard'; -import { LoginDto } from './dto/login.dto'; -import { UpdateProfileDto } from './dto/update-profile.dto'; -import { JwtService } from '@nestjs/jwt'; -import { randomBytes } from 'crypto'; - -// In-memory WS ticket store (short-lived, single-use) (C-3) -const wsTickets = new Map(); - -// Clean up expired tickets every 60 seconds -setInterval(() => { - const now = Date.now(); - for (const [ticket, data] of wsTickets) { - if (data.expires < now) wsTickets.delete(ticket); - } -}, 60000); - -@Controller('auth') -export class AuthController { - constructor( - private auth: AuthService, - private jwt: JwtService, - ) {} - - @Post('login') - async login(@Body() dto: LoginDto, @Response({ passthrough: true }) res: any) { - const result = await this.auth.login(dto.email, dto.password, dto.totpCode); - - if ('requires_totp' in result) { - return result; - } - - // Set JWT as httpOnly cookie (C-2) - res.cookie('wraith_token', result.access_token, { - httpOnly: true, - secure: process.env.NODE_ENV === 'production', - sameSite: 'strict', - maxAge: 4 * 60 * 60 * 1000, // 4 hours (H-1: down from 7 days) - path: '/', - }); - - // Return user info only — token is in the cookie, NOT in the response body - return { user: result.user }; - } - - @Post('logout') - @UseGuards(JwtAuthGuard) - logout(@Response({ passthrough: true }) res: any) { - res.clearCookie('wraith_token', { path: '/' }); - return { message: 'Logged out' }; - } - - /** - * Issue a short-lived, single-use WebSocket ticket (C-3). - * Frontend calls this before opening a WS connection, then passes - * the ticket as ?ticket= instead of the JWT. - */ - @Post('ws-ticket') - @UseGuards(JwtAuthGuard) - issueWsTicket(@Request() req: any) { - const ticket = randomBytes(32).toString('hex'); - wsTickets.set(ticket, { - userId: req.user.sub, - email: req.user.email, - role: req.user.role, - expires: Date.now() + 30000, // 30 seconds - }); - return { ticket }; - } - - /** - * Validate and consume a WS ticket. Called by WsAuthGuard. - * Returns the user payload or null if invalid/expired. - */ - static consumeWsTicket(ticket: string): { sub: number; email: string; role: string } | null { - const data = wsTickets.get(ticket); - if (!data) return null; - wsTickets.delete(ticket); // Single-use - if (data.expires < Date.now()) return null; - return { sub: data.userId, email: data.email, role: data.role }; - } - - @UseGuards(JwtAuthGuard) - @Get('profile') - getProfile(@Request() req: any) { - return this.auth.getProfile(req.user.sub); - } - - @UseGuards(JwtAuthGuard) - @Put('profile') - async updateProfile(@Request() req: any, @Body() dto: UpdateProfileDto) { - try { - return await this.auth.updateProfile(req.user.sub, dto); - } catch (e: any) { - if (e.status) throw e; - throw new InternalServerErrorException('Profile update failed'); - } - } - - @UseGuards(JwtAuthGuard) - @Post('totp/setup') - totpSetup(@Request() req: any) { - return this.auth.totpSetup(req.user.sub); - } - - @UseGuards(JwtAuthGuard) - @Post('totp/verify') - totpVerify(@Request() req: any, @Body() body: { code: string }) { - return this.auth.totpVerify(req.user.sub, body.code); - } - - @UseGuards(JwtAuthGuard) - @Post('totp/disable') - totpDisable(@Request() req: any, @Body() body: { password: string }) { - return this.auth.totpDisable(req.user.sub, body.password); - } - - // ─── Admin User Management ────────────────────────────────────────────── - - @UseGuards(JwtAuthGuard, AdminGuard) - @Get('users') - listUsers() { - return this.auth.listUsers(); - } - - @UseGuards(JwtAuthGuard, AdminGuard) - @Post('users') - createUser(@Body() body: { email: string; password: string; displayName?: string; role?: string }) { - return this.auth.createUser(body); - } - - @UseGuards(JwtAuthGuard, AdminGuard) - @Put('users/:id') - updateUser(@Param('id', ParseIntPipe) id: number, @Request() req: any, @Body() body: { email?: string; displayName?: string; role?: string }) { - return this.auth.adminUpdateUser(id, req.user.sub, body); - } - - @UseGuards(JwtAuthGuard, AdminGuard) - @Post('users/:id/reset-password') - resetPassword(@Param('id', ParseIntPipe) id: number, @Body() body: { password: string }) { - return this.auth.adminResetPassword(id, body.password); - } - - @UseGuards(JwtAuthGuard, AdminGuard) - @Post('users/:id/reset-totp') - resetTotp(@Param('id', ParseIntPipe) id: number) { - return this.auth.adminResetTotp(id); - } - - @UseGuards(JwtAuthGuard, AdminGuard) - @Delete('users/:id') - deleteUser(@Param('id', ParseIntPipe) id: number, @Request() req: any) { - return this.auth.adminDeleteUser(id, req.user.sub); - } -} diff --git a/backend/src/auth/auth.module.ts b/backend/src/auth/auth.module.ts deleted file mode 100644 index e013c37..0000000 --- a/backend/src/auth/auth.module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Module } from '@nestjs/common'; -import { JwtModule } from '@nestjs/jwt'; -import { PassportModule } from '@nestjs/passport'; -import { AuthService } from './auth.service'; -import { AuthController } from './auth.controller'; -import { JwtStrategy } from './jwt.strategy'; -import { WsAuthGuard } from './ws-auth.guard'; -import { AdminGuard } from './admin.guard'; -import { VaultModule } from '../vault/vault.module'; - -@Module({ - imports: [ - PassportModule, - VaultModule, // EncryptionService for TOTP secret encryption (H-3) - JwtModule.register({ - secret: process.env.JWT_SECRET, - signOptions: { expiresIn: '4h' }, // H-1: down from 7 days - }), - ], - providers: [AuthService, JwtStrategy, WsAuthGuard, AdminGuard], - controllers: [AuthController], - exports: [WsAuthGuard, AdminGuard, JwtModule], -}) -export class AuthModule {} diff --git a/backend/src/auth/auth.service.spec.ts b/backend/src/auth/auth.service.spec.ts deleted file mode 100644 index 040cc0c..0000000 --- a/backend/src/auth/auth.service.spec.ts +++ /dev/null @@ -1,204 +0,0 @@ -// backend/src/auth/auth.service.spec.ts -import { Test, TestingModule } from '@nestjs/testing'; -import { AuthService } from './auth.service'; -import { PrismaService } from '../prisma/prisma.service'; -import { JwtService } from '@nestjs/jwt'; -import { EncryptionService } from '../vault/encryption.service'; -import { - UnauthorizedException, - BadRequestException, - NotFoundException, - ForbiddenException, -} from '@nestjs/common'; -import * as argon2 from 'argon2'; -import * as bcrypt from 'bcrypt'; - -describe('AuthService', () => { - let service: AuthService; - let prisma: any; - let jwt: any; - let encryption: any; - - beforeEach(async () => { - prisma = { - user: { - findUnique: jest.fn(), - findMany: jest.fn(), - create: jest.fn(), - update: jest.fn(), - delete: jest.fn(), - }, - }; - jwt = { sign: jest.fn().mockReturnValue('mock-jwt-token') }; - encryption = { - encrypt: jest.fn().mockResolvedValue('v2:encrypted'), - decrypt: jest.fn().mockResolvedValue('decrypted-secret'), - }; - - const module: TestingModule = await Test.createTestingModule({ - providers: [ - AuthService, - { provide: PrismaService, useValue: prisma }, - { provide: JwtService, useValue: jwt }, - { provide: EncryptionService, useValue: encryption }, - ], - }).compile(); - - service = module.get(AuthService); - // onModuleInit pre-computes the dummy hash for timing-safe login - await service.onModuleInit(); - }); - - describe('login', () => { - it('should return access_token and user for valid Argon2id credentials', async () => { - const hash = await argon2.hash('correct-password', { type: argon2.argon2id }); - prisma.user.findUnique.mockResolvedValue({ - id: 1, - email: 'test@test.com', - passwordHash: hash, - displayName: 'Test User', - role: 'admin', - totpEnabled: false, - }); - - const result = await service.login('test@test.com', 'correct-password'); - expect(result).toHaveProperty('access_token', 'mock-jwt-token'); - expect((result as any).user.email).toBe('test@test.com'); - }); - - it('should throw UnauthorizedException for wrong password', async () => { - const hash = await argon2.hash('correct', { type: argon2.argon2id }); - prisma.user.findUnique.mockResolvedValue({ - id: 1, - email: 'test@test.com', - passwordHash: hash, - totpEnabled: false, - }); - await expect(service.login('test@test.com', 'wrong')).rejects.toThrow(UnauthorizedException); - }); - - it('should throw UnauthorizedException for non-existent user (constant time defense)', async () => { - prisma.user.findUnique.mockResolvedValue(null); - await expect(service.login('nobody@test.com', 'pass')).rejects.toThrow(UnauthorizedException); - }); - - it('should auto-upgrade bcrypt hash to Argon2id on successful login', async () => { - const bcryptHash = await bcrypt.hash('password', 10); - prisma.user.findUnique.mockResolvedValue({ - id: 1, - email: 'legacy@test.com', - passwordHash: bcryptHash, - displayName: 'Legacy User', - role: 'user', - totpEnabled: false, - }); - prisma.user.update.mockResolvedValue({}); - - await service.login('legacy@test.com', 'password'); - - expect(prisma.user.update).toHaveBeenCalledWith( - expect.objectContaining({ - where: { id: 1 }, - data: expect.objectContaining({ - passwordHash: expect.stringContaining('$argon2id$'), - }), - }), - ); - }); - - it('should return { requires_totp: true } when TOTP enabled but no code provided', async () => { - const hash = await argon2.hash('pass', { type: argon2.argon2id }); - prisma.user.findUnique.mockResolvedValue({ - id: 1, - email: 'totp@test.com', - passwordHash: hash, - totpEnabled: true, - totpSecret: 'v2:encrypted-secret', - }); - - const result = await service.login('totp@test.com', 'pass'); - expect(result).toEqual({ requires_totp: true }); - }); - }); - - describe('createUser', () => { - it('should hash password with Argon2id before storage', async () => { - prisma.user.findUnique.mockResolvedValue(null); - prisma.user.create.mockResolvedValue({ - id: 1, - email: 'new@test.com', - displayName: null, - role: 'user', - }); - - await service.createUser({ email: 'new@test.com', password: 'StrongPass1!' }); - - const createCall = prisma.user.create.mock.calls[0][0]; - expect(createCall.data.passwordHash).toMatch(/^\$argon2id\$/); - }); - - it('should throw BadRequestException for duplicate email', async () => { - prisma.user.findUnique.mockResolvedValue({ id: 1 }); - await expect( - service.createUser({ email: 'dup@test.com', password: 'pass' }), - ).rejects.toThrow(BadRequestException); - }); - }); - - describe('adminDeleteUser', () => { - it('should throw ForbiddenException on self-deletion attempt', async () => { - await expect(service.adminDeleteUser(1, 1)).rejects.toThrow(ForbiddenException); - }); - - it('should delete a different user', async () => { - prisma.user.findUnique.mockResolvedValue({ id: 2 }); - prisma.user.delete.mockResolvedValue({ id: 2 }); - await service.adminDeleteUser(2, 1); - expect(prisma.user.delete).toHaveBeenCalledWith({ where: { id: 2 } }); - }); - - it('should throw NotFoundException when target user does not exist', async () => { - prisma.user.findUnique.mockResolvedValue(null); - await expect(service.adminDeleteUser(99, 1)).rejects.toThrow(NotFoundException); - }); - }); - - describe('totpSetup', () => { - it('should encrypt TOTP secret before storage and return secret + qrCode', async () => { - prisma.user.findUnique.mockResolvedValue({ id: 1, email: 'test@test.com', totpEnabled: false }); - prisma.user.update.mockResolvedValue({}); - - const result = await service.totpSetup(1); - - expect(encryption.encrypt).toHaveBeenCalled(); - expect(prisma.user.update).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ totpSecret: 'v2:encrypted' }), - }), - ); - expect(result).toHaveProperty('secret'); - expect(result).toHaveProperty('qrCode'); - }); - - it('should throw BadRequestException if TOTP already enabled', async () => { - prisma.user.findUnique.mockResolvedValue({ id: 1, totpEnabled: true }); - await expect(service.totpSetup(1)).rejects.toThrow(BadRequestException); - }); - }); - - describe('updateProfile', () => { - it('should throw BadRequestException when changing password without providing current password', async () => { - prisma.user.findUnique.mockResolvedValue({ id: 1, passwordHash: 'hash' }); - await expect( - service.updateProfile(1, { newPassword: 'new-password' }), - ).rejects.toThrow(BadRequestException); - }); - }); - - describe('adminUpdateUser', () => { - it('should throw ForbiddenException when admin tries to remove their own admin role', async () => { - prisma.user.findUnique.mockResolvedValue({ id: 1, role: 'admin' }); - await expect(service.adminUpdateUser(1, 1, { role: 'user' })).rejects.toThrow(ForbiddenException); - }); - }); -}); diff --git a/backend/src/auth/auth.service.ts b/backend/src/auth/auth.service.ts deleted file mode 100644 index 8f5da7f..0000000 --- a/backend/src/auth/auth.service.ts +++ /dev/null @@ -1,299 +0,0 @@ -import { Injectable, UnauthorizedException, BadRequestException, NotFoundException, ForbiddenException, OnModuleInit, Logger } from '@nestjs/common'; -import { JwtService } from '@nestjs/jwt'; -import { PrismaService } from '../prisma/prisma.service'; -import { EncryptionService } from '../vault/encryption.service'; -import * as argon2 from 'argon2'; -import * as bcrypt from 'bcrypt'; // Keep for migration from bcrypt→argon2id -import * as speakeasy from 'speakeasy'; -import * as QRCode from 'qrcode'; - -// Argon2id parameters — OWASP recommended, matches vault encryption -const ARGON2_OPTIONS: argon2.Options & { raw?: false } = { - type: argon2.argon2id, - memoryCost: 65536, // 64 MiB - timeCost: 3, // 3 iterations - parallelism: 4, // 4 threads -}; - -@Injectable() -export class AuthService implements OnModuleInit { - private readonly logger = new Logger(AuthService.name); - private dummyHash = ''; - - constructor( - private prisma: PrismaService, - private jwt: JwtService, - private encryption: EncryptionService, - ) {} - - async onModuleInit() { - // Pre-compute a dummy hash for constant-time login on non-existent users (H-8) - this.dummyHash = await argon2.hash('dummy-timing-defense', ARGON2_OPTIONS); - this.logger.log('Argon2id password hashing initialized'); - } - - /** - * Hash a password with Argon2id (H-9) - */ - private async hashPassword(password: string): Promise { - return argon2.hash(password, ARGON2_OPTIONS); - } - - /** - * Verify a password against a stored hash. - * Handles both Argon2id (new) and bcrypt (legacy) hashes. - * Auto-upgrades bcrypt hashes to Argon2id on successful login (H-9). - */ - private async verifyPassword(password: string, hash: string, userId?: number): Promise { - if (hash.startsWith('$2b$') || hash.startsWith('$2a$')) { - // Legacy bcrypt hash — verify with bcrypt - const valid = await bcrypt.compare(password, hash); - if (valid && userId) { - // Auto-upgrade to Argon2id - const newHash = await this.hashPassword(password); - await this.prisma.user.update({ where: { id: userId }, data: { passwordHash: newHash } }); - this.logger.log(`User ${userId} password auto-upgraded from bcrypt to Argon2id`); - } - return valid; - } - // Argon2id hash - return argon2.verify(hash, password); - } - - /** - * Decrypt a TOTP secret from the database. - * Handles both encrypted (v2:...) and legacy plaintext secrets. - * Auto-migrates plaintext secrets to encrypted on read (H-3). - */ - private async getTotpSecret(user: { id: number; totpSecret: string | null }): Promise { - if (!user.totpSecret) return null; - if (user.totpSecret.startsWith('v2:')) { - return this.encryption.decrypt(user.totpSecret); - } - // Plaintext — auto-migrate to encrypted - const plaintext = user.totpSecret; - const encrypted = await this.encryption.encrypt(plaintext); - await this.prisma.user.update({ where: { id: user.id }, data: { totpSecret: encrypted } }); - this.logger.log(`User ${user.id} TOTP secret auto-encrypted`); - return plaintext; - } - - async login(email: string, password: string, totpCode?: string) { - const user = await this.prisma.user.findUnique({ where: { email } }); - - if (!user) { - // Constant-time: run Argon2id verify against dummy hash (H-8) - // This prevents timing attacks that reveal whether an email exists - await argon2.verify(this.dummyHash, password); - throw new UnauthorizedException('Invalid credentials'); - } - - const valid = await this.verifyPassword(password, user.passwordHash, user.id); - if (!valid) throw new UnauthorizedException('Invalid credentials'); - - // If TOTP is enabled, require a valid code - if (user.totpEnabled && user.totpSecret) { - if (!totpCode) { - // Signal frontend to show TOTP input - return { requires_totp: true }; - } - const secret = await this.getTotpSecret(user); - if (!secret) throw new UnauthorizedException('TOTP configuration error'); - const verified = speakeasy.totp.verify({ - secret, - encoding: 'base32', - token: totpCode, - window: 1, - }); - if (!verified) throw new UnauthorizedException('Invalid TOTP code'); - } - - const payload = { sub: user.id, email: user.email, role: user.role }; - return { - access_token: this.jwt.sign(payload), - user: { id: user.id, email: user.email, displayName: user.displayName, role: user.role }, - }; - } - - async getProfile(userId: number) { - const user = await this.prisma.user.findUnique({ where: { id: userId } }); - if (!user) throw new UnauthorizedException(); - return { - id: user.id, - email: user.email, - displayName: user.displayName, - role: user.role, - totpEnabled: user.totpEnabled, - }; - } - - async updateProfile(userId: number, data: { email?: string; displayName?: string; currentPassword?: string; newPassword?: string }) { - const user = await this.prisma.user.findUnique({ where: { id: userId } }); - if (!user) throw new UnauthorizedException(); - - const update: any = {}; - - if (data.email && data.email !== user.email) { - update.email = data.email; - } - if (data.displayName !== undefined) { - update.displayName = data.displayName; - } - if (data.newPassword) { - if (!data.currentPassword) { - throw new BadRequestException('Current password required to set new password'); - } - const valid = await this.verifyPassword(data.currentPassword, user.passwordHash); - if (!valid) throw new BadRequestException('Current password is incorrect'); - update.passwordHash = await this.hashPassword(data.newPassword); - } - - if (Object.keys(update).length === 0) { - return { message: 'No changes' }; - } - - await this.prisma.user.update({ where: { id: userId }, data: update }); - return { message: 'Profile updated' }; - } - - async totpSetup(userId: number) { - const user = await this.prisma.user.findUnique({ where: { id: userId } }); - if (!user) throw new UnauthorizedException(); - if (user.totpEnabled) throw new BadRequestException('TOTP already enabled'); - - const secret = speakeasy.generateSecret({ - name: `Wraith (${user.email})`, - issuer: 'Wraith', - }); - - // Encrypt TOTP secret before storage (H-3) - const encryptedSecret = await this.encryption.encrypt(secret.base32); - await this.prisma.user.update({ - where: { id: userId }, - data: { totpSecret: encryptedSecret }, - }); - - const qrCodeUrl = await QRCode.toDataURL(secret.otpauth_url!); - - return { - secret: secret.base32, - qrCode: qrCodeUrl, - }; - } - - async totpVerify(userId: number, code: string) { - const user = await this.prisma.user.findUnique({ where: { id: userId } }); - if (!user || !user.totpSecret) throw new BadRequestException('TOTP not set up'); - - const secret = await this.getTotpSecret(user); - if (!secret) throw new BadRequestException('TOTP configuration error'); - - const verified = speakeasy.totp.verify({ - secret, - encoding: 'base32', - token: code, - window: 1, - }); - - if (!verified) throw new BadRequestException('Invalid code — try again'); - - await this.prisma.user.update({ - where: { id: userId }, - data: { totpEnabled: true }, - }); - - return { message: 'TOTP enabled successfully' }; - } - - async totpDisable(userId: number, password: string) { - const user = await this.prisma.user.findUnique({ where: { id: userId } }); - if (!user) throw new UnauthorizedException(); - - const valid = await this.verifyPassword(password, user.passwordHash); - if (!valid) throw new BadRequestException('Password incorrect'); - - await this.prisma.user.update({ - where: { id: userId }, - data: { totpEnabled: false, totpSecret: null }, - }); - - return { message: 'TOTP disabled' }; - } - - // ─── Admin User Management ────────────────────────────────────────────── - - async listUsers() { - return this.prisma.user.findMany({ - select: { id: true, email: true, displayName: true, role: true, totpEnabled: true, createdAt: true }, - orderBy: { createdAt: 'asc' }, - }); - } - - async createUser(data: { email: string; password: string; displayName?: string; role?: string }) { - const existing = await this.prisma.user.findUnique({ where: { email: data.email } }); - if (existing) throw new BadRequestException('Email already in use'); - - const hash = await this.hashPassword(data.password); - const user = await this.prisma.user.create({ - data: { - email: data.email, - passwordHash: hash, - displayName: data.displayName || null, - role: data.role || 'user', - }, - }); - return { id: user.id, email: user.email, displayName: user.displayName, role: user.role }; - } - - async adminUpdateUser(targetId: number, adminId: number, data: { email?: string; displayName?: string; role?: string }) { - const target = await this.prisma.user.findUnique({ where: { id: targetId } }); - if (!target) throw new NotFoundException('User not found'); - - // Prevent removing your own admin role - if (targetId === adminId && data.role && data.role !== 'admin') { - throw new ForbiddenException('Cannot remove your own admin role'); - } - - return this.prisma.user.update({ - where: { id: targetId }, - data: { - email: data.email, - displayName: data.displayName, - role: data.role, - }, - select: { id: true, email: true, displayName: true, role: true }, - }); - } - - async adminResetPassword(targetId: number, newPassword: string) { - const target = await this.prisma.user.findUnique({ where: { id: targetId } }); - if (!target) throw new NotFoundException('User not found'); - - const hash = await this.hashPassword(newPassword); - await this.prisma.user.update({ where: { id: targetId }, data: { passwordHash: hash } }); - return { message: 'Password reset' }; - } - - async adminResetTotp(targetId: number) { - const target = await this.prisma.user.findUnique({ where: { id: targetId } }); - if (!target) throw new NotFoundException('User not found'); - - await this.prisma.user.update({ - where: { id: targetId }, - data: { totpEnabled: false, totpSecret: null }, - }); - return { message: 'TOTP reset' }; - } - - async adminDeleteUser(targetId: number, adminId: number) { - if (targetId === adminId) { - throw new ForbiddenException('Cannot delete your own account'); - } - const target = await this.prisma.user.findUnique({ where: { id: targetId } }); - if (!target) throw new NotFoundException('User not found'); - - // CASCADE will delete all their hosts, credentials, keys, logs - await this.prisma.user.delete({ where: { id: targetId } }); - return { message: 'User deleted' }; - } -} diff --git a/backend/src/auth/dto/login.dto.ts b/backend/src/auth/dto/login.dto.ts deleted file mode 100644 index 9f0a7c2..0000000 --- a/backend/src/auth/dto/login.dto.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { IsEmail, IsString, MinLength, IsOptional } from 'class-validator'; - -export class LoginDto { - @IsEmail() - email: string; - - @IsString() - @MinLength(1) - password: string; - - @IsOptional() - @IsString() - totpCode?: string; -} diff --git a/backend/src/auth/dto/update-profile.dto.ts b/backend/src/auth/dto/update-profile.dto.ts deleted file mode 100644 index 11f1931..0000000 --- a/backend/src/auth/dto/update-profile.dto.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { IsEmail, IsString, IsOptional, MinLength } from 'class-validator'; - -export class UpdateProfileDto { - @IsOptional() - @IsEmail() - email?: string; - - @IsOptional() - @IsString() - displayName?: string; - - @IsOptional() - @IsString() - currentPassword?: string; - - @IsOptional() - @IsString() - @MinLength(6) - newPassword?: string; -} diff --git a/backend/src/auth/jwt-auth.guard.spec.ts b/backend/src/auth/jwt-auth.guard.spec.ts deleted file mode 100644 index 0940c5a..0000000 --- a/backend/src/auth/jwt-auth.guard.spec.ts +++ /dev/null @@ -1,13 +0,0 @@ -// backend/src/auth/jwt-auth.guard.spec.ts -import { JwtAuthGuard } from './jwt-auth.guard'; - -describe('JwtAuthGuard', () => { - it('should be defined', () => { - expect(new JwtAuthGuard()).toBeDefined(); - }); - - it('should be an instance of JwtAuthGuard', () => { - const guard = new JwtAuthGuard(); - expect(guard).toBeInstanceOf(JwtAuthGuard); - }); -}); diff --git a/backend/src/auth/jwt-auth.guard.ts b/backend/src/auth/jwt-auth.guard.ts deleted file mode 100644 index 2155290..0000000 --- a/backend/src/auth/jwt-auth.guard.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { AuthGuard } from '@nestjs/passport'; - -@Injectable() -export class JwtAuthGuard extends AuthGuard('jwt') {} diff --git a/backend/src/auth/jwt.strategy.ts b/backend/src/auth/jwt.strategy.ts deleted file mode 100644 index 84eb489..0000000 --- a/backend/src/auth/jwt.strategy.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PassportStrategy } from '@nestjs/passport'; -import { ExtractJwt, Strategy } from 'passport-jwt'; - -@Injectable() -export class JwtStrategy extends PassportStrategy(Strategy) { - constructor() { - super({ - jwtFromRequest: (req: any) => { - // Cookie-based auth (C-2) — preferred in production - if (req?.cookies?.wraith_token) return req.cookies.wraith_token; - // Fallback: Authorization header (for migration / API clients) - return ExtractJwt.fromAuthHeaderAsBearerToken()(req); - }, - ignoreExpiration: false, - secretOrKey: process.env.JWT_SECRET, - }); - } - - validate(payload: { sub: number; email: string; role: string }) { - return { sub: payload.sub, email: payload.email, role: payload.role }; - } -} diff --git a/backend/src/auth/ws-auth.guard.spec.ts b/backend/src/auth/ws-auth.guard.spec.ts deleted file mode 100644 index d78ebf8..0000000 --- a/backend/src/auth/ws-auth.guard.spec.ts +++ /dev/null @@ -1,56 +0,0 @@ -// backend/src/auth/ws-auth.guard.spec.ts -import { WsAuthGuard } from './ws-auth.guard'; -import { AuthController } from './auth.controller'; - -describe('WsAuthGuard', () => { - let guard: WsAuthGuard; - let jwt: any; - - beforeEach(() => { - jwt = { - verify: jest.fn().mockReturnValue({ sub: 1, email: 'test@test.com' }), - }; - guard = new WsAuthGuard(jwt as any); - }); - - it('should authenticate via httpOnly cookie', () => { - const req = { headers: { cookie: 'wraith_token=valid-jwt; other=stuff' }, url: '/ws' }; - const result = guard.validateClient({}, req); - expect(jwt.verify).toHaveBeenCalledWith('valid-jwt'); - expect(result).toEqual({ sub: 1, email: 'test@test.com' }); - }); - - it('should authenticate via single-use WS ticket', () => { - const originalConsume = AuthController.consumeWsTicket; - AuthController.consumeWsTicket = jest.fn().mockReturnValue({ - sub: 42, - email: 'ticket@test.com', - role: 'user', - }); - const req = { url: '/api/ws/terminal?ticket=abc123', headers: {} }; - const result = guard.validateClient({}, req); - expect(AuthController.consumeWsTicket).toHaveBeenCalledWith('abc123'); - expect(result).toEqual({ sub: 42, email: 'ticket@test.com' }); - // Restore - AuthController.consumeWsTicket = originalConsume; - }); - - it('should fall back to legacy URL token when no cookie or ticket', () => { - const req = { url: '/api/ws/terminal?token=legacy-jwt', headers: {} }; - guard.validateClient({}, req); - expect(jwt.verify).toHaveBeenCalledWith('legacy-jwt'); - }); - - it('should return null when no credentials are present', () => { - const req = { url: '/api/ws/terminal', headers: {} }; - const result = guard.validateClient({}, req); - expect(result).toBeNull(); - }); - - it('should return null when JWT verification fails', () => { - jwt.verify.mockImplementation(() => { throw new Error('invalid signature'); }); - const req = { headers: { cookie: 'wraith_token=bad-jwt' }, url: '/ws' }; - const result = guard.validateClient({}, req); - expect(result).toBeNull(); - }); -}); diff --git a/backend/src/auth/ws-auth.guard.ts b/backend/src/auth/ws-auth.guard.ts deleted file mode 100644 index 5c67501..0000000 --- a/backend/src/auth/ws-auth.guard.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { JwtService } from '@nestjs/jwt'; -import { WsException } from '@nestjs/websockets'; -import { AuthController } from './auth.controller'; - -@Injectable() -export class WsAuthGuard { - constructor(private jwt: JwtService) {} - - validateClient(client: any, req?: any): { sub: number; email: string } | null { - try { - let token: string | undefined; - - // 1. Cookie-based auth (C-2) — preferred, sent automatically on WS upgrade - const cookieHeader = req?.headers?.cookie; - if (cookieHeader) { - const match = cookieHeader.match(/wraith_token=([^;]+)/); - if (match) token = match[1]; - } - - // 2. Single-use WS ticket (C-3) — short-lived nonce from POST /api/auth/ws-ticket - if (!token) { - const rawUrl = req?.url || client.url || client._url; - const url = new URL(rawUrl, 'http://localhost'); - const ticket = url.searchParams.get('ticket'); - if (ticket) { - const user = AuthController.consumeWsTicket(ticket); - if (user) return { sub: user.sub, email: user.email }; - } - } - - // 3. Legacy: JWT in URL query (kept for backwards compat during migration) - if (!token) { - const rawUrl = req?.url || client.url || client._url; - const url = new URL(rawUrl, 'http://localhost'); - const urlToken = url.searchParams.get('token'); - if (urlToken) token = urlToken; - } - - if (!token) throw new WsException('No token'); - return this.jwt.verify(token) as { sub: number; email: string }; - } catch { - return null; - } - } -} diff --git a/backend/src/connections/connections.module.ts b/backend/src/connections/connections.module.ts deleted file mode 100644 index aa04451..0000000 --- a/backend/src/connections/connections.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Module } from '@nestjs/common'; -import { HostsService } from './hosts.service'; -import { HostsController } from './hosts.controller'; -import { GroupsService } from './groups.service'; -import { GroupsController } from './groups.controller'; - -@Module({ - providers: [HostsService, GroupsService], - controllers: [HostsController, GroupsController], - exports: [HostsService], -}) -export class ConnectionsModule {} diff --git a/backend/src/connections/dto/create-group.dto.ts b/backend/src/connections/dto/create-group.dto.ts deleted file mode 100644 index 90deb2a..0000000 --- a/backend/src/connections/dto/create-group.dto.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { IsString, IsInt, IsOptional } from 'class-validator'; - -export class CreateGroupDto { - @IsString() - name: string; - - @IsInt() - @IsOptional() - parentId?: number; - - @IsInt() - @IsOptional() - sortOrder?: number; -} diff --git a/backend/src/connections/dto/create-host.dto.ts b/backend/src/connections/dto/create-host.dto.ts deleted file mode 100644 index e23b5d1..0000000 --- a/backend/src/connections/dto/create-host.dto.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { IsString, IsInt, IsOptional, IsEnum, IsArray, Min, Max } from 'class-validator'; -import { Protocol } from '@prisma/client'; - -export class CreateHostDto { - @IsString() - name: string; - - @IsString() - hostname: string; - - @IsInt() - @Min(1) - @Max(65535) - @IsOptional() - port?: number; - - @IsEnum(Protocol) - @IsOptional() - protocol?: Protocol; - - @IsInt() - @IsOptional() - groupId?: number; - - @IsInt() - @IsOptional() - credentialId?: number; - - @IsArray() - @IsString({ each: true }) - @IsOptional() - tags?: string[]; - - @IsString() - @IsOptional() - notes?: string; - - @IsString() - @IsOptional() - color?: string; -} diff --git a/backend/src/connections/dto/update-group.dto.ts b/backend/src/connections/dto/update-group.dto.ts deleted file mode 100644 index ddaa1b0..0000000 --- a/backend/src/connections/dto/update-group.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PartialType } from '@nestjs/mapped-types'; -import { CreateGroupDto } from './create-group.dto'; - -export class UpdateGroupDto extends PartialType(CreateGroupDto) {} diff --git a/backend/src/connections/dto/update-host.dto.ts b/backend/src/connections/dto/update-host.dto.ts deleted file mode 100644 index ce1e597..0000000 --- a/backend/src/connections/dto/update-host.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PartialType } from '@nestjs/mapped-types'; -import { CreateHostDto } from './create-host.dto'; - -export class UpdateHostDto extends PartialType(CreateHostDto) {} diff --git a/backend/src/connections/groups.controller.ts b/backend/src/connections/groups.controller.ts deleted file mode 100644 index 0554cf5..0000000 --- a/backend/src/connections/groups.controller.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Controller, Get, Post, Put, Delete, Param, Body, Request, UseGuards, ParseIntPipe } from '@nestjs/common'; -import { JwtAuthGuard } from '../auth/jwt-auth.guard'; -import { GroupsService } from './groups.service'; -import { CreateGroupDto } from './dto/create-group.dto'; -import { UpdateGroupDto } from './dto/update-group.dto'; - -@UseGuards(JwtAuthGuard) -@Controller('groups') -export class GroupsController { - constructor(private groups: GroupsService) {} - - @Get() - findAll(@Request() req: any) { - return this.groups.findAll(req.user.sub); - } - - @Get('tree') - findTree(@Request() req: any) { - return this.groups.findTree(req.user.sub); - } - - @Get(':id') - findOne(@Request() req: any, @Param('id', ParseIntPipe) id: number) { - return this.groups.findOne(id, req.user.sub); - } - - @Post() - create(@Request() req: any, @Body() dto: CreateGroupDto) { - return this.groups.create(req.user.sub, dto); - } - - @Put(':id') - update(@Request() req: any, @Param('id', ParseIntPipe) id: number, @Body() dto: UpdateGroupDto) { - return this.groups.update(id, req.user.sub, dto); - } - - @Delete(':id') - remove(@Request() req: any, @Param('id', ParseIntPipe) id: number) { - return this.groups.remove(id, req.user.sub); - } -} diff --git a/backend/src/connections/groups.service.ts b/backend/src/connections/groups.service.ts deleted file mode 100644 index 4ae0ceb..0000000 --- a/backend/src/connections/groups.service.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { Injectable, NotFoundException, ForbiddenException } from '@nestjs/common'; -import { PrismaService } from '../prisma/prisma.service'; -import { CreateGroupDto } from './dto/create-group.dto'; -import { UpdateGroupDto } from './dto/update-group.dto'; - -@Injectable() -export class GroupsService { - constructor(private prisma: PrismaService) {} - - findAll(userId: number) { - return this.prisma.hostGroup.findMany({ - where: { userId }, - include: { children: true, hosts: { select: { id: true, name: true, protocol: true } } }, - orderBy: { sortOrder: 'asc' }, - }); - } - - findTree(userId: number) { - return this.prisma.hostGroup.findMany({ - where: { parentId: null, userId }, - include: { - hosts: { orderBy: { sortOrder: 'asc' } }, - children: { - include: { - hosts: { orderBy: { sortOrder: 'asc' } }, - children: { - include: { - hosts: { orderBy: { sortOrder: 'asc' } }, - }, - }, - }, - orderBy: { sortOrder: 'asc' }, - }, - }, - orderBy: { sortOrder: 'asc' }, - }); - } - - async findOne(id: number, userId: number) { - const group = await this.prisma.hostGroup.findUnique({ - where: { id }, - include: { hosts: true, children: true }, - }); - if (!group) throw new NotFoundException(`Group ${id} not found`); - if (group.userId !== userId) throw new ForbiddenException('Access denied'); - return group; - } - - create(userId: number, dto: CreateGroupDto) { - return this.prisma.hostGroup.create({ data: { ...dto, userId } }); - } - - async update(id: number, userId: number, dto: UpdateGroupDto) { - await this.findOne(id, userId); - return this.prisma.hostGroup.update({ where: { id }, data: dto }); - } - - async remove(id: number, userId: number) { - await this.findOne(id, userId); - return this.prisma.hostGroup.delete({ where: { id } }); - } -} diff --git a/backend/src/connections/hosts.controller.ts b/backend/src/connections/hosts.controller.ts deleted file mode 100644 index acb9c71..0000000 --- a/backend/src/connections/hosts.controller.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Controller, Get, Post, Put, Delete, Param, Body, Query, Request, UseGuards, ParseIntPipe } from '@nestjs/common'; -import { JwtAuthGuard } from '../auth/jwt-auth.guard'; -import { HostsService } from './hosts.service'; -import { CreateHostDto } from './dto/create-host.dto'; -import { UpdateHostDto } from './dto/update-host.dto'; - -@UseGuards(JwtAuthGuard) -@Controller('hosts') -export class HostsController { - constructor(private hosts: HostsService) {} - - @Get() - findAll(@Request() req: any, @Query('search') search?: string) { - return this.hosts.findAll(req.user.sub, search); - } - - @Get(':id') - findOne(@Request() req: any, @Param('id', ParseIntPipe) id: number) { - return this.hosts.findOne(id, req.user.sub); - } - - @Post() - create(@Request() req: any, @Body() dto: CreateHostDto) { - return this.hosts.create(req.user.sub, dto); - } - - @Put(':id') - update(@Request() req: any, @Param('id', ParseIntPipe) id: number, @Body() dto: UpdateHostDto) { - return this.hosts.update(id, req.user.sub, dto); - } - - @Delete(':id') - remove(@Request() req: any, @Param('id', ParseIntPipe) id: number) { - return this.hosts.remove(id, req.user.sub); - } - - @Post('reorder') - reorder(@Request() req: any, @Body() body: { ids: number[] }) { - return this.hosts.reorder(req.user.sub, body.ids); - } -} diff --git a/backend/src/connections/hosts.service.ts b/backend/src/connections/hosts.service.ts deleted file mode 100644 index b49d4ce..0000000 --- a/backend/src/connections/hosts.service.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { Injectable, NotFoundException, ForbiddenException } from '@nestjs/common'; -import { PrismaService } from '../prisma/prisma.service'; -import { CreateHostDto } from './dto/create-host.dto'; -import { UpdateHostDto } from './dto/update-host.dto'; - -@Injectable() -export class HostsService { - constructor(private prisma: PrismaService) {} - - findAll(userId: number, search?: string) { - const where: any = { userId }; - if (search) { - where.OR = [ - { name: { contains: search, mode: 'insensitive' as const } }, - { hostname: { contains: search, mode: 'insensitive' as const } }, - { tags: { has: search } }, - ]; - } - return this.prisma.host.findMany({ - where, - include: { group: true, credential: { select: { id: true, name: true, type: true } } }, - orderBy: [{ lastConnectedAt: { sort: 'desc', nulls: 'last' } }, { sortOrder: 'asc' }], - }); - } - - async findOne(id: number, userId?: number) { - const host = await this.prisma.host.findUnique({ - where: { id }, - include: { group: true, credential: true }, - }); - if (!host) throw new NotFoundException(`Host ${id} not found`); - if (userId !== undefined && host.userId !== userId) { - throw new ForbiddenException('Access denied'); - } - return host; - } - - create(userId: number, dto: CreateHostDto) { - return this.prisma.host.create({ - data: { - name: dto.name, - hostname: dto.hostname, - port: dto.port ?? (dto.protocol === 'rdp' ? 3389 : 22), - protocol: dto.protocol ?? 'ssh', - groupId: dto.groupId, - credentialId: dto.credentialId, - userId, - tags: dto.tags ?? [], - notes: dto.notes, - color: dto.color, - }, - include: { group: true }, - }); - } - - async update(id: number, userId: number, dto: UpdateHostDto) { - await this.findOne(id, userId); - return this.prisma.host.update({ where: { id }, data: dto }); - } - - async remove(id: number, userId: number) { - await this.findOne(id, userId); - return this.prisma.host.delete({ where: { id } }); - } - - async touchLastConnected(id: number) { - return this.prisma.host.update({ - where: { id }, - data: { lastConnectedAt: new Date() }, - }); - } - - async reorder(userId: number, ids: number[]) { - // Verify all hosts belong to user - const hosts = await this.prisma.host.findMany({ where: { id: { in: ids }, userId } }); - if (hosts.length !== ids.length) throw new ForbiddenException('Access denied'); - - const updates = ids.map((id, index) => - this.prisma.host.update({ where: { id }, data: { sortOrder: index } }), - ); - return this.prisma.$transaction(updates); - } -} diff --git a/backend/src/main.ts b/backend/src/main.ts deleted file mode 100644 index 00b6fb4..0000000 --- a/backend/src/main.ts +++ /dev/null @@ -1,119 +0,0 @@ -import helmet from 'helmet'; -import * as cookieParser from 'cookie-parser'; -import { NestFactory } from '@nestjs/core'; -import { ValidationPipe } from '@nestjs/common'; -import { WsAdapter } from '@nestjs/platform-ws'; -import { AppModule } from './app.module'; -import { WebSocketServer } from 'ws'; -import { TerminalGateway } from './terminal/terminal.gateway'; -import { SftpGateway } from './terminal/sftp.gateway'; -import { RdpGateway } from './rdp/rdp.gateway'; - -// Crash handlers — catch whatever is killing the process -process.on('uncaughtException', (err) => { - console.error(`[FATAL] Uncaught Exception: ${err.message}\n${err.stack}`); -}); - -process.on('unhandledRejection', (reason: any) => { - console.error(`[FATAL] Unhandled Rejection: ${reason?.message || reason}\n${reason?.stack || ''}`); -}); - -async function bootstrap() { - const app = await NestFactory.create(AppModule); - app.use(cookieParser()); - app.use(helmet({ - contentSecurityPolicy: { - directives: { - defaultSrc: ["'self'"], - scriptSrc: ["'self'", "'unsafe-inline'"], - styleSrc: ["'self'", "'unsafe-inline'"], - imgSrc: ["'self'", "data:", "blob:"], - connectSrc: ["'self'", "ws:", "wss:"], - fontSrc: ["'self'", "data:"], - }, - }, - })); - app.setGlobalPrefix('api'); - app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true })); - app.useWebSocketAdapter(new WsAdapter(app)); - app.enableCors({ - origin: process.env.NODE_ENV === 'production' ? false : 'http://localhost:3001', - credentials: true, - }); - await app.listen(3000); - console.log('Wraith backend running on port 3000'); - - const server = app.getHttpServer(); - const terminalGateway = app.get(TerminalGateway); - const sftpGateway = app.get(SftpGateway); - const rdpGateway = app.get(RdpGateway); - - const terminalWss = new WebSocketServer({ noServer: true }); - const sftpWss = new WebSocketServer({ noServer: true }); - const rdpWss = new WebSocketServer({ noServer: true }); - - terminalWss.on('connection', (ws, req) => { - try { - console.log(`[WS] Terminal connection established`); - terminalGateway.handleConnection(ws, req); - } catch (err: any) { - console.error(`[FATAL] Terminal handleConnection crashed: ${err.message}\n${err.stack}`); - } - }); - - sftpWss.on('connection', (ws, req) => { - try { - console.log(`[WS] SFTP connection established`); - sftpGateway.handleConnection(ws, req); - } catch (err: any) { - console.error(`[FATAL] SFTP handleConnection crashed: ${err.message}\n${err.stack}`); - } - }); - - rdpWss.on('connection', (ws, req) => { - try { - console.log(`[WS] RDP connection established`); - rdpGateway.handleConnection(ws, req); - } catch (err: any) { - console.error(`[FATAL] RDP handleConnection crashed: ${err.message}\n${err.stack}`); - } - }); - - // Remove ALL existing upgrade listeners (WsAdapter's) so we handle upgrades first - const existingListeners = server.listeners('upgrade'); - server.removeAllListeners('upgrade'); - - // Our handler runs first — routes terminal/sftp, passes everything else to WsAdapter - server.on('upgrade', (req: any, socket: any, head: any) => { - try { - const pathname = req.url?.split('?')[0]; - console.log(`[HTTP-UPGRADE] path=${pathname} socket.destroyed=${socket.destroyed}`); - - if (pathname === '/api/ws/terminal') { - terminalWss.handleUpgrade(req, socket, head, (ws) => { - console.log(`[WS] Terminal upgrade complete`); - terminalWss.emit('connection', ws, req); - }); - } else if (pathname === '/api/ws/sftp') { - sftpWss.handleUpgrade(req, socket, head, (ws) => { - console.log(`[WS] SFTP upgrade complete`); - sftpWss.emit('connection', ws, req); - }); - } else if (pathname === '/api/ws/rdp') { - rdpWss.handleUpgrade(req, socket, head, (ws) => { - console.log(`[WS] RDP upgrade complete`); - rdpWss.emit('connection', ws, req); - }); - } else { - // Pass to WsAdapter's original handlers - for (const listener of existingListeners) { - listener.call(server, req, socket, head); - } - } - } catch (err: any) { - console.error(`[FATAL] Upgrade handler crashed: ${err.message}\n${err.stack}`); - socket.destroy(); - } - }); -} -bootstrap(); diff --git a/backend/src/prisma/prisma.module.ts b/backend/src/prisma/prisma.module.ts deleted file mode 100644 index 7207426..0000000 --- a/backend/src/prisma/prisma.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Global, Module } from '@nestjs/common'; -import { PrismaService } from './prisma.service'; - -@Global() -@Module({ - providers: [PrismaService], - exports: [PrismaService], -}) -export class PrismaModule {} diff --git a/backend/src/prisma/prisma.service.ts b/backend/src/prisma/prisma.service.ts deleted file mode 100644 index bb6565f..0000000 --- a/backend/src/prisma/prisma.service.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common'; -import { PrismaClient } from '@prisma/client'; - -@Injectable() -export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy { - async onModuleInit() { - await this.$connect(); - } - - async onModuleDestroy() { - await this.$disconnect(); - } -} diff --git a/backend/src/rdp/guacamole.service.ts b/backend/src/rdp/guacamole.service.ts deleted file mode 100644 index 7cbcba8..0000000 --- a/backend/src/rdp/guacamole.service.ts +++ /dev/null @@ -1,275 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import * as net from 'net'; - -/** - * Guacamole wire protocol: instructions are comma-separated length-prefixed fields - * terminated by semicolons. - * Example: "4.size,4.1024,3.768;" - * Format per field: "." - */ - -@Injectable() -export class GuacamoleService { - private readonly logger = new Logger(GuacamoleService.name); - private readonly host = process.env.GUACD_HOST || 'guacd'; - private readonly port = parseInt(process.env.GUACD_PORT || '4822', 10); - - /** - * Opens a raw TCP connection to guacd, completes the SELECT → CONNECT handshake, - * and returns the live socket ready for bidirectional Guacamole instruction traffic. - */ - async connect(params: { - hostname: string; - port: number; - username: string; - password: string; - domain?: string; - width: number; - height: number; - dpi?: number; - security?: string; - colorDepth?: number; - ignoreCert?: boolean; - }): Promise { - return new Promise((resolve, reject) => { - const socket = net.createConnection(this.port, this.host, () => { - this.logger.log(`Connected to guacd at ${this.host}:${this.port}`); - - // Phase 1: SELECT rdp — tells guacd which protocol to prepare - socket.write(this.encode('select', 'rdp')); - - let buffer = ''; - - const onHandshake = (data: Buffer) => { - buffer += data.toString('utf-8'); - - // guacd responds with "args" listing the parameters it expects. - // Wait until we receive at least one complete instruction (ends with ';'). - const semicolonIdx = buffer.indexOf(';'); - if (semicolonIdx === -1) return; - - // We've received the args instruction — remove handshake listener - socket.removeListener('data', onHandshake); - - // Clear the connect timeout — handshake completed - socket.setTimeout(0); - - // Parse the args instruction to get expected parameter names - const argsInstruction = buffer.substring(0, semicolonIdx + 1); - const argNames = this.decode(argsInstruction); - - // First element is the opcode ("args"), rest are parameter names - if (argNames[0] === 'args') { - argNames.shift(); - } - this.logger.log(`guacd expects ${argNames.length} args: ${argNames.join(', ')}`); - - // Phase 2: Client capability instructions — MUST be sent before CONNECT. - // The Guacamole protocol requires: size, audio, video, image, timezone - // between receiving 'args' and sending 'connect'. Without these, guacd - // sees 0x0 resolution and immediately kills the connection. - const width = String(params.width); - const height = String(params.height); - const dpi = String(params.dpi || 96); - socket.write(this.encode('size', width, height, dpi)); - socket.write(this.encode('audio')); - socket.write(this.encode('video')); - socket.write(this.encode('image', 'image/png', 'image/jpeg', 'image/webp')); - - let tz: string; - try { - tz = Intl.DateTimeFormat().resolvedOptions().timeZone; - } catch { - tz = 'UTC'; - } - socket.write(this.encode('timezone', tz)); - - // Phase 3: CONNECT — send values in the exact order guacd expects - const connectInstruction = this.buildConnectInstruction(params, argNames); - this.logger.log( - `Sending CONNECT: host=${params.hostname}:${params.port} user=${params.username} domain=${params.domain || '(none)'} ` + - `security=${params.security || 'any'} size=${width}x${height}@${dpi}dpi ignoreCert=${params.ignoreCert !== false}`, - ); - socket.write(connectInstruction); - - resolve(socket); - }; - - socket.on('data', onHandshake); - }); - - socket.on('error', (err) => { - this.logger.error(`guacd connection error: ${err.message}`); - reject(err); - }); - - // 10-second timeout for the SELECT → args handshake - socket.setTimeout(10000, () => { - socket.destroy(); - reject(new Error(`guacd handshake timeout connecting to ${this.host}:${this.port}`)); - }); - }); - } - - private buildConnectInstruction( - params: { - hostname: string; - port: number; - username: string; - password: string; - domain?: string; - width: number; - height: number; - dpi?: number; - security?: string; - colorDepth?: number; - ignoreCert?: boolean; - }, - argNames: string[], - ): string { - // Map our params to guacd's expected arg names - const paramMap: Record = { - 'hostname': params.hostname, - 'port': String(params.port), - 'username': params.username, - 'password': params.password, - 'domain': params.domain || '', - 'width': String(params.width), - 'height': String(params.height), - 'dpi': String(params.dpi || 96), - 'security': params.security || 'any', - 'color-depth': String(params.colorDepth || 24), - 'ignore-cert': params.ignoreCert !== false ? 'true' : 'false', - 'disable-audio': 'false', - 'enable-wallpaper': 'false', - 'enable-theming': 'true', - 'enable-font-smoothing': 'true', - 'resize-method': 'reconnect', - 'server-layout': '', - 'timezone': '', - 'console': '', - 'initial-program': '', - 'client-name': 'Wraith', - 'enable-full-window-drag': 'false', - 'enable-desktop-composition': 'false', - 'enable-menu-animations': 'false', - 'disable-bitmap-caching': 'false', - 'disable-offscreen-caching': 'false', - 'disable-glyph-caching': 'false', - 'preconnection-id': '', - 'preconnection-blob': '', - 'enable-sftp': 'false', - 'sftp-hostname': '', - 'sftp-port': '', - 'sftp-username': '', - 'sftp-password': '', - 'sftp-private-key': '', - 'sftp-passphrase': '', - 'sftp-directory': '', - 'sftp-root-directory': '', - 'sftp-server-alive-interval': '', - 'recording-path': '', - 'recording-name': '', - 'recording-exclude-output': '', - 'recording-exclude-mouse': '', - 'recording-include-keys': '', - 'create-recording-path': '', - 'remote-app': '', - 'remote-app-dir': '', - 'remote-app-args': '', - 'gateway-hostname': '', - 'gateway-port': '', - 'gateway-domain': '', - 'gateway-username': '', - 'gateway-password': '', - 'load-balance-info': '', - 'normalize-clipboard': '', - 'force-lossless': '', - 'wol-send-packet': '', - 'wol-mac-addr': '', - 'wol-broadcast-addr': '', - 'wol-udp-port': '', - 'wol-wait-time': '', - }; - - // Build values array matching the exact order guacd expects - // VERSION_X_Y_Z args must be echoed back as-is - const values = argNames.map((name) => { - if (name.startsWith('VERSION_')) return name; - return paramMap[name] ?? ''; - }); - return this.encode('connect', ...values); - } - - // Opcodes that clients are allowed to send to guacd. - // Server-originating opcodes (img, blob, end, ack, etc.) are intentionally excluded. - private static ALLOWED_CLIENT_OPCODES = new Set([ - 'key', 'mouse', 'size', 'clipboard', 'sync', 'disconnect', 'nop', - ]); - - /** - * Validate a raw Guacamole instruction received from a browser client. - * - * Checks: - * 1. The instruction matches the Guacamole wire format: .[,...] - * 2. The declared length matches the actual opcode length (prevents injection - * via length field manipulation). - * 3. The opcode is in the client-allowed allowlist. - * - * Returns true if the instruction is safe to forward to guacd. - */ - validateClientInstruction(instruction: string): boolean { - try { - // Must begin with a length-prefixed opcode: "." - const match = instruction.match(/^(\d+)\.([\w-]+)/); - if (!match) return false; - const declaredLen = parseInt(match[1], 10); - const opcode = match[2]; - // The declared length must equal the actual opcode length to prevent - // smuggling a different opcode via a mismatched length prefix. - if (declaredLen !== opcode.length) return false; - return GuacamoleService.ALLOWED_CLIENT_OPCODES.has(opcode); - } catch { - return false; - } - } - - /** - * Encode a Guacamole instruction. - * Each part is length-prefixed: "." - * Parts are comma-separated, instruction ends with ';' - * Example: encode('size', '1024', '768') → "4.size,4.1024,3.768;" - */ - encode(...parts: string[]): string { - return parts.map((p) => `${p.length}.${p}`).join(',') + ';'; - } - - /** - * Decode a Guacamole instruction string back to a string array. - */ - decode(instruction: string): string[] { - const parts: string[] = []; - let pos = 0; - - // Strip trailing semicolon if present - const clean = instruction.endsWith(';') - ? instruction.slice(0, -1) - : instruction; - - while (pos < clean.length) { - const dotIndex = clean.indexOf('.', pos); - if (dotIndex === -1) break; - - const len = parseInt(clean.substring(pos, dotIndex), 10); - if (isNaN(len)) break; - - const value = clean.substring(dotIndex + 1, dotIndex + 1 + len); - parts.push(value); - - // Move past: value + separator (comma or end) - pos = dotIndex + 1 + len + 1; - } - - return parts; - } -} diff --git a/backend/src/rdp/rdp.gateway.ts b/backend/src/rdp/rdp.gateway.ts deleted file mode 100644 index ac27947..0000000 --- a/backend/src/rdp/rdp.gateway.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import * as net from 'net'; -import { WsAuthGuard } from '../auth/ws-auth.guard'; -import { GuacamoleService } from './guacamole.service'; -import { CredentialsService } from '../vault/credentials.service'; -import { HostsService } from '../connections/hosts.service'; -import { PrismaService } from '../prisma/prisma.service'; - -@Injectable() -export class RdpGateway { - private readonly logger = new Logger(RdpGateway.name); - - // Maps browser WebSocket client → live guacd TCP socket - private clientSockets = new Map(); - private clientUsers = new Map(); - - constructor( - private guacamole: GuacamoleService, - private credentials: CredentialsService, - private hosts: HostsService, - private prisma: PrismaService, - private wsAuth: WsAuthGuard, - ) {} - - handleConnection(client: any, req: any) { - const user = this.wsAuth.validateClient(client, req); - if (!user) { - client.close(4001, 'Unauthorized'); - return; - } - - this.clientUsers.set(client, user); - this.logger.log(`RDP WS connected: ${user.email}`); - - client.on('message', async (raw: Buffer) => { - try { - const msg = JSON.parse(raw.toString()); - this.logger.log(`[RDP] Message: ${msg.type}`); - - if (msg.type === 'connect') { - await this.handleConnect(client, msg); - } else if (msg.type === 'guac') { - // Validate before forwarding raw Guacamole instruction to guacd TCP socket - if (typeof msg.instruction !== 'string') { - this.send(client, { type: 'error', message: 'Invalid instruction' }); - return; - } - if (msg.instruction.length > 65536) { - this.send(client, { type: 'error', message: 'Instruction too large' }); - return; - } - if (!this.guacamole.validateClientInstruction(msg.instruction)) { - this.logger.warn(`[RDP] Blocked invalid Guacamole instruction: ${msg.instruction.substring(0, 80)}`); - this.send(client, { type: 'error', message: 'Invalid instruction' }); - return; - } - const socket = this.clientSockets.get(client); - if (socket && !socket.destroyed) { - socket.write(msg.instruction); - } - } - } catch (err: any) { - this.logger.error(`RDP message error: ${err.message}`); - this.send(client, { type: 'error', message: err.message }); - } - }); - - client.on('close', () => { - this.logger.log('RDP WS disconnected'); - const socket = this.clientSockets.get(client); - if (socket) { - socket.destroy(); - this.clientSockets.delete(client); - this.logger.log('guacd socket destroyed on WS close'); - } - this.clientUsers.delete(client); - }); - } - - private async handleConnect(client: any, msg: any) { - const host = await this.hosts.findOne(msg.hostId); - - // Decrypt credentials if attached to host - const cred = host.credentialId - ? await this.credentials.decryptForConnection(host.credentialId) - : null; - - this.logger.log( - `Opening RDP tunnel: ${host.hostname}:${host.port} for host "${host.name}" (user: ${cred?.username || 'none'}, hasPwd: ${!!cred?.password}, domain: ${cred?.domain || 'none'})`, - ); - - const socket = await this.guacamole.connect({ - hostname: host.hostname, - port: host.port, - username: cred?.username || '', - password: cred?.password || '', - domain: cred?.domain || undefined, - width: msg.width || 1920, - height: msg.height || 1080, - dpi: msg.dpi || 96, - security: msg.security || 'any', - colorDepth: msg.colorDepth || 24, - ignoreCert: true, - }); - - this.clientSockets.set(client, socket); - - // Pipe guacd → browser: buffer TCP stream into complete Guacamole instructions. - // TCP is a stream protocol — data events can contain partial instructions, - // multiple instructions, or any combination. We must only forward complete - // instructions (terminated by ';') to the browser. - let guacMsgCount = 0; - let tcpBuffer = ''; - socket.on('data', (data: Buffer) => { - tcpBuffer += data.toString('utf-8'); - - // Extract all complete instructions from the buffer - let semicolonIdx: number; - while ((semicolonIdx = tcpBuffer.indexOf(';')) !== -1) { - const instruction = tcpBuffer.substring(0, semicolonIdx + 1); - tcpBuffer = tcpBuffer.substring(semicolonIdx + 1); - - guacMsgCount++; - // Log first 10 instructions and any errors for diagnostics - if (guacMsgCount <= 10 || instruction.includes('error')) { - this.logger.log( - `[guacd→browser #${guacMsgCount}] ${instruction.substring(0, 300)}`, - ); - } - if (client.readyState === 1 /* WebSocket.OPEN */) { - client.send(JSON.stringify({ type: 'guac', instruction })); - } - } - }); - - socket.on('close', () => { - this.logger.log(`guacd socket closed for host ${host.id}`); - this.send(client, { type: 'disconnected', reason: 'RDP session closed' }); - this.clientSockets.delete(client); - }); - - socket.on('error', (err) => { - this.logger.error(`guacd socket error for host ${host.id}: ${err.message}`); - this.send(client, { type: 'error', message: err.message }); - }); - - // Connection tracking - const wsUser = this.clientUsers.get(client); - this.hosts.touchLastConnected(host.id).catch(() => {}); - this.prisma.connectionLog - .create({ data: { hostId: host.id, userId: wsUser!.sub, protocol: 'rdp' } }) - .catch(() => {}); - - this.send(client, { type: 'connected', hostId: host.id, hostName: host.name }); - } - - private send(client: any, data: any) { - if (client.readyState === 1 /* WebSocket.OPEN */) { - client.send(JSON.stringify(data)); - } - } -} diff --git a/backend/src/rdp/rdp.module.ts b/backend/src/rdp/rdp.module.ts deleted file mode 100644 index aab00f6..0000000 --- a/backend/src/rdp/rdp.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Module } from '@nestjs/common'; -import { GuacamoleService } from './guacamole.service'; -import { RdpGateway } from './rdp.gateway'; -import { VaultModule } from '../vault/vault.module'; -import { ConnectionsModule } from '../connections/connections.module'; -import { AuthModule } from '../auth/auth.module'; - -@Module({ - imports: [VaultModule, ConnectionsModule, AuthModule], - providers: [GuacamoleService, RdpGateway], -}) -export class RdpModule {} diff --git a/backend/src/settings/settings.controller.ts b/backend/src/settings/settings.controller.ts deleted file mode 100644 index 10d99eb..0000000 --- a/backend/src/settings/settings.controller.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Controller, Get, Put, Body, UseGuards } from '@nestjs/common'; -import { JwtAuthGuard } from '../auth/jwt-auth.guard'; -import { SettingsService } from './settings.service'; - -@UseGuards(JwtAuthGuard) -@Controller('settings') -export class SettingsController { - constructor(private settings: SettingsService) {} - - @Get() - getAll() { - return this.settings.getAll(); - } - - @Put() - update(@Body() body: Record) { - return this.settings.setMany(body); - } -} diff --git a/backend/src/settings/settings.module.ts b/backend/src/settings/settings.module.ts deleted file mode 100644 index 7114b26..0000000 --- a/backend/src/settings/settings.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Module } from '@nestjs/common'; -import { SettingsService } from './settings.service'; -import { SettingsController } from './settings.controller'; - -@Module({ - providers: [SettingsService], - controllers: [SettingsController], - exports: [SettingsService], -}) -export class SettingsModule {} diff --git a/backend/src/settings/settings.service.ts b/backend/src/settings/settings.service.ts deleted file mode 100644 index 6255908..0000000 --- a/backend/src/settings/settings.service.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { PrismaService } from '../prisma/prisma.service'; - -@Injectable() -export class SettingsService { - constructor(private prisma: PrismaService) {} - - async getAll(): Promise> { - const settings = await this.prisma.setting.findMany(); - return Object.fromEntries(settings.map((s) => [s.key, s.value])); - } - - async get(key: string): Promise { - const setting = await this.prisma.setting.findUnique({ where: { key } }); - return setting?.value ?? null; - } - - async set(key: string, value: string) { - return this.prisma.setting.upsert({ - where: { key }, - update: { value }, - create: { key, value }, - }); - } - - async setMany(settings: Record) { - const ops = Object.entries(settings).map(([key, value]) => - this.prisma.setting.upsert({ where: { key }, update: { value }, create: { key, value } }), - ); - return this.prisma.$transaction(ops); - } - - async remove(key: string) { - return this.prisma.setting.delete({ where: { key } }).catch(() => null); - } -} diff --git a/backend/src/terminal/sftp.gateway.ts b/backend/src/terminal/sftp.gateway.ts deleted file mode 100644 index 2126927..0000000 --- a/backend/src/terminal/sftp.gateway.ts +++ /dev/null @@ -1,249 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import { WsAuthGuard } from '../auth/ws-auth.guard'; -import { SshConnectionService } from './ssh-connection.service'; - -const MAX_EDIT_SIZE = 5 * 1024 * 1024; // 5MB - -@Injectable() -export class SftpGateway { - private readonly logger = new Logger(SftpGateway.name); - - // Maps WebSocket client → set of session IDs they own - private clientSessions = new Map>(); - - constructor( - private ssh: SshConnectionService, - private wsAuth: WsAuthGuard, - ) {} - - handleConnection(client: any, req: any) { - const user = this.wsAuth.validateClient(client, req); - if (!user) { - client.close(4001, 'Unauthorized'); - return; - } - this.logger.log(`SFTP WS connected: ${user.email}`); - - // Initialize an empty session ownership set for this client - this.clientSessions.set(client, new Set()); - - client.on('message', async (raw: Buffer) => { - try { - const msg = JSON.parse(raw.toString()); - this.logger.log(`[SFTP] Message: ${msg.type} sessionId=${msg.sessionId} path=${msg.path || ''}`); - await this.handleMessage(client, msg); - } catch (err: any) { - this.logger.error(`[SFTP] Error: ${err.message}`); - this.send(client, { type: 'error', message: err.message }); - } - }); - - client.on('close', () => { - this.clientSessions.delete(client); - }); - } - - private async handleMessage(client: any, msg: any) { - const { sessionId } = msg; - if (!sessionId) { - return this.send(client, { type: 'error', message: 'sessionId required' }); - } - - const ownedSessions = this.clientSessions.get(client); - - // If this client has already claimed at least one session, enforce ownership - // for all subsequent requests. A client claims a sessionId on first successful - // SFTP channel open (see registration below). - if (ownedSessions && ownedSessions.size > 0 && !ownedSessions.has(sessionId)) { - this.logger.warn(`[SFTP] Session access denied: client does not own sessionId=${sessionId}`); - return this.send(client, { type: 'error', message: 'Session access denied' }); - } - - this.logger.log(`[SFTP] Getting SFTP channel for session ${sessionId}...`); - let sftp: any; - try { - sftp = await this.ssh.getSftpChannel(sessionId); - this.logger.log(`[SFTP] Got SFTP channel OK — type: ${typeof sftp}, constructor: ${sftp?.constructor?.name}`); - } catch (err: any) { - this.logger.error(`[SFTP] Failed to get SFTP channel: ${err.message}`); - return this.send(client, { type: 'error', message: `SFTP channel failed: ${err.message}` }); - } - - // Register this sessionId as owned by this client on first successful channel open - if (ownedSessions && !ownedSessions.has(sessionId)) { - ownedSessions.add(sessionId); - this.logger.log(`[SFTP] Registered sessionId=${sessionId} for client`); - } - - // Listen for SFTP channel errors - sftp.on('error', (err: any) => { - this.logger.error(`[SFTP] Channel error event: ${err.message}`); - }); - sftp.on('close', () => { - this.logger.warn(`[SFTP] Channel closed`); - }); - - switch (msg.type) { - case 'list': { - // Resolve '~' to the user's home directory via SFTP realpath('.') - const resolvePath = (path: string, cb: (resolved: string) => void) => { - if (path === '~') { - sftp.realpath('.', (err: any, absPath: string) => { - if (err) { - this.logger.warn(`[SFTP] realpath('.') failed, falling back to /: ${err.message}`); - cb('/'); - } else { - cb(absPath); - } - }); - } else { - cb(path); - } - }; - - resolvePath(msg.path, (resolvedPath) => { - this.logger.log(`[SFTP] readdir starting for path: "${resolvedPath}"`); - try { - sftp.readdir(resolvedPath, (err: any, list: any[]) => { - this.logger.log(`[SFTP] readdir callback fired, err=${err?.message || 'null'}, entries=${list?.length || 0}`); - if (err) return this.send(client, { type: 'error', message: err.message }); - const entries = list.map((f: any) => ({ - name: f.filename, - path: `${resolvedPath === '/' ? '' : resolvedPath}/${f.filename}`, - size: f.attrs.size, - isDirectory: (f.attrs.mode & 0o40000) !== 0, - permissions: (f.attrs.mode & 0o7777).toString(8), - modified: new Date(f.attrs.mtime * 1000).toISOString(), - })); - this.logger.log(`[SFTP] Sending list response with ${entries.length} entries, client.readyState=${client.readyState}`); - this.send(client, { type: 'list', path: resolvedPath, entries }); - }); - } catch (syncErr: any) { - this.logger.error(`[SFTP] readdir threw synchronously: ${syncErr.message}`); - this.send(client, { type: 'error', message: syncErr.message }); - } - }); - break; - } - case 'read': { - sftp.stat(msg.path, (err: any, stats: any) => { - if (err) return this.send(client, { type: 'error', message: err.message }); - if (stats.size > MAX_EDIT_SIZE) { - return this.send(client, { - type: 'error', - message: `File too large for editing (${(stats.size / 1024 / 1024).toFixed(1)}MB, max 5MB). Download instead.`, - }); - } - const chunks: Buffer[] = []; - const stream = sftp.createReadStream(msg.path); - stream.on('data', (chunk: Buffer) => chunks.push(chunk)); - stream.on('end', () => { - const content = Buffer.concat(chunks).toString('utf-8'); - this.send(client, { type: 'fileContent', path: msg.path, content, encoding: 'utf-8' }); - }); - stream.on('error', (e: any) => this.send(client, { type: 'error', message: e.message })); - }); - break; - } - case 'write': { - const stream = sftp.createWriteStream(msg.path); - stream.end(Buffer.from(msg.data, 'utf-8'), () => { - this.send(client, { type: 'saved', path: msg.path }); - }); - stream.on('error', (e: any) => this.send(client, { type: 'error', message: e.message })); - break; - } - case 'upload': { - const buf = Buffer.from(msg.data, 'base64'); - const stream = sftp.createWriteStream(msg.path); - stream.end(buf, () => { - this.logger.log(`[SFTP] Upload complete: ${msg.path} (${buf.length} bytes)`); - this.send(client, { type: 'uploaded', path: msg.path, size: buf.length }); - }); - stream.on('error', (e: any) => this.send(client, { type: 'error', message: e.message })); - break; - } - case 'mkdir': { - sftp.mkdir(msg.path, (err: any) => { - if (err) return this.send(client, { type: 'error', message: err.message }); - this.send(client, { type: 'created', path: msg.path }); - }); - break; - } - case 'rename': { - sftp.rename(msg.oldPath, msg.newPath, (err: any) => { - if (err) return this.send(client, { type: 'error', message: err.message }); - this.send(client, { type: 'renamed', oldPath: msg.oldPath, newPath: msg.newPath }); - }); - break; - } - case 'delete': { - sftp.unlink(msg.path, (err: any) => { - if (err) { - sftp.rmdir(msg.path, (err2: any) => { - if (err2) return this.send(client, { type: 'error', message: err2.message }); - this.send(client, { type: 'deleted', path: msg.path }); - }); - } else { - this.send(client, { type: 'deleted', path: msg.path }); - } - }); - break; - } - case 'chmod': { - const mode = parseInt(msg.mode, 8); - sftp.chmod(msg.path, mode, (err: any) => { - if (err) return this.send(client, { type: 'error', message: err.message }); - this.send(client, { type: 'chmodDone', path: msg.path, mode: msg.mode }); - }); - break; - } - case 'stat': { - sftp.stat(msg.path, (err: any, stats: any) => { - if (err) return this.send(client, { type: 'error', message: err.message }); - this.send(client, { - type: 'stat', - path: msg.path, - size: stats.size, - isDirectory: (stats.mode & 0o40000) !== 0, - permissions: (stats.mode & 0o7777).toString(8), - modified: new Date(stats.mtime * 1000).toISOString(), - accessed: new Date(stats.atime * 1000).toISOString(), - }); - }); - break; - } - case 'download': { - const readStream = sftp.createReadStream(msg.path); - sftp.stat(msg.path, (err: any, stats: any) => { - if (err) return this.send(client, { type: 'error', message: err.message }); - const transferId = `dl-${Date.now()}`; - let sent = 0; - this.send(client, { type: 'downloadStart', transferId, path: msg.path, total: stats.size }); - readStream.on('data', (chunk: Buffer) => { - sent += chunk.length; - client.send(JSON.stringify({ - type: 'downloadChunk', - transferId, - data: chunk.toString('base64'), - progress: { bytes: sent, total: stats.size }, - })); - }); - readStream.on('end', () => { - this.send(client, { type: 'downloadComplete', transferId }); - }); - readStream.on('error', (e: any) => { - this.send(client, { type: 'error', message: e.message }); - }); - }); - break; - } - } - } - - private send(client: any, data: any) { - if (client.readyState === 1) { - client.send(JSON.stringify(data)); - } - } -} diff --git a/backend/src/terminal/ssh-connection.service.ts b/backend/src/terminal/ssh-connection.service.ts deleted file mode 100644 index 55a77a8..0000000 --- a/backend/src/terminal/ssh-connection.service.ts +++ /dev/null @@ -1,229 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import { Client, ClientChannel, utils } from 'ssh2'; -import { createHash } from 'crypto'; -import { CredentialsService } from '../vault/credentials.service'; -import { HostsService } from '../connections/hosts.service'; -import { PrismaService } from '../prisma/prisma.service'; -import { v4 as uuid } from 'uuid'; - -export interface SshSession { - id: string; - hostId: number; - client: Client; - stream: ClientChannel | null; -} - -@Injectable() -export class SshConnectionService { - private readonly logger = new Logger(SshConnectionService.name); - private sessions = new Map(); - private pendingClients = new Map(); // sessionId → client (before 'ready') - - constructor( - private credentials: CredentialsService, - private hosts: HostsService, - private prisma: PrismaService, - ) {} - - async connect( - hostId: number, - userId: number, - onData: (data: string) => void, - onClose: (reason: string) => void, - onHostKeyVerify: (fingerprint: string, isNew: boolean) => Promise, - options?: { enableCwdTracking?: boolean }, - ): Promise { - const host = await this.hosts.findOne(hostId); - const cred = host.credentialId - ? await this.credentials.decryptForConnection(host.credentialId) - : null; - - const sessionId = uuid(); - const client = new Client(); - this.pendingClients.set(sessionId, client); - - return new Promise((resolve, reject) => { - let settled = false; - - client.on('ready', () => { - this.pendingClients.delete(sessionId); - client.shell({ term: 'xterm-256color' }, (err, stream) => { - if (err) { - client.end(); - return reject(err); - } - const session: SshSession = { id: sessionId, hostId, client, stream }; - this.sessions.set(sessionId, session); - - stream.on('data', (data: Buffer) => onData(data.toString('utf-8'))); - - // Shell integration: inject PROMPT_COMMAND (bash) / precmd (zsh) to emit - // OSC 7 escape sequences reporting the current working directory on every prompt. - // Leading space prevents the command from being saved to shell history. - // The frontend captures OSC 7 via xterm.js and syncs the SFTP sidebar. - // Only injected when the caller explicitly opts in via options.enableCwdTracking. - if (options?.enableCwdTracking) { - const shellIntegration = - ` if [ -n "$ZSH_VERSION" ]; then` + - ` __wraith_cwd(){ printf '\\e]7;file://%s%s\\a' "$HOST" "$PWD"; };` + - ` precmd_functions+=(__wraith_cwd);` + - ` elif [ -n "$BASH_VERSION" ]; then` + - ` PROMPT_COMMAND='printf "\\033]7;file://%s%s\\a" "$HOSTNAME" "$PWD"';` + - ` fi\n`; - stream.write(shellIntegration); - } - - stream.on('close', () => { - this.disconnect(sessionId); - onClose('Session ended'); - }); - - // Update lastConnectedAt and create connection log - this.hosts.touchLastConnected(hostId); - this.prisma.connectionLog.create({ - data: { hostId, userId, protocol: host.protocol }, - }).catch(() => {}); - - resolve(sessionId); - }); - }); - - client.on('error', (err) => { - this.logger.error(`SSH error for host ${hostId}: ${err.message}`); - settled = true; - this.pendingClients.delete(sessionId); - this.disconnect(sessionId); - client.destroy(); - onClose(err.message); - reject(err); - }); - - const connectConfig: any = { - host: host.hostname, - port: host.port, - username: cred?.username || 'root', - debug: (msg: string) => { - this.logger.log(`[SSH-DEBUG] ${msg}`); - }, - hostVerifier: (key: Buffer, verify: (accept: boolean) => void) => { - const fingerprint = createHash('sha256').update(key).digest('base64'); - const fp = `SHA256:${fingerprint}`; - - if (host.hostFingerprint === fp) { - verify(true); // known host — key matches, accept silently - return; - } - - if (host.hostFingerprint && host.hostFingerprint !== fp) { - // Key has CHANGED from what was stored — possible MITM attack. Block immediately. - this.logger.warn( - `[SSH] HOST KEY CHANGED for hostId=${hostId} — possible MITM attack. Connection blocked.`, - ); - verify(false); - return; - } - - // No stored fingerprint — first connection, ask the user via WebSocket - onHostKeyVerify(fp, true).then((accepted) => { - if (settled) return; // connection already timed out / errored — don't call back into ssh2 - if (accepted) { - // Persist fingerprint so future connections auto-accept - this.prisma.host.update({ - where: { id: hostId }, - data: { hostFingerprint: fp }, - }).catch(() => {}); - } - verify(accepted); - }); - }, - }; - - if (cred?.sshKey) { - connectConfig.privateKey = cred.sshKey.privateKey; - if (cred.sshKey.passphrase) { - connectConfig.passphrase = cred.sshKey.passphrase; - } - this.logger.log(`[SSH] Using SSH key auth`); - } else if (cred?.password) { - connectConfig.password = cred.password; - this.logger.log(`[SSH] Using password auth for hostId=${hostId}`); - } else { - this.logger.warn(`[SSH] No auth method available for host ${hostId}`); - } - - client.connect(connectConfig); - }); - } - - write(sessionId: string, data: string) { - const session = this.sessions.get(sessionId); - if (session?.stream) { - session.stream.write(data); - } - } - - resize(sessionId: string, cols: number, rows: number) { - const session = this.sessions.get(sessionId); - if (session?.stream) { - session.stream.setWindow(rows, cols, 0, 0); - } - } - - disconnect(sessionId: string) { - // Kill pending (not yet 'ready') connections - const pending = this.pendingClients.get(sessionId); - if (pending) { - pending.destroy(); - this.pendingClients.delete(sessionId); - } - - const session = this.sessions.get(sessionId); - if (session) { - session.stream?.close(); - session.client.end(); - this.sessions.delete(sessionId); - this.sftpChannels.delete(sessionId); - - // Update connection log with disconnect time - this.prisma.connectionLog.updateMany({ - where: { hostId: session.hostId, disconnectedAt: null }, - data: { disconnectedAt: new Date() }, - }).catch(() => {}); - } - } - - getSession(sessionId: string): SshSession | undefined { - return this.sessions.get(sessionId); - } - - private sftpChannels = new Map(); - - getSftpChannel(sessionId: string): Promise { - const logger = this.logger; - const cached = this.sftpChannels.get(sessionId); - if (cached) { - return Promise.resolve(cached); - } - return new Promise((resolve, reject) => { - const session = this.sessions.get(sessionId); - if (!session) { - logger.error(`[SFTP] Session ${sessionId} not found in sessions map (${this.sessions.size} active sessions)`); - return reject(new Error('Session not found')); - } - logger.log(`[SFTP] Requesting SFTP subsystem on session ${sessionId}`); - session.client.sftp((err, sftp) => { - if (err) { - logger.error(`[SFTP] client.sftp() callback error: ${err.message}`); - return reject(err); - } - logger.log(`[SFTP] SFTP subsystem opened successfully for session ${sessionId}`); - sftp.on('close', () => { - this.sftpChannels.delete(sessionId); - logger.log(`[SFTP] Channel closed for session ${sessionId}`); - }); - this.sftpChannels.set(sessionId, sftp); - resolve(sftp); - }); - }); - } -} diff --git a/backend/src/terminal/terminal.gateway.ts b/backend/src/terminal/terminal.gateway.ts deleted file mode 100644 index 6c3e9aa..0000000 --- a/backend/src/terminal/terminal.gateway.ts +++ /dev/null @@ -1,149 +0,0 @@ -import { Injectable, Logger } from '@nestjs/common'; -import { WsAuthGuard } from '../auth/ws-auth.guard'; -import { SshConnectionService } from './ssh-connection.service'; - -@Injectable() -export class TerminalGateway { - private readonly logger = new Logger(TerminalGateway.name); - private clientSessions = new Map(); // ws client → sessionIds - private clientUsers = new Map(); // ws client → user - private pendingHostKeyVerifications = new Map void }>(); // verifyId → resolver - - constructor( - private ssh: SshConnectionService, - private wsAuth: WsAuthGuard, - ) {} - - handleConnection(client: any, req: any) { - this.logger.log(`[WS] handleConnection fired, req.url=${req?.url}, client.url=${client?.url}`); - const user = this.wsAuth.validateClient(client, req); - if (!user) { - this.logger.warn(`[WS] Auth failed — closing 4001`); - client.close(4001, 'Unauthorized'); - return; - } - this.clientSessions.set(client, []); - this.clientUsers.set(client, user); - this.logger.log(`[WS] Terminal connected: ${user.email}`); - - client.on('message', async (raw: Buffer) => { - try { - const msg = JSON.parse(raw.toString()); - if (msg.type === 'data') { - this.logger.log(`[WS-TERMINAL] type=data sessionId=${msg.sessionId} bytes=${msg.data?.length || 0}`); - } else { - this.logger.log(`[WS-TERMINAL] ${JSON.stringify(msg).substring(0, 200)}`); - } - await this.handleMessage(client, msg); - } catch (err: any) { - this.logger.error(`[WS] handleMessage error: ${err.message}\n${err.stack}`); - this.send(client, { type: 'error', message: err.message }); - } - }); - - client.on('close', () => { - this.logger.log(`[WS] Client disconnected`); - const sessions = this.clientSessions.get(client) || []; - sessions.forEach((sid) => this.ssh.disconnect(sid)); - this.clientSessions.delete(client); - this.clientUsers.delete(client); - }); - } - - private async handleMessage(client: any, msg: any) { - switch (msg.type) { - case 'connect': { - let sessionId = ''; - const wsUser = this.clientUsers.get(client); - try { - sessionId = await this.ssh.connect( - msg.hostId, - wsUser!.sub, - (data) => this.send(client, { type: 'data', sessionId, data }), - (reason) => this.send(client, { type: 'disconnected', sessionId, reason }), - (fingerprint: string, isNew: boolean) => { - // New host — ask the user; changed host keys are rejected in the service before reaching here - return new Promise((resolve) => { - const verifyId = `${sessionId}-${Date.now()}`; - this.pendingHostKeyVerifications.set(verifyId, { resolve }); - this.send(client, { - type: 'host-key-verify', - verifyId, - fingerprint, - isNew, - }); - // Timeout after 30 seconds — reject if no response - setTimeout(() => { - if (this.pendingHostKeyVerifications.has(verifyId)) { - this.pendingHostKeyVerifications.delete(verifyId); - resolve(false); - } - }, 30000); - }); - }, - ); - } catch (err: any) { - this.logger.error(`[WS] SSH connect failed: ${err.message}`); - this.send(client, { type: 'error', message: `Connection failed: ${err.message}` }); - break; - } - const sessions = this.clientSessions.get(client) || []; - sessions.push(sessionId); - this.clientSessions.set(client, sessions); - this.send(client, { type: 'connected', sessionId }); - break; - } - case 'data': { - // M-1: Verify the session belongs to this client before processing - const dataSessions = this.clientSessions.get(client); - if (!dataSessions || !dataSessions.includes(msg.sessionId)) { - this.send(client, { type: 'error', message: 'Session access denied' }); - return; - } - if (msg.sessionId) { - this.ssh.write(msg.sessionId, msg.data); - } - break; - } - case 'resize': { - // M-1: Verify the session belongs to this client before processing - const resizeSessions = this.clientSessions.get(client); - if (!resizeSessions || !resizeSessions.includes(msg.sessionId)) { - this.send(client, { type: 'error', message: 'Session access denied' }); - return; - } - if (msg.sessionId) { - this.ssh.resize(msg.sessionId, msg.cols, msg.rows); - } - break; - } - case 'disconnect': { - // M-1: Verify the session belongs to this client before processing - const disconnectSessions = this.clientSessions.get(client); - if (!disconnectSessions || !disconnectSessions.includes(msg.sessionId)) { - this.send(client, { type: 'error', message: 'Session access denied' }); - return; - } - if (msg.sessionId) { - this.ssh.disconnect(msg.sessionId); - } - break; - } - case 'host-key-accept': - case 'host-key-reject': { - const pending = this.pendingHostKeyVerifications.get(msg.verifyId); - if (pending) { - pending.resolve(msg.type === 'host-key-accept'); - this.pendingHostKeyVerifications.delete(msg.verifyId); - } - break; - } - } - } - - private send(client: any, data: any) { - if (client.readyState === 1) { // WebSocket.OPEN - client.send(JSON.stringify(data)); - } - } -} diff --git a/backend/src/terminal/terminal.module.ts b/backend/src/terminal/terminal.module.ts deleted file mode 100644 index 99e9df8..0000000 --- a/backend/src/terminal/terminal.module.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Module } from '@nestjs/common'; -import { SshConnectionService } from './ssh-connection.service'; -import { TerminalGateway } from './terminal.gateway'; -import { SftpGateway } from './sftp.gateway'; -import { VaultModule } from '../vault/vault.module'; -import { ConnectionsModule } from '../connections/connections.module'; -import { AuthModule } from '../auth/auth.module'; - -@Module({ - imports: [VaultModule, ConnectionsModule, AuthModule], - providers: [SshConnectionService, TerminalGateway, SftpGateway], - exports: [SshConnectionService, TerminalGateway, SftpGateway], -}) -export class TerminalModule {} diff --git a/backend/src/vault/credentials.controller.ts b/backend/src/vault/credentials.controller.ts deleted file mode 100644 index fffdb6f..0000000 --- a/backend/src/vault/credentials.controller.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { Controller, Get, Post, Put, Delete, Param, Body, Request, UseGuards, ParseIntPipe, Logger } from '@nestjs/common'; -import { JwtAuthGuard } from '../auth/jwt-auth.guard'; -import { AdminGuard } from '../auth/admin.guard'; -import { CredentialsService } from './credentials.service'; -import { EncryptionService } from './encryption.service'; -import { PrismaService } from '../prisma/prisma.service'; -import { CreateCredentialDto } from './dto/create-credential.dto'; -import { UpdateCredentialDto } from './dto/update-credential.dto'; - -@UseGuards(JwtAuthGuard) -@Controller('credentials') -export class CredentialsController { - private readonly logger = new Logger(CredentialsController.name); - constructor( - private credentials: CredentialsService, - private encryption: EncryptionService, - private prisma: PrismaService, - ) {} - - @Get() - findAll(@Request() req: any) { - return this.credentials.findAll(req.user.sub); - } - - @Get(':id') - findOne(@Request() req: any, @Param('id', ParseIntPipe) id: number) { - return this.credentials.findOne(id, req.user.sub); - } - - @Post() - create(@Request() req: any, @Body() dto: CreateCredentialDto) { - return this.credentials.create(req.user.sub, dto); - } - - @Put(':id') - update(@Request() req: any, @Param('id', ParseIntPipe) id: number, @Body() dto: UpdateCredentialDto) { - return this.credentials.update(id, req.user.sub, dto); - } - - @Delete(':id') - remove(@Request() req: any, @Param('id', ParseIntPipe) id: number) { - return this.credentials.remove(id, req.user.sub); - } - - /** - * Admin-only: Migrate all v1 (raw key) encrypted values to v2 (Argon2id). - * Safe to run multiple times — skips records already on v2. - */ - @Post('migrate-v2') - @UseGuards(AdminGuard) - async migrateToV2() { - let credsMigrated = 0; - let keysMigrated = 0; - - // Migrate credential passwords - const creds = await this.prisma.credential.findMany({ - where: { encryptedValue: { not: null } }, - select: { id: true, encryptedValue: true }, - }); - for (const cred of creds) { - if (cred.encryptedValue && this.encryption.isV1(cred.encryptedValue)) { - const upgraded = await this.encryption.upgradeToV2(cred.encryptedValue); - if (upgraded) { - await this.prisma.credential.update({ where: { id: cred.id }, data: { encryptedValue: upgraded } }); - credsMigrated++; - } - } - } - - // Migrate SSH key private keys and passphrases - const keys = await this.prisma.sshKey.findMany({ - select: { id: true, encryptedPrivateKey: true, passphraseEncrypted: true }, - }); - for (const key of keys) { - const updates: any = {}; - if (this.encryption.isV1(key.encryptedPrivateKey)) { - updates.encryptedPrivateKey = await this.encryption.upgradeToV2(key.encryptedPrivateKey); - } - if (key.passphraseEncrypted && this.encryption.isV1(key.passphraseEncrypted)) { - updates.passphraseEncrypted = await this.encryption.upgradeToV2(key.passphraseEncrypted); - } - if (Object.keys(updates).length) { - await this.prisma.sshKey.update({ where: { id: key.id }, data: updates }); - keysMigrated++; - } - } - - this.logger.log(`v2 migration complete: ${credsMigrated} credentials, ${keysMigrated} SSH keys upgraded`); - return { credsMigrated, keysMigrated }; - } -} diff --git a/backend/src/vault/credentials.service.spec.ts b/backend/src/vault/credentials.service.spec.ts deleted file mode 100644 index 73ae8fe..0000000 --- a/backend/src/vault/credentials.service.spec.ts +++ /dev/null @@ -1,178 +0,0 @@ -// backend/src/vault/credentials.service.spec.ts -import { Test, TestingModule } from '@nestjs/testing'; -import { CredentialsService } from './credentials.service'; -import { PrismaService } from '../prisma/prisma.service'; -import { EncryptionService } from './encryption.service'; -import { NotFoundException, ForbiddenException } from '@nestjs/common'; - -describe('CredentialsService', () => { - let service: CredentialsService; - let prisma: any; - let encryption: any; - - beforeEach(async () => { - prisma = { - credential: { - findMany: jest.fn(), - findUnique: jest.fn(), - create: jest.fn(), - update: jest.fn(), - delete: jest.fn(), - }, - }; - encryption = { - encrypt: jest.fn().mockResolvedValue('v2:mock:encrypted'), - decrypt: jest.fn().mockResolvedValue('decrypted-password'), - }; - - const module: TestingModule = await Test.createTestingModule({ - providers: [ - CredentialsService, - { provide: PrismaService, useValue: prisma }, - { provide: EncryptionService, useValue: encryption }, - ], - }).compile(); - - service = module.get(CredentialsService); - }); - - describe('findAll', () => { - it('should query by userId and exclude encryptedValue from select', async () => { - prisma.credential.findMany.mockResolvedValue([{ id: 1, name: 'test' }]); - await service.findAll(1); - expect(prisma.credential.findMany).toHaveBeenCalledWith( - expect.objectContaining({ - where: { userId: 1 }, - select: expect.objectContaining({ id: true, name: true }), - }), - ); - // encryptedValue must never appear in the list response (H-10) - const call = prisma.credential.findMany.mock.calls[0][0]; - expect(call.select.encryptedValue).toBeUndefined(); - }); - }); - - describe('findOne', () => { - it('should return credential for owner', async () => { - prisma.credential.findUnique.mockResolvedValue({ id: 1, userId: 1, name: 'cred', sshKey: null, hosts: [] }); - const result = await service.findOne(1, 1); - expect(result.name).toBe('cred'); - }); - - it('should throw ForbiddenException when userId does not match', async () => { - prisma.credential.findUnique.mockResolvedValue({ id: 1, userId: 2, name: 'cred', sshKey: null, hosts: [] }); - await expect(service.findOne(1, 1)).rejects.toThrow(ForbiddenException); - }); - - it('should throw NotFoundException for missing credential', async () => { - prisma.credential.findUnique.mockResolvedValue(null); - await expect(service.findOne(99, 1)).rejects.toThrow(NotFoundException); - }); - }); - - describe('create', () => { - it('should encrypt password before storage', async () => { - prisma.credential.create.mockResolvedValue({ id: 1 }); - await service.create(1, { - name: 'test', - username: 'admin', - password: 'secret', - type: 'password' as any, - }); - expect(encryption.encrypt).toHaveBeenCalledWith('secret'); - expect(prisma.credential.create).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ encryptedValue: 'v2:mock:encrypted' }), - }), - ); - }); - - it('should store null encryptedValue when no password provided', async () => { - prisma.credential.create.mockResolvedValue({ id: 2 }); - await service.create(1, { name: 'ssh-only', type: 'ssh_key' as any, sshKeyId: 5 }); - expect(encryption.encrypt).not.toHaveBeenCalled(); - expect(prisma.credential.create).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ encryptedValue: null }), - }), - ); - }); - }); - - describe('decryptForConnection', () => { - it('should decrypt password credential', async () => { - prisma.credential.findUnique.mockResolvedValue({ - id: 1, - username: 'admin', - domain: null, - encryptedValue: 'v2:encrypted', - sshKey: null, - sshKeyId: null, - }); - const result = await service.decryptForConnection(1); - expect(result.password).toBe('decrypted-password'); - expect(result.username).toBe('admin'); - }); - - it('should decrypt SSH key credential with passphrase', async () => { - prisma.credential.findUnique.mockResolvedValue({ - id: 1, - username: 'root', - domain: null, - encryptedValue: null, - sshKey: { encryptedPrivateKey: 'v2:key', passphraseEncrypted: 'v2:pass' }, - sshKeyId: 5, - }); - encryption.decrypt - .mockResolvedValueOnce('private-key-content') - .mockResolvedValueOnce('passphrase'); - const result = await service.decryptForConnection(1); - expect(result.sshKey).toEqual({ privateKey: 'private-key-content', passphrase: 'passphrase' }); - }); - - it('should throw NotFoundException for orphaned SSH key reference', async () => { - prisma.credential.findUnique.mockResolvedValue({ - id: 1, - name: 'orphan', - username: 'root', - domain: null, - encryptedValue: null, - sshKey: null, - sshKeyId: 99, - }); - await expect(service.decryptForConnection(1)).rejects.toThrow(NotFoundException); - }); - - it('should throw NotFoundException for credential with no auth method', async () => { - prisma.credential.findUnique.mockResolvedValue({ - id: 1, - name: 'empty', - username: 'root', - domain: null, - encryptedValue: null, - sshKey: null, - sshKeyId: null, - }); - await expect(service.decryptForConnection(1)).rejects.toThrow(NotFoundException); - }); - - it('should throw NotFoundException for missing credential', async () => { - prisma.credential.findUnique.mockResolvedValue(null); - await expect(service.decryptForConnection(99)).rejects.toThrow(NotFoundException); - }); - }); - - describe('remove', () => { - it('should delete owned credential', async () => { - prisma.credential.findUnique.mockResolvedValue({ id: 1, userId: 1, sshKey: null, hosts: [] }); - prisma.credential.delete.mockResolvedValue({ id: 1 }); - await service.remove(1, 1); - expect(prisma.credential.delete).toHaveBeenCalledWith({ where: { id: 1 } }); - }); - - it('should throw ForbiddenException when removing non-owned credential', async () => { - prisma.credential.findUnique.mockResolvedValue({ id: 1, userId: 2, sshKey: null, hosts: [] }); - await expect(service.remove(1, 1)).rejects.toThrow(ForbiddenException); - }); - }); -}); diff --git a/backend/src/vault/credentials.service.ts b/backend/src/vault/credentials.service.ts deleted file mode 100644 index 8f58848..0000000 --- a/backend/src/vault/credentials.service.ts +++ /dev/null @@ -1,115 +0,0 @@ -import { Injectable, NotFoundException, ForbiddenException } from '@nestjs/common'; -import { PrismaService } from '../prisma/prisma.service'; -import { EncryptionService } from './encryption.service'; -import { CreateCredentialDto } from './dto/create-credential.dto'; -import { UpdateCredentialDto } from './dto/update-credential.dto'; - -@Injectable() -export class CredentialsService { - constructor( - private prisma: PrismaService, - private encryption: EncryptionService, - ) {} - - findAll(userId: number) { - // H-10: exclude encryptedValue from list response — frontend never needs it - return this.prisma.credential.findMany({ - where: { userId }, - select: { - id: true, - name: true, - username: true, - domain: true, - type: true, - sshKeyId: true, - sshKey: { select: { id: true, name: true, keyType: true, fingerprint: true } }, - createdAt: true, - updatedAt: true, - }, - orderBy: { name: 'asc' }, - }); - } - - async findOne(id: number, userId?: number) { - const cred = await this.prisma.credential.findUnique({ - where: { id }, - include: { sshKey: true, hosts: { select: { id: true, name: true } } }, - }); - if (!cred) throw new NotFoundException(`Credential ${id} not found`); - if (userId !== undefined && cred.userId !== userId) { - throw new ForbiddenException('Access denied'); - } - return cred; - } - - async create(userId: number, dto: CreateCredentialDto) { - const encryptedValue = dto.password ? await this.encryption.encrypt(dto.password) : null; - return this.prisma.credential.create({ - data: { - name: dto.name, - username: dto.username, - domain: dto.domain, - type: dto.type, - userId, - encryptedValue, - sshKeyId: dto.sshKeyId, - }, - }); - } - - async update(id: number, userId: number, dto: UpdateCredentialDto) { - await this.findOne(id, userId); - const data: any = { ...dto }; - delete data.password; - if (dto.password) { - data.encryptedValue = await this.encryption.encrypt(dto.password); - } - return this.prisma.credential.update({ where: { id }, data }); - } - - async remove(id: number, userId: number) { - await this.findOne(id, userId); - return this.prisma.credential.delete({ where: { id } }); - } - - /** Decrypt credential for use in SSH/RDP connections. Never expose over API. */ - async decryptForConnection(id: number): Promise<{ - username: string | null; - domain: string | null; - password: string | null; - sshKey: { privateKey: string; passphrase: string | null } | null; - }> { - const cred = await this.prisma.credential.findUnique({ - where: { id }, - include: { sshKey: true }, - }); - if (!cred) throw new NotFoundException(`Credential ${id} not found`); - - let password: string | null = null; - if (cred.encryptedValue) { - password = await this.encryption.decrypt(cred.encryptedValue); - } - - let sshKey: { privateKey: string; passphrase: string | null } | null = null; - if (cred.sshKey) { - const privateKey = await this.encryption.decrypt(cred.sshKey.encryptedPrivateKey); - const passphrase = cred.sshKey.passphraseEncrypted - ? await this.encryption.decrypt(cred.sshKey.passphraseEncrypted) - : null; - sshKey = { privateKey, passphrase }; - } else if (cred.sshKeyId) { - // Orphaned reference — credential points to a deleted/missing SSH key - throw new NotFoundException( - `Credential "${cred.name}" references SSH key #${cred.sshKeyId} which no longer exists. Re-import the key or update the credential.`, - ); - } - - if (!password && !sshKey) { - throw new NotFoundException( - `Credential "${cred.name}" has no password or SSH key configured.`, - ); - } - - return { username: cred.username, domain: cred.domain, password, sshKey }; - } -} diff --git a/backend/src/vault/dto/create-credential.dto.ts b/backend/src/vault/dto/create-credential.dto.ts deleted file mode 100644 index 4b270b0..0000000 --- a/backend/src/vault/dto/create-credential.dto.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { IsString, IsOptional, IsEnum, IsInt } from 'class-validator'; -import { CredentialType } from '@prisma/client'; - -export class CreateCredentialDto { - @IsString() - name: string; - - @IsString() - @IsOptional() - username?: string; - - @IsString() - @IsOptional() - domain?: string; - - @IsEnum(CredentialType) - type: CredentialType; - - @IsString() - @IsOptional() - password?: string; // plaintext — encrypted before storage - - @IsInt() - @IsOptional() - sshKeyId?: number; -} diff --git a/backend/src/vault/dto/create-ssh-key.dto.ts b/backend/src/vault/dto/create-ssh-key.dto.ts deleted file mode 100644 index d4fb9e4..0000000 --- a/backend/src/vault/dto/create-ssh-key.dto.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { IsString, IsOptional } from 'class-validator'; - -export class CreateSshKeyDto { - @IsString() - name: string; - - @IsString() - privateKey: string; // plaintext — encrypted before storage - - @IsString() - @IsOptional() - passphrase?: string; // plaintext — encrypted before storage - - @IsString() - @IsOptional() - publicKey?: string; -} diff --git a/backend/src/vault/dto/update-credential.dto.ts b/backend/src/vault/dto/update-credential.dto.ts deleted file mode 100644 index 0cfb002..0000000 --- a/backend/src/vault/dto/update-credential.dto.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PartialType } from '@nestjs/mapped-types'; -import { CreateCredentialDto } from './create-credential.dto'; - -export class UpdateCredentialDto extends PartialType(CreateCredentialDto) {} diff --git a/backend/src/vault/dto/update-ssh-key.dto.ts b/backend/src/vault/dto/update-ssh-key.dto.ts deleted file mode 100644 index 97f6de7..0000000 --- a/backend/src/vault/dto/update-ssh-key.dto.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { IsString, IsOptional } from 'class-validator'; - -export class UpdateSshKeyDto { - @IsString() - @IsOptional() - name?: string; - - @IsString() - @IsOptional() - passphrase?: string; // new passphrase (re-encrypted) -} diff --git a/backend/src/vault/encryption.service.spec.ts b/backend/src/vault/encryption.service.spec.ts deleted file mode 100644 index 2d45475..0000000 --- a/backend/src/vault/encryption.service.spec.ts +++ /dev/null @@ -1,85 +0,0 @@ -// backend/src/vault/encryption.service.spec.ts - -// Set test encryption key before importing the service — constructor reads process.env.ENCRYPTION_KEY -process.env.ENCRYPTION_KEY = 'a'.repeat(64); // 64 hex chars = 32 bytes - -import { EncryptionService } from './encryption.service'; - -describe('EncryptionService', () => { - let service: EncryptionService; - - beforeAll(async () => { - service = new EncryptionService(); - await service.onModuleInit(); - }); - - describe('encrypt/decrypt round-trip', () => { - it('should encrypt and decrypt a string', async () => { - const plaintext = 'my-secret-password'; - const encrypted = await service.encrypt(plaintext); - expect(encrypted).toMatch(/^v2:/); - const decrypted = await service.decrypt(encrypted); - expect(decrypted).toBe(plaintext); - }); - - it('should produce different ciphertexts for the same plaintext (random salt + IV)', async () => { - const plaintext = 'same-input'; - const a = await service.encrypt(plaintext); - const b = await service.encrypt(plaintext); - expect(a).not.toBe(b); - }); - - it('should handle empty string', async () => { - const encrypted = await service.encrypt(''); - const decrypted = await service.decrypt(encrypted); - expect(decrypted).toBe(''); - }); - - it('should handle unicode and emoji', async () => { - const plaintext = '密码 пароль 🔐'; - const encrypted = await service.encrypt(plaintext); - const decrypted = await service.decrypt(encrypted); - expect(decrypted).toBe(plaintext); - }); - }); - - describe('v2 format structure', () => { - it('should produce a 5-part colon-delimited ciphertext', async () => { - const encrypted = await service.encrypt('test'); - const parts = encrypted.split(':'); - expect(parts[0]).toBe('v2'); - expect(parts).toHaveLength(5); // v2:salt:iv:authTag:ciphertext - }); - }); - - describe('isV1', () => { - it('should detect v1 format', () => { - expect(service.isV1('v1:abc123:def456:ghi789')).toBe(true); - }); - - it('should not classify v2 as v1', () => { - expect(service.isV1('v2:abc:def:ghi:jkl')).toBe(false); - }); - }); - - describe('upgradeToV2', () => { - it('should return null for already-v2 ciphertext', async () => { - const v2 = await service.encrypt('test'); - const result = await service.upgradeToV2(v2); - expect(result).toBeNull(); - }); - }); - - describe('error handling', () => { - it('should throw on unknown encryption version', async () => { - await expect(service.decrypt('v3:bad:data')).rejects.toThrow('Unknown encryption version'); - }); - - it('should throw on tampered ciphertext (auth tag mismatch)', async () => { - const encrypted = await service.encrypt('test'); - // Corrupt the last 4 hex chars of the ciphertext segment - const tampered = encrypted.slice(0, -4) + 'dead'; - await expect(service.decrypt(tampered)).rejects.toThrow(); - }); - }); -}); diff --git a/backend/src/vault/encryption.service.ts b/backend/src/vault/encryption.service.ts deleted file mode 100644 index e43add1..0000000 --- a/backend/src/vault/encryption.service.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; -import { createCipheriv, createDecipheriv, randomBytes } from 'crypto'; -import * as argon2 from 'argon2'; - -/** - * Vault Encryption Service - * - * v1: AES-256-GCM with raw ENCRYPTION_KEY (legacy, still decryptable) - * v2: AES-256-GCM with Argon2id-derived key (GPU-resistant) - * - * On encrypt, always produces v2. On decrypt, handles both v1 and v2. - * Use migrateToV2() to re-encrypt all v1 records. - * - * Argon2id parameters (OWASP recommended): - * memory: 64 MiB, iterations: 3, parallelism: 4 - * salt: 16 random bytes (stored per-ciphertext) - */ -@Injectable() -export class EncryptionService implements OnModuleInit { - private readonly logger = new Logger(EncryptionService.name); - private readonly algorithm = 'aes-256-gcm'; - - // v1: raw key from env (kept for backwards compat decryption) - private readonly rawKey: Buffer; - - // v2: Argon2id-derived key (computed once at startup with a fixed salt derived from ENCRYPTION_KEY) - // Per-record salts are used in the ciphertext format, but the master derived key uses the env key as input - private readonly masterPassword: Buffer; - - // Argon2id tuning — OWASP recommendations for sensitive data - private readonly argon2Options = { - type: argon2.argon2id, - memoryCost: 65536, // 64 MiB - timeCost: 3, // 3 iterations - parallelism: 4, // 4 threads - hashLength: 32, // 256-bit derived key - }; - - constructor() { - const hex = process.env.ENCRYPTION_KEY; - if (!hex || hex.length < 64) { - throw new Error('ENCRYPTION_KEY must be a 64-char hex string (32 bytes)'); - } - this.rawKey = Buffer.from(hex.slice(0, 64), 'hex'); - this.masterPassword = this.rawKey; // Used as Argon2 password input - } - - async onModuleInit() { - // Warm up Argon2 by deriving a test key at startup — this also validates the config - try { - await this.deriveKey(randomBytes(16)); - this.logger.log('Argon2id key derivation initialized (v2 encryption active)'); - } catch (err: any) { - this.logger.error(`Argon2id initialization failed: ${err.message}`); - throw err; - } - } - - /** - * Derive a 256-bit AES key from the master password + per-record salt using Argon2id - */ - private async deriveKey(salt: Buffer): Promise { - const hash = await argon2.hash(this.masterPassword, { - ...this.argon2Options, - salt, - raw: true, // Return raw Buffer instead of encoded string - }); - return hash; - } - - /** - * Encrypt plaintext using AES-256-GCM with Argon2id-derived key (v2) - * Format: v2:::: - */ - async encrypt(plaintext: string): Promise { - const salt = randomBytes(16); - const derivedKey = await this.deriveKey(salt); - const iv = randomBytes(16); - - const cipher = createCipheriv(this.algorithm, derivedKey, iv); - const encrypted = Buffer.concat([ - cipher.update(plaintext, 'utf8'), - cipher.final(), - ]); - const authTag = cipher.getAuthTag(); - - return `v2:${salt.toString('hex')}:${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted.toString('hex')}`; - } - - /** - * Decrypt ciphertext — handles both v1 (legacy raw key) and v2 (Argon2id) - */ - async decrypt(encrypted: string): Promise { - const parts = encrypted.split(':'); - const version = parts[0]; - - if (version === 'v2') { - // v2: Argon2id-derived key - const [, saltHex, ivHex, authTagHex, ciphertextHex] = parts; - const salt = Buffer.from(saltHex, 'hex'); - const iv = Buffer.from(ivHex, 'hex'); - const authTag = Buffer.from(authTagHex, 'hex'); - const ciphertext = Buffer.from(ciphertextHex, 'hex'); - - const derivedKey = await this.deriveKey(salt); - const decipher = createDecipheriv(this.algorithm, derivedKey, iv); - decipher.setAuthTag(authTag); - - return Buffer.concat([ - decipher.update(ciphertext), - decipher.final(), - ]).toString('utf8'); - } - - if (version === 'v1') { - // v1: raw ENCRYPTION_KEY (legacy backwards compat) - const [, ivHex, authTagHex, ciphertextHex] = parts; - const iv = Buffer.from(ivHex, 'hex'); - const authTag = Buffer.from(authTagHex, 'hex'); - const ciphertext = Buffer.from(ciphertextHex, 'hex'); - - const decipher = createDecipheriv(this.algorithm, this.rawKey, iv); - decipher.setAuthTag(authTag); - - return Buffer.concat([ - decipher.update(ciphertext), - decipher.final(), - ]).toString('utf8'); - } - - throw new Error(`Unknown encryption version: ${version}`); - } - - /** - * Check if a ciphertext is using the legacy v1 format - */ - isV1(encrypted: string): boolean { - return encrypted.startsWith('v1:'); - } - - /** - * Re-encrypt a v1 ciphertext to v2 (Argon2id). Returns the new ciphertext, - * or null if already v2. - */ - async upgradeToV2(encrypted: string): Promise { - if (!this.isV1(encrypted)) return null; - const plaintext = await this.decrypt(encrypted); - return this.encrypt(plaintext); - } -} diff --git a/backend/src/vault/ssh-keys.controller.ts b/backend/src/vault/ssh-keys.controller.ts deleted file mode 100644 index 0a7627a..0000000 --- a/backend/src/vault/ssh-keys.controller.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { Controller, Get, Post, Put, Delete, Param, Body, Request, UseGuards, ParseIntPipe } from '@nestjs/common'; -import { JwtAuthGuard } from '../auth/jwt-auth.guard'; -import { SshKeysService } from './ssh-keys.service'; -import { CreateSshKeyDto } from './dto/create-ssh-key.dto'; -import { UpdateSshKeyDto } from './dto/update-ssh-key.dto'; - -@UseGuards(JwtAuthGuard) -@Controller('ssh-keys') -export class SshKeysController { - constructor(private sshKeys: SshKeysService) {} - - @Get() - findAll(@Request() req: any) { - return this.sshKeys.findAll(req.user.sub); - } - - @Get(':id') - findOne(@Request() req: any, @Param('id', ParseIntPipe) id: number) { - return this.sshKeys.findOne(id, req.user.sub); - } - - @Post() - create(@Request() req: any, @Body() dto: CreateSshKeyDto) { - return this.sshKeys.create(req.user.sub, dto); - } - - @Put(':id') - update(@Request() req: any, @Param('id', ParseIntPipe) id: number, @Body() dto: UpdateSshKeyDto) { - return this.sshKeys.update(id, req.user.sub, dto); - } - - @Delete(':id') - remove(@Request() req: any, @Param('id', ParseIntPipe) id: number) { - return this.sshKeys.remove(id, req.user.sub); - } -} diff --git a/backend/src/vault/ssh-keys.service.spec.ts b/backend/src/vault/ssh-keys.service.spec.ts deleted file mode 100644 index 3882c75..0000000 --- a/backend/src/vault/ssh-keys.service.spec.ts +++ /dev/null @@ -1,147 +0,0 @@ -// backend/src/vault/ssh-keys.service.spec.ts -import { Test, TestingModule } from '@nestjs/testing'; -import { SshKeysService } from './ssh-keys.service'; -import { PrismaService } from '../prisma/prisma.service'; -import { EncryptionService } from './encryption.service'; -import { NotFoundException, ForbiddenException } from '@nestjs/common'; - -describe('SshKeysService', () => { - let service: SshKeysService; - let prisma: any; - let encryption: any; - - beforeEach(async () => { - prisma = { - sshKey: { - findMany: jest.fn(), - findUnique: jest.fn(), - create: jest.fn(), - update: jest.fn(), - delete: jest.fn(), - }, - }; - encryption = { - encrypt: jest.fn().mockResolvedValue('v2:mock:encrypted'), - }; - - const module: TestingModule = await Test.createTestingModule({ - providers: [ - SshKeysService, - { provide: PrismaService, useValue: prisma }, - { provide: EncryptionService, useValue: encryption }, - ], - }).compile(); - - service = module.get(SshKeysService); - }); - - describe('findAll', () => { - it('should return keys without private key data', async () => { - prisma.sshKey.findMany.mockResolvedValue([{ id: 1, name: 'key1' }]); - await service.findAll(1); - const call = prisma.sshKey.findMany.mock.calls[0][0]; - expect(call.select.encryptedPrivateKey).toBeUndefined(); - expect(call.select.passphraseEncrypted).toBeUndefined(); - }); - }); - - describe('findOne', () => { - it('should return key for owner without encrypted private key field', async () => { - prisma.sshKey.findUnique.mockResolvedValue({ - id: 1, - userId: 1, - name: 'key1', - keyType: 'ed25519', - fingerprint: 'SHA256:abc', - publicKey: null, - credentials: [], - createdAt: new Date(), - encryptedPrivateKey: 'v2:secret', - }); - const result = await service.findOne(1, 1); - expect(result.name).toBe('key1'); - // findOne strips encryptedPrivateKey from the returned object - expect((result as any).encryptedPrivateKey).toBeUndefined(); - }); - - it('should throw ForbiddenException for non-owner', async () => { - prisma.sshKey.findUnique.mockResolvedValue({ id: 1, userId: 2, name: 'key1', credentials: [] }); - await expect(service.findOne(1, 1)).rejects.toThrow(ForbiddenException); - }); - - it('should throw NotFoundException for missing key', async () => { - prisma.sshKey.findUnique.mockResolvedValue(null); - await expect(service.findOne(99, 1)).rejects.toThrow(NotFoundException); - }); - }); - - describe('create', () => { - it('should encrypt both private key and passphrase', async () => { - prisma.sshKey.create.mockResolvedValue({ id: 1 }); - await service.create(1, { - name: 'my-key', - privateKey: '-----BEGIN OPENSSH PRIVATE KEY-----\ndata\n-----END OPENSSH PRIVATE KEY-----', - passphrase: 'secret', - }); - expect(encryption.encrypt).toHaveBeenCalledTimes(2); - }); - - it('should detect RSA key type', async () => { - prisma.sshKey.create.mockResolvedValue({ id: 1 }); - await service.create(1, { - name: 'rsa-key', - privateKey: '-----BEGIN RSA PRIVATE KEY-----\ndata\n-----END RSA PRIVATE KEY-----', - }); - expect(prisma.sshKey.create).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ keyType: 'rsa' }), - }), - ); - }); - - it('should detect ed25519 key type from OPENSSH header', async () => { - prisma.sshKey.create.mockResolvedValue({ id: 1 }); - await service.create(1, { - name: 'ed25519-key', - privateKey: '-----BEGIN OPENSSH PRIVATE KEY-----\ndata\n-----END OPENSSH PRIVATE KEY-----', - }); - expect(prisma.sshKey.create).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ keyType: 'ed25519' }), - }), - ); - }); - - it('should detect ECDSA key type', async () => { - prisma.sshKey.create.mockResolvedValue({ id: 1 }); - await service.create(1, { - name: 'ecdsa-key', - privateKey: '-----BEGIN EC PRIVATE KEY-----\ndata\n-----END EC PRIVATE KEY-----', - }); - expect(prisma.sshKey.create).toHaveBeenCalledWith( - expect.objectContaining({ - data: expect.objectContaining({ keyType: 'ecdsa' }), - }), - ); - }); - }); - - describe('remove', () => { - it('should delete owned key', async () => { - prisma.sshKey.findUnique.mockResolvedValue({ id: 1, userId: 1 }); - prisma.sshKey.delete.mockResolvedValue({ id: 1 }); - await service.remove(1, 1); - expect(prisma.sshKey.delete).toHaveBeenCalledWith({ where: { id: 1 } }); - }); - - it('should throw ForbiddenException for non-owner', async () => { - prisma.sshKey.findUnique.mockResolvedValue({ id: 1, userId: 2 }); - await expect(service.remove(1, 1)).rejects.toThrow(ForbiddenException); - }); - - it('should throw NotFoundException for missing key', async () => { - prisma.sshKey.findUnique.mockResolvedValue(null); - await expect(service.remove(99, 1)).rejects.toThrow(NotFoundException); - }); - }); -}); diff --git a/backend/src/vault/ssh-keys.service.ts b/backend/src/vault/ssh-keys.service.ts deleted file mode 100644 index 3384b0c..0000000 --- a/backend/src/vault/ssh-keys.service.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { Injectable, NotFoundException, ForbiddenException } from '@nestjs/common'; -import { PrismaService } from '../prisma/prisma.service'; -import { EncryptionService } from './encryption.service'; -import { CreateSshKeyDto } from './dto/create-ssh-key.dto'; -import { UpdateSshKeyDto } from './dto/update-ssh-key.dto'; -import { createHash } from 'crypto'; - -@Injectable() -export class SshKeysService { - constructor( - private prisma: PrismaService, - private encryption: EncryptionService, - ) {} - - findAll(userId: number) { - return this.prisma.sshKey.findMany({ - where: { userId }, - select: { id: true, name: true, keyType: true, fingerprint: true, publicKey: true, createdAt: true }, - orderBy: { name: 'asc' }, - }); - } - - async findOne(id: number, userId?: number) { - const key = await this.prisma.sshKey.findUnique({ - where: { id }, - include: { credentials: { select: { id: true, name: true } } }, - }); - if (!key) throw new NotFoundException(`SSH key ${id} not found`); - if (userId !== undefined && key.userId !== userId) { - throw new ForbiddenException('Access denied'); - } - // Never return encrypted private key over API - return { - id: key.id, - name: key.name, - keyType: key.keyType, - fingerprint: key.fingerprint, - publicKey: key.publicKey, - credentials: key.credentials, - createdAt: key.createdAt, - }; - } - - async create(userId: number, dto: CreateSshKeyDto) { - // Detect key type from private key content - const keyType = this.detectKeyType(dto.privateKey); - - // Generate fingerprint from public key if provided, else from private key - const fingerprint = this.generateFingerprint(dto.publicKey || dto.privateKey); - - // Encrypt sensitive data with Argon2id-derived key (v2) - const encryptedPrivateKey = await this.encryption.encrypt(dto.privateKey); - const passphraseEncrypted = dto.passphrase - ? await this.encryption.encrypt(dto.passphrase) - : null; - - return this.prisma.sshKey.create({ - data: { - name: dto.name, - keyType, - fingerprint, - publicKey: dto.publicKey || null, - userId, - encryptedPrivateKey, - passphraseEncrypted, - }, - }); - } - - async update(id: number, userId: number, dto: UpdateSshKeyDto) { - const key = await this.prisma.sshKey.findUnique({ where: { id } }); - if (!key) throw new NotFoundException(`SSH key ${id} not found`); - if (key.userId !== userId) throw new ForbiddenException('Access denied'); - - const data: any = {}; - if (dto.name) data.name = dto.name; - if (dto.passphrase !== undefined) { - data.passphraseEncrypted = dto.passphrase - ? await this.encryption.encrypt(dto.passphrase) - : null; - } - return this.prisma.sshKey.update({ where: { id }, data }); - } - - async remove(id: number, userId: number) { - const key = await this.prisma.sshKey.findUnique({ where: { id } }); - if (!key) throw new NotFoundException(`SSH key ${id} not found`); - if (key.userId !== userId) throw new ForbiddenException('Access denied'); - return this.prisma.sshKey.delete({ where: { id } }); - } - - private detectKeyType(privateKey: string): string { - if (privateKey.includes('RSA')) return 'rsa'; - if (privateKey.includes('EC')) return 'ecdsa'; - if (privateKey.includes('OPENSSH')) return 'ed25519'; // OpenSSH format, likely ed25519 - return 'unknown'; - } - - private generateFingerprint(keyContent: string): string { - try { - const hash = createHash('sha256').update(keyContent.trim()).digest('base64'); - return `SHA256:${hash}`; - } catch { - return 'unknown'; - } - } -} diff --git a/backend/src/vault/vault.module.ts b/backend/src/vault/vault.module.ts deleted file mode 100644 index 656bd6b..0000000 --- a/backend/src/vault/vault.module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Module } from '@nestjs/common'; -import { EncryptionService } from './encryption.service'; -import { CredentialsService } from './credentials.service'; -import { CredentialsController } from './credentials.controller'; -import { SshKeysService } from './ssh-keys.service'; -import { SshKeysController } from './ssh-keys.controller'; - -@Module({ - providers: [EncryptionService, CredentialsService, SshKeysService], - controllers: [CredentialsController, SshKeysController], - exports: [EncryptionService, CredentialsService, SshKeysService], -}) -export class VaultModule {} diff --git a/backend/tsconfig.build.json b/backend/tsconfig.build.json deleted file mode 100644 index cc01185..0000000 --- a/backend/tsconfig.build.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*.spec.ts"] -} diff --git a/backend/tsconfig.json b/backend/tsconfig.json deleted file mode 100644 index c01dd18..0000000 --- a/backend/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "compilerOptions": { - "module": "commonjs", - "declaration": true, - "removeComments": true, - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "allowSyntheticDefaultImports": true, - "target": "ES2021", - "sourceMap": true, - "outDir": "./dist", - "baseUrl": "./", - "incremental": true, - "skipLibCheck": true, - "strictNullChecks": true, - "noImplicitAny": true, - "strictBindCallApply": true, - "forceConsistentCasingInFileNames": true, - "noFallthroughCasesInSwitch": true, - "paths": { "@/*": ["src/*"] } - } -} diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index b4f63c1..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,36 +0,0 @@ -services: - app: - build: . - ports: ["4210:3000"] - environment: - DATABASE_URL: postgresql://wraith:${DB_PASSWORD}@postgres:5432/wraith - JWT_SECRET: ${JWT_SECRET} - ENCRYPTION_KEY: ${ENCRYPTION_KEY} - GUACD_HOST: guacd - GUACD_PORT: "4822" - depends_on: - postgres: - condition: service_healthy - guacd: - condition: service_started - restart: unless-stopped - - guacd: - image: guacamole/guacd - restart: always - - postgres: - image: postgres:16-alpine - volumes: [pgdata:/var/lib/postgresql/data] - environment: - POSTGRES_DB: wraith - POSTGRES_USER: wraith - POSTGRES_PASSWORD: ${DB_PASSWORD} - healthcheck: - test: ["CMD-SHELL", "pg_isready -U wraith"] - interval: 5s - timeout: 3s - retries: 5 - -volumes: - pgdata: diff --git a/docs/plans/2026-03-14-test-suite-buildout.md b/docs/plans/2026-03-14-test-suite-buildout.md new file mode 100644 index 0000000..8fbcec0 --- /dev/null +++ b/docs/plans/2026-03-14-test-suite-buildout.md @@ -0,0 +1,1371 @@ +# Wraith Remote — Test Suite Build-Out Implementation Plan + +> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Build a full service-layer test suite (~96 tests) covering all backend services, guards, and controller logic plus frontend stores, composables, and middleware. + +**Architecture:** Backend uses NestJS Testing Module with Jest. Frontend uses Vitest + happy-dom with mocked Nuxt auto-imports ($fetch, navigateTo). All tests are unit tests with mocked dependencies — no real database or network calls. + +**Tech Stack:** Jest 29 (backend, already configured), Vitest (frontend, new), @nestjs/testing, @vue/test-utils, @pinia/testing, happy-dom + +--- + +## File Structure + +### Backend (existing Jest infrastructure, new test files) + +| File | Purpose | +|------|---------| +| `backend/src/__mocks__/prisma.mock.ts` | Shared PrismaService mock factory | +| `backend/src/vault/encryption.service.spec.ts` | Encryption round-trip, v1/v2, upgrade | +| `backend/src/vault/credentials.service.spec.ts` | Credential CRUD, decryptForConnection | +| `backend/src/vault/ssh-keys.service.spec.ts` | SSH key CRUD, key type detection | +| `backend/src/auth/auth.service.spec.ts` | Login, password hashing, TOTP, admin CRUD | +| `backend/src/auth/auth.controller.spec.ts` | Cookie auth, WS tickets, route wiring | +| `backend/src/auth/jwt-auth.guard.spec.ts` | JWT guard pass/reject | +| `backend/src/auth/admin.guard.spec.ts` | Admin role enforcement | +| `backend/src/auth/ws-auth.guard.spec.ts` | Cookie, ticket, legacy token auth | + +### Frontend (new Vitest infrastructure + test files) + +| File | Purpose | +|------|---------| +| `frontend/vitest.config.ts` | Vitest config with happy-dom | +| `frontend/tests/setup.ts` | Global mocks for $fetch, navigateTo | +| `frontend/tests/stores/auth.store.spec.ts` | Auth store login/logout/profile | +| `frontend/tests/stores/connection.store.spec.ts` | Host/group CRUD | +| `frontend/tests/composables/useVault.spec.ts` | Vault API calls | +| `frontend/tests/middleware/admin.spec.ts` | Admin route guard | + +--- + +## Chunk 1: Backend Test Infrastructure + Encryption Tests + +### Task 1: Create Prisma Mock Factory + +**Files:** +- Create: `backend/src/__mocks__/prisma.mock.ts` + +- [ ] **Step 1: Create the shared mock** + +```typescript +// backend/src/__mocks__/prisma.mock.ts +export const mockPrismaService = { + user: { + findUnique: jest.fn(), + findFirst: jest.fn(), + findMany: jest.fn(), + create: jest.fn(), + update: jest.fn(), + delete: jest.fn(), + }, + credential: { + findUnique: jest.fn(), + findMany: jest.fn(), + create: jest.fn(), + update: jest.fn(), + delete: jest.fn(), + }, + sshKey: { + findUnique: jest.fn(), + findMany: jest.fn(), + create: jest.fn(), + update: jest.fn(), + delete: jest.fn(), + }, + connectionLog: { + create: jest.fn(), + updateMany: jest.fn(), + }, +}; + +export function createMockPrisma() { + // Deep clone to prevent test bleed + const mock = JSON.parse(JSON.stringify(mockPrismaService)); + // Re-attach jest.fn() since JSON.parse loses functions + for (const model of Object.keys(mock)) { + for (const method of Object.keys(mock[model])) { + mock[model][method] = jest.fn(); + } + } + return mock; +} +``` + +- [ ] **Step 2: Commit** + +```bash +git add backend/src/__mocks__/prisma.mock.ts +git commit -m "test: add shared Prisma mock factory" +``` + +### Task 2: Encryption Service Tests + +**Files:** +- Create: `backend/src/vault/encryption.service.spec.ts` +- Reference: `backend/src/vault/encryption.service.ts` + +- [ ] **Step 1: Write encryption tests** + +```typescript +// backend/src/vault/encryption.service.spec.ts +import { EncryptionService } from './encryption.service'; + +// Set test encryption key before importing service +process.env.ENCRYPTION_KEY = 'a'.repeat(64); // 32 bytes hex + +describe('EncryptionService', () => { + let service: EncryptionService; + + beforeAll(async () => { + service = new EncryptionService(); + await service.onModuleInit(); + }); + + describe('encrypt/decrypt round-trip', () => { + it('should encrypt and decrypt a string', async () => { + const plaintext = 'my-secret-password'; + const encrypted = await service.encrypt(plaintext); + expect(encrypted).toMatch(/^v2:/); + const decrypted = await service.decrypt(encrypted); + expect(decrypted).toBe(plaintext); + }); + + it('should produce different ciphertexts for the same plaintext', async () => { + const plaintext = 'same-input'; + const a = await service.encrypt(plaintext); + const b = await service.encrypt(plaintext); + expect(a).not.toBe(b); // Different salts + IVs + }); + + it('should handle empty string', async () => { + const encrypted = await service.encrypt(''); + const decrypted = await service.decrypt(encrypted); + expect(decrypted).toBe(''); + }); + + it('should handle unicode', async () => { + const plaintext = '密码 пароль 🔐'; + const encrypted = await service.encrypt(plaintext); + const decrypted = await service.decrypt(encrypted); + expect(decrypted).toBe(plaintext); + }); + }); + + describe('v2 format', () => { + it('should produce v2-prefixed ciphertext', async () => { + const encrypted = await service.encrypt('test'); + const parts = encrypted.split(':'); + expect(parts[0]).toBe('v2'); + expect(parts).toHaveLength(5); // v2:salt:iv:authTag:ciphertext + }); + }); + + describe('isV1', () => { + it('should detect v1 format', () => { + expect(service.isV1('v1:abc:def:ghi')).toBe(true); + }); + + it('should not detect v2 as v1', () => { + expect(service.isV1('v2:abc:def:ghi:jkl')).toBe(false); + }); + }); + + describe('upgradeToV2', () => { + it('should return null for v2 ciphertext', async () => { + const v2 = await service.encrypt('test'); + const result = await service.upgradeToV2(v2); + expect(result).toBeNull(); + }); + }); + + describe('error handling', () => { + it('should throw on unknown version', async () => { + await expect(service.decrypt('v3:bad:data')).rejects.toThrow('Unknown encryption version'); + }); + + it('should throw on tampered ciphertext', async () => { + const encrypted = await service.encrypt('test'); + const tampered = encrypted.slice(0, -4) + 'dead'; + await expect(service.decrypt(tampered)).rejects.toThrow(); + }); + }); +}); +``` + +- [ ] **Step 2: Run tests to verify they pass** + +Run: `cd backend && npx jest src/vault/encryption.service.spec.ts --verbose` +Expected: 8 tests PASS + +- [ ] **Step 3: Commit** + +```bash +git add backend/src/vault/encryption.service.spec.ts +git commit -m "test: encryption service — round-trip, v1/v2 format, upgrade, error handling" +``` + +--- + +## Chunk 2: Backend Vault Service Tests + +### Task 3: Credentials Service Tests + +**Files:** +- Create: `backend/src/vault/credentials.service.spec.ts` +- Reference: `backend/src/vault/credentials.service.ts` + +- [ ] **Step 1: Write credentials service tests** + +```typescript +// backend/src/vault/credentials.service.spec.ts +import { Test, TestingModule } from '@nestjs/testing'; +import { CredentialsService } from './credentials.service'; +import { PrismaService } from '../prisma/prisma.service'; +import { EncryptionService } from './encryption.service'; +import { NotFoundException, ForbiddenException } from '@nestjs/common'; + +describe('CredentialsService', () => { + let service: CredentialsService; + let prisma: any; + let encryption: any; + + beforeEach(async () => { + prisma = { + credential: { + findMany: jest.fn(), + findUnique: jest.fn(), + create: jest.fn(), + update: jest.fn(), + delete: jest.fn(), + }, + }; + encryption = { + encrypt: jest.fn().mockResolvedValue('v2:mock:encrypted'), + decrypt: jest.fn().mockResolvedValue('decrypted-password'), + }; + + const module: TestingModule = await Test.createTestingModule({ + providers: [ + CredentialsService, + { provide: PrismaService, useValue: prisma }, + { provide: EncryptionService, useValue: encryption }, + ], + }).compile(); + + service = module.get(CredentialsService); + }); + + describe('findAll', () => { + it('should return credentials without encryptedValue', async () => { + prisma.credential.findMany.mockResolvedValue([{ id: 1, name: 'test' }]); + const result = await service.findAll(1); + expect(prisma.credential.findMany).toHaveBeenCalledWith( + expect.objectContaining({ + where: { userId: 1 }, + select: expect.objectContaining({ id: true, name: true }), + }), + ); + // Verify encryptedValue is NOT in select + const call = prisma.credential.findMany.mock.calls[0][0]; + expect(call.select.encryptedValue).toBeUndefined(); + }); + }); + + describe('findOne', () => { + it('should return credential for owner', async () => { + prisma.credential.findUnique.mockResolvedValue({ id: 1, userId: 1, name: 'cred' }); + const result = await service.findOne(1, 1); + expect(result.name).toBe('cred'); + }); + + it('should throw ForbiddenException for non-owner', async () => { + prisma.credential.findUnique.mockResolvedValue({ id: 1, userId: 2, name: 'cred' }); + await expect(service.findOne(1, 1)).rejects.toThrow(ForbiddenException); + }); + + it('should throw NotFoundException for missing credential', async () => { + prisma.credential.findUnique.mockResolvedValue(null); + await expect(service.findOne(99, 1)).rejects.toThrow(NotFoundException); + }); + }); + + describe('create', () => { + it('should encrypt password before storage', async () => { + prisma.credential.create.mockResolvedValue({ id: 1 }); + await service.create(1, { name: 'test', username: 'admin', password: 'secret', type: 'password' as any }); + expect(encryption.encrypt).toHaveBeenCalledWith('secret'); + expect(prisma.credential.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ encryptedValue: 'v2:mock:encrypted' }), + }), + ); + }); + }); + + describe('decryptForConnection', () => { + it('should decrypt password credential', async () => { + prisma.credential.findUnique.mockResolvedValue({ + id: 1, username: 'admin', domain: null, + encryptedValue: 'v2:encrypted', sshKey: null, sshKeyId: null, + }); + const result = await service.decryptForConnection(1); + expect(result.password).toBe('decrypted-password'); + expect(result.username).toBe('admin'); + }); + + it('should decrypt SSH key credential', async () => { + prisma.credential.findUnique.mockResolvedValue({ + id: 1, username: 'root', domain: null, encryptedValue: null, + sshKey: { encryptedPrivateKey: 'v2:key', passphraseEncrypted: 'v2:pass' }, + sshKeyId: 5, + }); + encryption.decrypt.mockResolvedValueOnce('private-key-content'); + encryption.decrypt.mockResolvedValueOnce('passphrase'); + const result = await service.decryptForConnection(1); + expect(result.sshKey).toEqual({ privateKey: 'private-key-content', passphrase: 'passphrase' }); + }); + + it('should throw for orphaned SSH key reference', async () => { + prisma.credential.findUnique.mockResolvedValue({ + id: 1, name: 'orphan', username: 'root', domain: null, + encryptedValue: null, sshKey: null, sshKeyId: 99, + }); + await expect(service.decryptForConnection(1)).rejects.toThrow(NotFoundException); + }); + + it('should throw for credential with no auth method', async () => { + prisma.credential.findUnique.mockResolvedValue({ + id: 1, name: 'empty', username: 'root', domain: null, + encryptedValue: null, sshKey: null, sshKeyId: null, + }); + await expect(service.decryptForConnection(1)).rejects.toThrow(NotFoundException); + }); + }); + + describe('remove', () => { + it('should delete owned credential', async () => { + prisma.credential.findUnique.mockResolvedValue({ id: 1, userId: 1 }); + prisma.credential.delete.mockResolvedValue({ id: 1 }); + await service.remove(1, 1); + expect(prisma.credential.delete).toHaveBeenCalledWith({ where: { id: 1 } }); + }); + }); +}); +``` + +- [ ] **Step 2: Run tests** + +Run: `cd backend && npx jest src/vault/credentials.service.spec.ts --verbose` +Expected: 10 tests PASS + +- [ ] **Step 3: Commit** + +```bash +git add backend/src/vault/credentials.service.spec.ts +git commit -m "test: credentials service — CRUD, ownership, decryptForConnection" +``` + +### Task 4: SSH Keys Service Tests + +**Files:** +- Create: `backend/src/vault/ssh-keys.service.spec.ts` +- Reference: `backend/src/vault/ssh-keys.service.ts` + +- [ ] **Step 1: Write SSH keys tests** + +```typescript +// backend/src/vault/ssh-keys.service.spec.ts +import { Test, TestingModule } from '@nestjs/testing'; +import { SshKeysService } from './ssh-keys.service'; +import { PrismaService } from '../prisma/prisma.service'; +import { EncryptionService } from './encryption.service'; +import { NotFoundException, ForbiddenException } from '@nestjs/common'; + +describe('SshKeysService', () => { + let service: SshKeysService; + let prisma: any; + let encryption: any; + + beforeEach(async () => { + prisma = { + sshKey: { + findMany: jest.fn(), + findUnique: jest.fn(), + create: jest.fn(), + update: jest.fn(), + delete: jest.fn(), + }, + }; + encryption = { + encrypt: jest.fn().mockResolvedValue('v2:mock:encrypted'), + }; + + const module: TestingModule = await Test.createTestingModule({ + providers: [ + SshKeysService, + { provide: PrismaService, useValue: prisma }, + { provide: EncryptionService, useValue: encryption }, + ], + }).compile(); + + service = module.get(SshKeysService); + }); + + describe('findAll', () => { + it('should return keys without private key data', async () => { + prisma.sshKey.findMany.mockResolvedValue([{ id: 1, name: 'key1' }]); + await service.findAll(1); + const call = prisma.sshKey.findMany.mock.calls[0][0]; + expect(call.select.encryptedPrivateKey).toBeUndefined(); + expect(call.select.passphraseEncrypted).toBeUndefined(); + }); + }); + + describe('findOne', () => { + it('should return key for owner', async () => { + prisma.sshKey.findUnique.mockResolvedValue({ id: 1, userId: 1, name: 'key1', keyType: 'ed25519' }); + const result = await service.findOne(1, 1); + expect(result.name).toBe('key1'); + // Should not include encrypted private key + expect((result as any).encryptedPrivateKey).toBeUndefined(); + }); + + it('should throw ForbiddenException for non-owner', async () => { + prisma.sshKey.findUnique.mockResolvedValue({ id: 1, userId: 2 }); + await expect(service.findOne(1, 1)).rejects.toThrow(ForbiddenException); + }); + }); + + describe('create', () => { + it('should encrypt private key and passphrase', async () => { + prisma.sshKey.create.mockResolvedValue({ id: 1 }); + await service.create(1, { + name: 'my-key', + privateKey: '-----BEGIN OPENSSH PRIVATE KEY-----\ndata\n-----END OPENSSH PRIVATE KEY-----', + passphrase: 'secret', + }); + expect(encryption.encrypt).toHaveBeenCalledTimes(2); + }); + + it('should detect RSA key type', async () => { + prisma.sshKey.create.mockResolvedValue({ id: 1 }); + await service.create(1, { + name: 'rsa-key', + privateKey: '-----BEGIN RSA PRIVATE KEY-----\ndata\n-----END RSA PRIVATE KEY-----', + }); + expect(prisma.sshKey.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ keyType: 'rsa' }), + }), + ); + }); + + it('should detect ed25519 key type from OPENSSH format', async () => { + prisma.sshKey.create.mockResolvedValue({ id: 1 }); + await service.create(1, { + name: 'ed25519-key', + privateKey: '-----BEGIN OPENSSH PRIVATE KEY-----\ndata\n-----END OPENSSH PRIVATE KEY-----', + }); + expect(prisma.sshKey.create).toHaveBeenCalledWith( + expect.objectContaining({ + data: expect.objectContaining({ keyType: 'ed25519' }), + }), + ); + }); + }); + + describe('remove', () => { + it('should delete owned key', async () => { + prisma.sshKey.findUnique.mockResolvedValue({ id: 1, userId: 1 }); + prisma.sshKey.delete.mockResolvedValue({ id: 1 }); + await service.remove(1, 1); + expect(prisma.sshKey.delete).toHaveBeenCalled(); + }); + + it('should throw for non-owner', async () => { + prisma.sshKey.findUnique.mockResolvedValue({ id: 1, userId: 2 }); + await expect(service.remove(1, 1)).rejects.toThrow(ForbiddenException); + }); + }); +}); +``` + +- [ ] **Step 2: Run tests** + +Run: `cd backend && npx jest src/vault/ssh-keys.service.spec.ts --verbose` +Expected: 8 tests PASS + +- [ ] **Step 3: Commit** + +```bash +git add backend/src/vault/ssh-keys.service.spec.ts +git commit -m "test: SSH keys service — CRUD, ownership, key type detection, encryption" +``` + +--- + +## Chunk 3: Backend Auth Tests + +### Task 5: Auth Guards Tests + +**Files:** +- Create: `backend/src/auth/jwt-auth.guard.spec.ts` +- Create: `backend/src/auth/admin.guard.spec.ts` +- Create: `backend/src/auth/ws-auth.guard.spec.ts` + +- [ ] **Step 1: Write JWT auth guard test** + +```typescript +// backend/src/auth/jwt-auth.guard.spec.ts +import { JwtAuthGuard } from './jwt-auth.guard'; + +describe('JwtAuthGuard', () => { + it('should be defined', () => { + expect(new JwtAuthGuard()).toBeDefined(); + }); + + it('should extend AuthGuard("jwt")', () => { + const guard = new JwtAuthGuard(); + expect(guard).toBeInstanceOf(JwtAuthGuard); + }); +}); +``` + +- [ ] **Step 2: Write admin guard test** + +```typescript +// backend/src/auth/admin.guard.spec.ts +import { AdminGuard } from './admin.guard'; +import { ExecutionContext, ForbiddenException } from '@nestjs/common'; + +function createMockContext(user: any): ExecutionContext { + return { + switchToHttp: () => ({ + getRequest: () => ({ user }), + }), + } as any; +} + +describe('AdminGuard', () => { + const guard = new AdminGuard(); + + it('should allow admin role', () => { + const ctx = createMockContext({ role: 'admin' }); + expect(guard.canActivate(ctx)).toBe(true); + }); + + it('should reject non-admin role', () => { + const ctx = createMockContext({ role: 'user' }); + expect(() => guard.canActivate(ctx)).toThrow(ForbiddenException); + }); + + it('should reject missing user', () => { + const ctx = createMockContext(undefined); + expect(() => guard.canActivate(ctx)).toThrow(ForbiddenException); + }); +}); +``` + +- [ ] **Step 3: Write WS auth guard test** + +```typescript +// backend/src/auth/ws-auth.guard.spec.ts +import { WsAuthGuard } from './ws-auth.guard'; +import { JwtService } from '@nestjs/jwt'; +import { AuthController } from './auth.controller'; + +describe('WsAuthGuard', () => { + let guard: WsAuthGuard; + let jwt: any; + + beforeEach(() => { + jwt = { + verify: jest.fn().mockReturnValue({ sub: 1, email: 'test@test.com' }), + }; + guard = new WsAuthGuard(jwt as any); + }); + + it('should authenticate via cookie', () => { + const req = { headers: { cookie: 'wraith_token=valid-jwt; other=stuff' } }; + const result = guard.validateClient({}, req); + expect(jwt.verify).toHaveBeenCalledWith('valid-jwt'); + expect(result).toEqual({ sub: 1, email: 'test@test.com' }); + }); + + it('should authenticate via WS ticket', () => { + const originalConsume = AuthController.consumeWsTicket; + AuthController.consumeWsTicket = jest.fn().mockReturnValue({ sub: 1, email: 'test@test.com', role: 'admin' }); + const req = { url: '/api/ws/terminal?ticket=abc123', headers: {} }; + const result = guard.validateClient({}, req); + expect(result).toEqual({ sub: 1, email: 'test@test.com' }); + AuthController.consumeWsTicket = originalConsume; + }); + + it('should fall back to legacy URL token', () => { + const req = { url: '/api/ws/terminal?token=legacy-jwt', headers: {} }; + guard.validateClient({}, req); + expect(jwt.verify).toHaveBeenCalledWith('legacy-jwt'); + }); + + it('should return null for no credentials', () => { + const req = { url: '/api/ws/terminal', headers: {} }; + const result = guard.validateClient({}, req); + expect(result).toBeNull(); + }); + + it('should return null for invalid JWT', () => { + jwt.verify.mockImplementation(() => { throw new Error('invalid'); }); + const req = { headers: { cookie: 'wraith_token=bad-jwt' } }; + const result = guard.validateClient({}, req); + expect(result).toBeNull(); + }); +}); +``` + +- [ ] **Step 4: Run all guard tests** + +Run: `cd backend && npx jest src/auth/*.guard.spec.ts --verbose` +Expected: 10 tests PASS + +- [ ] **Step 5: Commit** + +```bash +git add backend/src/auth/jwt-auth.guard.spec.ts backend/src/auth/admin.guard.spec.ts backend/src/auth/ws-auth.guard.spec.ts +git commit -m "test: auth guards — JWT, admin, WS (cookie, ticket, legacy token)" +``` + +### Task 6: Auth Service Tests + +**Files:** +- Create: `backend/src/auth/auth.service.spec.ts` +- Reference: `backend/src/auth/auth.service.ts` + +- [ ] **Step 1: Write auth service tests** + +```typescript +// backend/src/auth/auth.service.spec.ts +import { Test, TestingModule } from '@nestjs/testing'; +import { AuthService } from './auth.service'; +import { PrismaService } from '../prisma/prisma.service'; +import { JwtService } from '@nestjs/jwt'; +import { EncryptionService } from '../vault/encryption.service'; +import { UnauthorizedException, BadRequestException, NotFoundException, ForbiddenException } from '@nestjs/common'; +import * as argon2 from 'argon2'; +import * as bcrypt from 'bcrypt'; + +describe('AuthService', () => { + let service: AuthService; + let prisma: any; + let jwt: any; + let encryption: any; + + beforeEach(async () => { + prisma = { + user: { + findUnique: jest.fn(), + findMany: jest.fn(), + create: jest.fn(), + update: jest.fn(), + delete: jest.fn(), + }, + }; + jwt = { sign: jest.fn().mockReturnValue('mock-jwt-token') }; + encryption = { + encrypt: jest.fn().mockResolvedValue('v2:encrypted'), + decrypt: jest.fn().mockResolvedValue('decrypted-secret'), + }; + + const module: TestingModule = await Test.createTestingModule({ + providers: [ + AuthService, + { provide: PrismaService, useValue: prisma }, + { provide: JwtService, useValue: jwt }, + { provide: EncryptionService, useValue: encryption }, + ], + }).compile(); + + service = module.get(AuthService); + await service.onModuleInit(); + }); + + describe('login', () => { + it('should return access_token and user for valid credentials', async () => { + const hash = await argon2.hash('correct-password', { type: argon2.argon2id }); + prisma.user.findUnique.mockResolvedValue({ + id: 1, email: 'test@test.com', passwordHash: hash, + displayName: 'Test', role: 'admin', totpEnabled: false, + }); + const result = await service.login('test@test.com', 'correct-password'); + expect(result).toHaveProperty('access_token', 'mock-jwt-token'); + expect(result).toHaveProperty('user'); + expect((result as any).user.email).toBe('test@test.com'); + }); + + it('should throw for wrong password', async () => { + const hash = await argon2.hash('correct', { type: argon2.argon2id }); + prisma.user.findUnique.mockResolvedValue({ + id: 1, email: 'test@test.com', passwordHash: hash, + totpEnabled: false, + }); + await expect(service.login('test@test.com', 'wrong')).rejects.toThrow(UnauthorizedException); + }); + + it('should throw for non-existent user (constant time)', async () => { + prisma.user.findUnique.mockResolvedValue(null); + await expect(service.login('nobody@test.com', 'pass')).rejects.toThrow(UnauthorizedException); + }); + + it('should auto-upgrade bcrypt hash to argon2id on login', async () => { + const bcryptHash = await bcrypt.hash('password', 10); + prisma.user.findUnique.mockResolvedValue({ + id: 1, email: 'legacy@test.com', passwordHash: bcryptHash, + displayName: 'Legacy', role: 'user', totpEnabled: false, + }); + prisma.user.update.mockResolvedValue({}); + await service.login('legacy@test.com', 'password'); + // Should have called update to upgrade the hash + expect(prisma.user.update).toHaveBeenCalledWith( + expect.objectContaining({ + where: { id: 1 }, + data: expect.objectContaining({ + passwordHash: expect.stringContaining('$argon2id$'), + }), + }), + ); + }); + + it('should return requires_totp when TOTP enabled but no code provided', async () => { + const hash = await argon2.hash('pass', { type: argon2.argon2id }); + prisma.user.findUnique.mockResolvedValue({ + id: 1, email: 'totp@test.com', passwordHash: hash, + totpEnabled: true, totpSecret: 'v2:encrypted-secret', + }); + const result = await service.login('totp@test.com', 'pass'); + expect(result).toEqual({ requires_totp: true }); + }); + }); + + describe('createUser', () => { + it('should hash password with argon2id', async () => { + prisma.user.findUnique.mockResolvedValue(null); // no existing user + prisma.user.create.mockResolvedValue({ id: 1, email: 'new@test.com', displayName: null, role: 'user' }); + await service.createUser({ email: 'new@test.com', password: 'StrongPass1!' }); + const createCall = prisma.user.create.mock.calls[0][0]; + expect(createCall.data.passwordHash).toMatch(/^\$argon2id\$/); + }); + + it('should throw for duplicate email', async () => { + prisma.user.findUnique.mockResolvedValue({ id: 1 }); + await expect(service.createUser({ email: 'dup@test.com', password: 'pass' })) + .rejects.toThrow(BadRequestException); + }); + }); + + describe('adminDeleteUser', () => { + it('should prevent self-deletion', async () => { + await expect(service.adminDeleteUser(1, 1)).rejects.toThrow(ForbiddenException); + }); + + it('should delete another user', async () => { + prisma.user.findUnique.mockResolvedValue({ id: 2 }); + prisma.user.delete.mockResolvedValue({ id: 2 }); + await service.adminDeleteUser(2, 1); + expect(prisma.user.delete).toHaveBeenCalledWith({ where: { id: 2 } }); + }); + }); + + describe('totpSetup', () => { + it('should encrypt TOTP secret before storage', async () => { + prisma.user.findUnique.mockResolvedValue({ id: 1, totpEnabled: false }); + prisma.user.update.mockResolvedValue({}); + const result = await service.totpSetup(1); + expect(encryption.encrypt).toHaveBeenCalled(); + expect(result).toHaveProperty('secret'); + expect(result).toHaveProperty('qrCode'); + }); + + it('should throw if TOTP already enabled', async () => { + prisma.user.findUnique.mockResolvedValue({ id: 1, totpEnabled: true }); + await expect(service.totpSetup(1)).rejects.toThrow(BadRequestException); + }); + }); + + describe('updateProfile', () => { + it('should require current password for password change', async () => { + prisma.user.findUnique.mockResolvedValue({ id: 1, passwordHash: 'hash' }); + await expect(service.updateProfile(1, { newPassword: 'new' })) + .rejects.toThrow(BadRequestException); + }); + }); +}); +``` + +- [ ] **Step 2: Run tests** + +Run: `cd backend && npx jest src/auth/auth.service.spec.ts --verbose` +Expected: ~12 tests PASS + +- [ ] **Step 3: Commit** + +```bash +git add backend/src/auth/auth.service.spec.ts +git commit -m "test: auth service — login, bcrypt migration, TOTP, admin CRUD, profile" +``` + +### Task 7: Auth Controller Tests + +**Files:** +- Create: `backend/src/auth/auth.controller.spec.ts` + +- [ ] **Step 1: Write auth controller tests** + +```typescript +// backend/src/auth/auth.controller.spec.ts +import { Test, TestingModule } from '@nestjs/testing'; +import { AuthController } from './auth.controller'; +import { AuthService } from './auth.service'; +import { JwtService } from '@nestjs/jwt'; + +describe('AuthController', () => { + let controller: AuthController; + let authService: any; + + beforeEach(async () => { + authService = { + login: jest.fn(), + getProfile: jest.fn(), + totpSetup: jest.fn(), + listUsers: jest.fn(), + }; + + const module: TestingModule = await Test.createTestingModule({ + controllers: [AuthController], + providers: [ + { provide: AuthService, useValue: authService }, + { provide: JwtService, useValue: { sign: jest.fn(), verify: jest.fn() } }, + ], + }).compile(); + + controller = module.get(AuthController); + }); + + describe('login', () => { + it('should set httpOnly cookie on successful login', async () => { + authService.login.mockResolvedValue({ + access_token: 'jwt-token', + user: { id: 1, email: 'test@test.com', role: 'admin' }, + }); + const res = { cookie: jest.fn() }; + const result = await controller.login({ email: 'test@test.com', password: 'pass' } as any, res); + expect(res.cookie).toHaveBeenCalledWith('wraith_token', 'jwt-token', expect.objectContaining({ + httpOnly: true, + sameSite: 'strict', + path: '/', + })); + expect(result).toEqual({ user: expect.objectContaining({ email: 'test@test.com' }) }); + // Token should NOT be in response body + expect(result).not.toHaveProperty('access_token'); + }); + + it('should pass through requires_totp without setting cookie', async () => { + authService.login.mockResolvedValue({ requires_totp: true }); + const res = { cookie: jest.fn() }; + const result = await controller.login({ email: 'test@test.com', password: 'pass' } as any, res); + expect(res.cookie).not.toHaveBeenCalled(); + expect(result).toEqual({ requires_totp: true }); + }); + }); + + describe('logout', () => { + it('should clear cookie', () => { + const res = { clearCookie: jest.fn() }; + const result = controller.logout(res); + expect(res.clearCookie).toHaveBeenCalledWith('wraith_token', { path: '/' }); + }); + }); + + describe('ws-ticket', () => { + it('should issue a ticket', () => { + const req = { user: { sub: 1, email: 'test@test.com', role: 'admin' } }; + const result = controller.issueWsTicket(req); + expect(result).toHaveProperty('ticket'); + expect(result.ticket).toHaveLength(64); // 32 bytes hex + }); + + it('should consume ticket exactly once', () => { + const req = { user: { sub: 1, email: 'test@test.com', role: 'admin' } }; + const { ticket } = controller.issueWsTicket(req); + const first = AuthController.consumeWsTicket(ticket); + expect(first).toEqual(expect.objectContaining({ sub: 1 })); + const second = AuthController.consumeWsTicket(ticket); + expect(second).toBeNull(); // Single-use + }); + }); +}); +``` + +- [ ] **Step 2: Run tests** + +Run: `cd backend && npx jest src/auth/auth.controller.spec.ts --verbose` +Expected: 6 tests PASS + +- [ ] **Step 3: Commit** + +```bash +git add backend/src/auth/auth.controller.spec.ts +git commit -m "test: auth controller — cookie login, logout, WS ticket issuance/consumption" +``` + +--- + +## Chunk 4: Frontend Test Infrastructure + Tests + +### Task 8: Frontend Test Infrastructure + +**Files:** +- Create: `frontend/vitest.config.ts` +- Create: `frontend/tests/setup.ts` +- Modify: `frontend/package.json` + +- [ ] **Step 1: Install Vitest dependencies** + +Run: `cd frontend && npm install --save-dev vitest @vue/test-utils @pinia/testing happy-dom` + +- [ ] **Step 2: Create Vitest config** + +```typescript +// frontend/vitest.config.ts +import { defineConfig } from 'vitest/config'; +import { resolve } from 'path'; + +export default defineConfig({ + test: { + environment: 'happy-dom', + globals: true, + setupFiles: ['./tests/setup.ts'], + include: ['tests/**/*.spec.ts'], + }, + resolve: { + alias: { + '~': resolve(__dirname, '.'), + '#imports': resolve(__dirname, '.nuxt/imports.d.ts'), + }, + }, +}); +``` + +- [ ] **Step 3: Create test setup with Nuxt auto-import mocks** + +```typescript +// frontend/tests/setup.ts +import { vi } from 'vitest'; + +// Mock Nuxt auto-imports +(globalThis as any).$fetch = vi.fn(); +(globalThis as any).navigateTo = vi.fn(); +(globalThis as any).defineNuxtRouteMiddleware = (fn: any) => fn; +(globalThis as any).defineNuxtPlugin = vi.fn(); +(globalThis as any).definePageMeta = vi.fn(); +(globalThis as any).useAuthStore = vi.fn(); +(globalThis as any).useSessionStore = vi.fn(); +(globalThis as any).useConnectionStore = vi.fn(); + +// Mock ref, computed, onMounted from Vue (Nuxt auto-imports these) +import { ref, computed, onMounted, watch } from 'vue'; +(globalThis as any).ref = ref; +(globalThis as any).computed = computed; +(globalThis as any).onMounted = onMounted; +(globalThis as any).watch = watch; +``` + +- [ ] **Step 4: Add test scripts to frontend package.json** + +Add to `frontend/package.json` scripts: +```json +"test": "vitest run", +"test:watch": "vitest", +"test:cov": "vitest run --coverage" +``` + +- [ ] **Step 5: Commit** + +```bash +git add frontend/vitest.config.ts frontend/tests/setup.ts frontend/package.json +git commit -m "test: frontend test infrastructure — Vitest, happy-dom, Nuxt auto-import mocks" +``` + +### Task 9: Auth Store Tests + +**Files:** +- Create: `frontend/tests/stores/auth.store.spec.ts` +- Reference: `frontend/stores/auth.store.ts` + +- [ ] **Step 1: Write auth store tests** + +```typescript +// frontend/tests/stores/auth.store.spec.ts +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { setActivePinia, createPinia } from 'pinia'; +import { useAuthStore } from '../../stores/auth.store'; + +describe('Auth Store', () => { + beforeEach(() => { + setActivePinia(createPinia()); + vi.resetAllMocks(); + }); + + describe('initial state', () => { + it('should start with no user', () => { + const auth = useAuthStore(); + expect(auth.user).toBeNull(); + expect(auth.isAuthenticated).toBe(false); + expect(auth.isAdmin).toBe(false); + }); + }); + + describe('login', () => { + it('should store user on successful login', async () => { + const mockUser = { id: 1, email: 'test@test.com', displayName: 'Test', role: 'admin' }; + (globalThis as any).$fetch = vi.fn().mockResolvedValue({ user: mockUser }); + + const auth = useAuthStore(); + await auth.login('test@test.com', 'password'); + expect(auth.user).toEqual(mockUser); + expect(auth.isAuthenticated).toBe(true); + expect(auth.isAdmin).toBe(true); + }); + + it('should return requires_totp without storing user', async () => { + (globalThis as any).$fetch = vi.fn().mockResolvedValue({ requires_totp: true }); + + const auth = useAuthStore(); + const result = await auth.login('test@test.com', 'password'); + expect(result).toEqual({ requires_totp: true }); + expect(auth.user).toBeNull(); + }); + + it('should not store token in state (httpOnly cookie)', async () => { + (globalThis as any).$fetch = vi.fn().mockResolvedValue({ + user: { id: 1, email: 'test@test.com', role: 'user' }, + }); + + const auth = useAuthStore(); + await auth.login('test@test.com', 'password'); + expect((auth as any).token).toBeUndefined(); + }); + }); + + describe('logout', () => { + it('should clear user and call logout API', async () => { + (globalThis as any).$fetch = vi.fn().mockResolvedValue({}); + (globalThis as any).navigateTo = vi.fn(); + + const auth = useAuthStore(); + auth.user = { id: 1, email: 'test@test.com', displayName: null, role: 'admin' }; + await auth.logout(); + expect(auth.user).toBeNull(); + expect(auth.isAuthenticated).toBe(false); + expect(navigateTo).toHaveBeenCalledWith('/login'); + }); + }); + + describe('fetchProfile', () => { + it('should populate user on success', async () => { + const mockUser = { id: 1, email: 'test@test.com', displayName: 'Test', role: 'user' }; + (globalThis as any).$fetch = vi.fn().mockResolvedValue(mockUser); + + const auth = useAuthStore(); + await auth.fetchProfile(); + expect(auth.user).toEqual(mockUser); + }); + + it('should clear user on failure', async () => { + (globalThis as any).$fetch = vi.fn().mockRejectedValue(new Error('401')); + + const auth = useAuthStore(); + auth.user = { id: 1, email: 'old@test.com', displayName: null, role: 'user' }; + await auth.fetchProfile(); + expect(auth.user).toBeNull(); + }); + }); + + describe('getWsTicket', () => { + it('should return ticket string', async () => { + (globalThis as any).$fetch = vi.fn().mockResolvedValue({ ticket: 'abc123' }); + + const auth = useAuthStore(); + const ticket = await auth.getWsTicket(); + expect(ticket).toBe('abc123'); + }); + }); + + describe('getters', () => { + it('isAdmin should be true for admin role', () => { + const auth = useAuthStore(); + auth.user = { id: 1, email: 'a@b.com', displayName: null, role: 'admin' }; + expect(auth.isAdmin).toBe(true); + }); + + it('isAdmin should be false for user role', () => { + const auth = useAuthStore(); + auth.user = { id: 1, email: 'a@b.com', displayName: null, role: 'user' }; + expect(auth.isAdmin).toBe(false); + }); + }); +}); +``` + +- [ ] **Step 2: Run tests** + +Run: `cd frontend && npx vitest run tests/stores/auth.store.spec.ts` +Expected: 10 tests PASS + +- [ ] **Step 3: Commit** + +```bash +git add frontend/tests/stores/auth.store.spec.ts +git commit -m "test: auth store — login, logout, fetchProfile, getWsTicket, getters" +``` + +### Task 10: Connection Store Tests + +**Files:** +- Create: `frontend/tests/stores/connection.store.spec.ts` + +- [ ] **Step 1: Write connection store tests** + +```typescript +// frontend/tests/stores/connection.store.spec.ts +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { setActivePinia, createPinia } from 'pinia'; +import { useConnectionStore } from '../../stores/connection.store'; + +describe('Connection Store', () => { + beforeEach(() => { + setActivePinia(createPinia()); + vi.resetAllMocks(); + (globalThis as any).$fetch = vi.fn().mockResolvedValue([]); + }); + + describe('fetchHosts', () => { + it('should populate hosts from API', async () => { + const mockHosts = [{ id: 1, name: 'Server 1' }]; + (globalThis as any).$fetch = vi.fn().mockResolvedValue(mockHosts); + + const store = useConnectionStore(); + await store.fetchHosts(); + expect(store.hosts).toEqual(mockHosts); + expect((globalThis as any).$fetch).toHaveBeenCalledWith('/api/hosts'); + }); + + it('should not send Authorization header', async () => { + const store = useConnectionStore(); + await store.fetchHosts(); + const callArgs = (globalThis as any).$fetch.mock.calls[0]; + expect(callArgs[1]?.headers?.Authorization).toBeUndefined(); + }); + }); + + describe('createHost', () => { + it('should POST and refresh hosts', async () => { + (globalThis as any).$fetch = vi.fn() + .mockResolvedValueOnce({ id: 1, name: 'New' }) // create + .mockResolvedValueOnce([]); // fetchHosts + + const store = useConnectionStore(); + await store.createHost({ name: 'New', hostname: '10.0.0.1', port: 22 } as any); + expect((globalThis as any).$fetch).toHaveBeenCalledWith('/api/hosts', expect.objectContaining({ method: 'POST' })); + }); + }); + + describe('deleteHost', () => { + it('should DELETE and refresh hosts', async () => { + (globalThis as any).$fetch = vi.fn() + .mockResolvedValueOnce({}) // delete + .mockResolvedValueOnce([]); // fetchHosts + + const store = useConnectionStore(); + await store.deleteHost(1); + expect((globalThis as any).$fetch).toHaveBeenCalledWith('/api/hosts/1', expect.objectContaining({ method: 'DELETE' })); + }); + }); + + describe('group CRUD', () => { + it('should create group and refresh tree', async () => { + (globalThis as any).$fetch = vi.fn().mockResolvedValue([]); + const store = useConnectionStore(); + await store.createGroup({ name: 'Production' }); + expect((globalThis as any).$fetch).toHaveBeenCalledWith('/api/groups', expect.objectContaining({ + method: 'POST', + body: { name: 'Production' }, + })); + }); + }); +}); +``` + +- [ ] **Step 2: Run tests** + +Run: `cd frontend && npx vitest run tests/stores/connection.store.spec.ts` +Expected: 5 tests PASS + +- [ ] **Step 3: Commit** + +```bash +git add frontend/tests/stores/connection.store.spec.ts +git commit -m "test: connection store — host/group CRUD, no auth headers" +``` + +### Task 11: Vault Composable + Admin Middleware Tests + +**Files:** +- Create: `frontend/tests/composables/useVault.spec.ts` +- Create: `frontend/tests/middleware/admin.spec.ts` + +- [ ] **Step 1: Write vault composable tests** + +```typescript +// frontend/tests/composables/useVault.spec.ts +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { useVault } from '../../composables/useVault'; + +describe('useVault', () => { + beforeEach(() => { + vi.resetAllMocks(); + (globalThis as any).$fetch = vi.fn().mockResolvedValue([]); + }); + + it('should list keys without auth header', async () => { + const { listKeys } = useVault(); + await listKeys(); + expect((globalThis as any).$fetch).toHaveBeenCalledWith('/api/ssh-keys'); + const callArgs = (globalThis as any).$fetch.mock.calls[0]; + expect(callArgs[1]?.headers?.Authorization).toBeUndefined(); + }); + + it('should list credentials without auth header', async () => { + const { listCredentials } = useVault(); + await listCredentials(); + expect((globalThis as any).$fetch).toHaveBeenCalledWith('/api/credentials'); + }); + + it('should import key via POST', async () => { + const { importKey } = useVault(); + await importKey({ name: 'key', privateKey: 'pem-data' }); + expect((globalThis as any).$fetch).toHaveBeenCalledWith('/api/ssh-keys', expect.objectContaining({ method: 'POST' })); + }); + + it('should delete key via DELETE', async () => { + const { deleteKey } = useVault(); + await deleteKey(5); + expect((globalThis as any).$fetch).toHaveBeenCalledWith('/api/ssh-keys/5', expect.objectContaining({ method: 'DELETE' })); + }); + + it('should create credential via POST', async () => { + const { createCredential } = useVault(); + await createCredential({ name: 'cred', username: 'admin' }); + expect((globalThis as any).$fetch).toHaveBeenCalledWith('/api/credentials', expect.objectContaining({ method: 'POST' })); + }); + + it('should update credential via PUT', async () => { + const { updateCredential } = useVault(); + await updateCredential(3, { name: 'updated' }); + expect((globalThis as any).$fetch).toHaveBeenCalledWith('/api/credentials/3', expect.objectContaining({ method: 'PUT' })); + }); + + it('should delete credential via DELETE', async () => { + const { deleteCredential } = useVault(); + await deleteCredential(3); + expect((globalThis as any).$fetch).toHaveBeenCalledWith('/api/credentials/3', expect.objectContaining({ method: 'DELETE' })); + }); +}); +``` + +- [ ] **Step 2: Write admin middleware tests** + +```typescript +// frontend/tests/middleware/admin.spec.ts +import { describe, it, expect, vi, beforeEach } from 'vitest'; + +describe('Admin Middleware', () => { + let middleware: any; + + beforeEach(() => { + vi.resetAllMocks(); + (globalThis as any).navigateTo = vi.fn().mockReturnValue('/'); + }); + + it('should redirect non-admin to /', async () => { + (globalThis as any).useAuthStore = vi.fn().mockReturnValue({ isAdmin: false }); + // Re-import to pick up fresh mock + const mod = await import('../../middleware/admin.ts'); + middleware = mod.default; + const result = middleware({} as any, {} as any); + expect(navigateTo).toHaveBeenCalledWith('/'); + }); + + it('should allow admin through', async () => { + (globalThis as any).useAuthStore = vi.fn().mockReturnValue({ isAdmin: true }); + const mod = await import('../../middleware/admin.ts'); + middleware = mod.default; + const result = middleware({} as any, {} as any); + // Should not redirect — returns undefined + expect(result).toBeUndefined(); + }); +}); +``` + +- [ ] **Step 3: Run all frontend tests** + +Run: `cd frontend && npx vitest run` +Expected: ~24 tests PASS + +- [ ] **Step 4: Commit** + +```bash +git add frontend/tests/composables/useVault.spec.ts frontend/tests/middleware/admin.spec.ts +git commit -m "test: vault composable + admin middleware — API calls, auth headers, route guard" +``` + +--- + +## Chunk 5: Integration Verification + Pre-commit Hook + +### Task 12: Run Full Test Suites + +- [ ] **Step 1: Run all backend tests** + +Run: `cd backend && npx jest --verbose` +Expected: ~66 tests PASS across 8 spec files + +- [ ] **Step 2: Run all frontend tests** + +Run: `cd frontend && npx vitest run` +Expected: ~30 tests PASS across 4 spec files + +- [ ] **Step 3: Add test:cov script to backend** + +Add to `backend/package.json` scripts: `"test:cov": "jest --coverage"` + +- [ ] **Step 4: Commit** + +```bash +git add backend/package.json +git commit -m "chore: add test:cov script to backend" +``` + +### Task 13: Final Push + +- [ ] **Step 1: Push everything** + +```bash +git push +``` + +- [ ] **Step 2: Verify test count** + +Run: `cd backend && npx jest --verbose 2>&1 | tail -5` +Run: `cd frontend && npx vitest run 2>&1 | tail -5` + +Report total test count to Commander. diff --git a/docs/screenshots/wraith-final.png b/docs/screenshots/wraith-final.png new file mode 100644 index 0000000..674a7d7 Binary files /dev/null and b/docs/screenshots/wraith-final.png differ diff --git a/docs/test-buildout-spec.md b/docs/test-buildout-spec.md new file mode 100644 index 0000000..5d82c54 --- /dev/null +++ b/docs/test-buildout-spec.md @@ -0,0 +1,108 @@ +# Wraith Remote — Test Suite Build-Out Spec + +**Date:** 2026-03-14 +**Scope:** Level B — Full service layer coverage (~80-100 tests) +**Status:** Pinned — awaiting green light to execute + +--- + +## Backend (Jest) + +Jest is already configured in `backend/package.json`. Zero spec files exist today. + +### Infrastructure + +- Tests co-located with source: `*.spec.ts` next to `*.ts` +- Shared mock factories in `backend/src/__mocks__/` for Prisma, JwtService, EncryptionService +- `beforeEach` resets all mocks to prevent test bleed + +### Test Files + +| File | Tests | Priority | +|------|-------|----------| +| `auth.service.spec.ts` | Login (valid, invalid, non-existent user timing), bcrypt→argon2 migration, TOTP setup/verify/disable, TOTP secret encryption/decryption, password hashing, profile update, admin CRUD | ~20 | +| `encryption.service.spec.ts` | v2 encrypt/decrypt round-trip, v1 backwards compat decrypt, v1→v2 upgrade, isV1 detection, invalid version handling, key derivation warmup | ~8 | +| `credentials.service.spec.ts` | findAll excludes encryptedValue, findOne with ownership check, create with encryption, update with password change, remove, decryptForConnection (password, SSH key, orphaned key, no auth) | ~10 | +| `ssh-keys.service.spec.ts` | Create with encryption, findAll (no private key leak), findOne ownership, update passphrase, remove, key type detection, fingerprint generation | ~8 | +| `jwt-auth.guard.spec.ts` | Passes valid JWT, rejects missing/expired/invalid JWT | ~3 | +| `admin.guard.spec.ts` | Allows admin role, blocks non-admin, blocks missing user | ~3 | +| `ws-auth.guard.spec.ts` | Cookie-based auth, WS ticket auth (valid, expired, reused), legacy URL token fallback, no token rejection | ~6 | +| `auth.controller.spec.ts` | Login sets cookie, logout clears cookie, ws-ticket issuance and consumption, TOTP endpoints wired correctly | ~8 | + +**Backend total: ~66 tests** + +### Mocking Strategy + +- **PrismaService:** Jest manual mock returning controlled data per test +- **EncryptionService:** Mock encrypt returns `v2:mock:...`, mock decrypt returns plaintext +- **JwtService:** Mock sign returns `mock-jwt-token`, mock verify returns payload +- **Argon2:** Real library (fast enough for unit tests, tests actual hashing behavior) +- **bcrypt:** Real library (needed to test migration path) + +--- + +## Frontend (Vitest) + +No test infrastructure exists today. Needs full setup. + +### Infrastructure + +- Install: `vitest`, `@vue/test-utils`, `@pinia/testing`, `happy-dom` +- Config: `frontend/vitest.config.ts` with happy-dom environment +- Global mock for `$fetch` (Nuxt auto-import) via setup file +- Global mock for `navigateTo` (Nuxt auto-import) + +### Test Files + +| File | Tests | Priority | +|------|-------|----------| +| `stores/auth.store.spec.ts` | Login success (stores user, no token in state), login TOTP flow, logout clears state + calls API, fetchProfile success/failure, getWsTicket, isAuthenticated/isAdmin getters | ~10 | +| `stores/connection.store.spec.ts` | fetchHosts, fetchTree, createHost, updateHost, deleteHost, group CRUD, no Authorization headers in requests | ~8 | +| `composables/useVault.spec.ts` | listKeys, importKey, deleteKey, listCredentials, createCredential, updateCredential, deleteCredential — all without Authorization headers | ~7 | +| `middleware/admin.spec.ts` | Redirects non-admin to /, allows admin through | ~3 | +| `plugins/auth.client.spec.ts` | Calls fetchProfile on init when user is null, skips when user exists | ~2 | + +**Frontend total: ~30 tests** + +--- + +## NPM Scripts + +```json +// backend/package.json +"test": "jest", +"test:watch": "jest --watch", +"test:cov": "jest --coverage" + +// frontend/package.json +"test": "vitest run", +"test:watch": "vitest", +"test:cov": "vitest run --coverage" +``` + +--- + +## Pre-commit Hook (Husky) + +- Install `husky` + `lint-staged` at repo root +- Pre-commit runs: backend Jest (changed files) + frontend Vitest (changed files) +- Fast feedback — only tests related to changed files run on commit + +--- + +## Execution Plan + +1. **Agent 1:** Backend test infra (mock factories) + auth service tests + auth controller tests + guard tests (~37 tests) +2. **Agent 2:** Backend vault tests — encryption, credentials, SSH keys (~26 tests) +3. **Agent 3:** Frontend test infra (Vitest setup) + store tests + middleware + plugin tests (~30 tests) +4. **XO (me):** Wire npm scripts, verify all pass, add Husky pre-commit hook, final integration check + +--- + +## Future (Level C — when ready) + +- Component tests with Vue Test Utils (render + interaction) +- Integration tests against a real test PostgreSQL (Docker test container) +- Gateway tests (WebSocket mocking for terminal, SFTP, RDP) +- E2E with Playwright +- CI pipeline (Gitea Actions) diff --git a/frontend/app.vue b/frontend/app.vue deleted file mode 100644 index 2234b12..0000000 --- a/frontend/app.vue +++ /dev/null @@ -1,12 +0,0 @@ - - - diff --git a/frontend/assets/css/main.css b/frontend/assets/css/main.css deleted file mode 100644 index c7303f9..0000000 --- a/frontend/assets/css/main.css +++ /dev/null @@ -1,7 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -html, body, #__nuxt { - @apply h-full bg-gray-900 text-gray-100; -} diff --git a/frontend/components/connections/GroupEditDialog.vue b/frontend/components/connections/GroupEditDialog.vue deleted file mode 100644 index 22eb218..0000000 --- a/frontend/components/connections/GroupEditDialog.vue +++ /dev/null @@ -1,108 +0,0 @@ - - - diff --git a/frontend/components/connections/HostCard.vue b/frontend/components/connections/HostCard.vue deleted file mode 100644 index 5ee5cd3..0000000 --- a/frontend/components/connections/HostCard.vue +++ /dev/null @@ -1,128 +0,0 @@ - - - diff --git a/frontend/components/connections/HostEditDialog.vue b/frontend/components/connections/HostEditDialog.vue deleted file mode 100644 index 2ebbee8..0000000 --- a/frontend/components/connections/HostEditDialog.vue +++ /dev/null @@ -1,249 +0,0 @@ - - -