diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 4f0a546..caa2767 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -5,6 +5,8 @@ ### NEVER GUESS - ALWAYS READ THE ACTUAL CODE **FUCKING ALWAYS look at the dependency actual code. Don't start fucking guessing stuff.** +run "pnpm run watch" when starting to do stuff, so the UI gets recompiled and the server automatically restarts on file changes. + When working with any dependency: 1. **READ the actual source code** in `node_modules/` or check the package documentation 2. **CHECK the exact API** - don't assume based on similar libraries diff --git a/deno.json b/deno.json index 9f1597f..48c2d52 100644 --- a/deno.json +++ b/deno.json @@ -7,7 +7,7 @@ "test": "deno test --allow-all test/", "test:watch": "deno test --allow-all --watch test/", "compile": "bash scripts/compile-all.sh", - "dev": "deno run --allow-all --unstable-ffi --watch mod.ts server --ephemeral --monitor" + "dev": "pnpm run watch" }, "imports": { "@std/path": "jsr:@std/path@^1.1.2", @@ -17,7 +17,7 @@ "@std/encoding": "jsr:@std/encoding@^1.0.10", "@db/sqlite": "jsr:@db/sqlite@0.12.0", "@push.rocks/smartdaemon": "npm:@push.rocks/smartdaemon@^2.1.0", - "@apiclient.xyz/docker": "npm:@apiclient.xyz/docker@2.1.0", + "@apiclient.xyz/docker": "npm:@apiclient.xyz/docker@^5.0.2", "@apiclient.xyz/cloudflare": "npm:@apiclient.xyz/cloudflare@6.4.3", "@push.rocks/smartacme": "npm:@push.rocks/smartacme@^8.0.0", "@push.rocks/smartregistry": "npm:@push.rocks/smartregistry@^1.8.0", diff --git a/package.json b/package.json index 36e71de..deb2f2f 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "onebox": "./bin/onebox-wrapper.js" }, "scripts": { - "postinstall": "node scripts/install-binary.js" + "postinstall": "node scripts/install-binary.js", + "watch": "concurrently --kill-others --names \"BACKEND,UI\" --prefix-colors \"cyan,magenta\" \"deno run --allow-all --unstable-ffi --watch mod.ts server --ephemeral --monitor\" \"cd ui && pnpm run watch\"" }, "keywords": [ "docker", @@ -50,5 +51,8 @@ "arm64" ], "packageManager": "pnpm@10.18.1+sha512.77a884a165cbba2d8d1c19e3b4880eee6d2fcabd0d879121e282196b80042351d5eb3ca0935fa599da1dc51265cc68816ad2bddd2a2de5ea9fdf92adbec7cd34", - "dependencies": {} + "dependencies": {}, + "devDependencies": { + "concurrently": "^9.1.2" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..9fc870b --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,206 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + concurrently: + specifier: ^9.1.2 + version: 9.2.1 + +packages: + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + concurrently@9.2.1: + resolution: {integrity: sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==} + engines: {node: '>=18'} + hasBin: true + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + require-directory@2.1.1: + resolution: {integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I=} + engines: {node: '>=0.10.0'} + + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + shell-quote@1.8.3: + resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + engines: {node: '>= 0.4'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + +snapshots: + + ansi-regex@5.0.1: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concurrently@9.2.1: + dependencies: + chalk: 4.1.2 + rxjs: 7.8.2 + shell-quote: 1.8.3 + supports-color: 8.1.1 + tree-kill: 1.2.2 + yargs: 17.7.2 + + emoji-regex@8.0.0: {} + + escalade@3.2.0: {} + + get-caller-file@2.0.5: {} + + has-flag@4.0.0: {} + + is-fullwidth-code-point@3.0.0: {} + + require-directory@2.1.1: {} + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + shell-quote@1.8.3: {} + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + tree-kill@1.2.2: {} + + tslib@2.8.1: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + y18n@5.0.8: {} + + yargs-parser@21.1.1: {} + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 diff --git a/readme.md b/readme.md index ac6ed59..fb76827 100644 --- a/readme.md +++ b/readme.md @@ -1,59 +1,88 @@ # @serve.zone/onebox -> Self-hosted container platform with automatic SSL and DNS - a mini Heroku for single servers +> 🚀 Self-hosted Docker Swarm platform with native reverse proxy, automatic SSL, and real-time WebSocket updates -**Onebox** is a single-executable tool that transforms any Linux server into a simple container hosting platform. Deploy Docker containers with automatic HTTPS, DNS configuration, and Nginx reverse proxy - all managed through a beautiful Angular web interface or powerful CLI. +**Onebox** transforms any Linux server into a powerful container hosting platform. Deploy Docker Swarm services with automatic HTTPS, DNS configuration, and a native Deno reverse proxy - all managed through a beautiful Angular web interface with real-time updates. -## Features +## Issue Reporting and Security -- 🐳 **Docker Container Management** - Deploy, start, stop, and manage containers -- 🌐 **Automatic Nginx Reverse Proxy** - Traffic routing with zero configuration -- 🔒 **Automatic SSL Certificates** - Let's Encrypt integration via SmartACME -- ☁️ **Cloudflare DNS Integration** - Automatic DNS record management -- 📊 **Metrics & Monitoring** - Historical CPU, memory, and network stats -- 📝 **Log Aggregation** - Centralized container logs -- 🎨 **Angular Web UI** - Modern, responsive interface -- 👥 **Multi-user Support** - Role-based access control -- 🔐 **Private Registry Support** - Use Docker Hub, Gitea, or custom registries +For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly. + +## What Makes Onebox Different? 🎯 + +- **Native Deno Reverse Proxy** - Built from scratch in Deno (no Nginx required!), featuring HTTP/HTTPS servers with SNI support and bidirectional WebSocket proxying +- **Docker Swarm First** - All workloads run as Swarm services, not standalone containers, for built-in orchestration +- **Real-time Everything** - WebSocket-powered live updates for service status, logs, and metrics across all connected clients +- **Single Executable** - Compiles to a standalone binary - just run it, no dependencies +- **Private Registry Included** - Built-in Docker registry with auto-deploy on push +- **Zero Config SSL** - Automatic Let's Encrypt certificates with hot-reload +- **Cloudflare Integration** - Automatic DNS record management +- **Modern Stack** - Deno runtime + SQLite database + Angular 18 UI + +## Features ✨ + +### Core Platform +- 🐳 **Docker Swarm Management** - Deploy, scale, and orchestrate services with Swarm mode +- 🌐 **Native Reverse Proxy** - Deno-based HTTP/HTTPS proxy with dynamic routing from database +- 🔒 **Automatic SSL Certificates** - Let's Encrypt integration with hot-reload and renewal monitoring +- ☁️ **Cloudflare DNS Integration** - Automatic DNS record creation and synchronization +- 📦 **Built-in Registry** - Private Docker registry with per-service tokens and auto-update +- 🔄 **Real-time WebSocket Updates** - Live service status, logs, and system events + +### Monitoring & Management +- 📊 **Metrics Collection** - Historical CPU, memory, and network stats (every 60s) +- 📝 **Centralized Logging** - Container logs with retention policies +- 🎨 **Angular Web UI** - Modern, responsive interface with real-time updates +- 👥 **Multi-user Support** - Role-based access control (admin/user) - 💾 **SQLite Database** - Embedded, zero-configuration storage -- 📦 **Single Executable** - No dependencies, no installation hassle -- 🔄 **Systemd Integration** - Run as a daemon with auto-restart -## Quick Start +### Developer Experience +- 🚀 **Auto-update on Push** - Push to registry and services update automatically +- 🔐 **Private Registry Support** - Use Docker Hub, Gitea, or custom registries +- 🔄 **Systemd Integration** - Run as a daemon with auto-restart +- 🎛️ **Full CLI & API** - Manage everything from terminal or HTTP API + +## Quick Start 🏁 ### Installation ```bash -# Install via shell script -curl -sSL https://code.foss.global/serve.zone/onebox/raw/branch/main/install.sh | sudo bash +# Download the latest release for your platform +curl -sSL https://code.foss.global/serve.zone/onebox/releases/latest/download/onebox-linux-x64 -o onebox +chmod +x onebox +sudo mv onebox /usr/local/bin/ -# Or via npm/pnpm +# Or install from npm pnpm install -g @serve.zone/onebox ``` -### Deploy Your First Service +### First Run ```bash -# Add a registry (optional, for private images) -onebox registry add --url registry.example.com --username myuser --password mypass +# Start the server in development mode +onebox server --ephemeral -# Deploy a service +# In another terminal, deploy your first service onebox service add myapp \ --image nginx:latest \ --domain app.example.com \ - --env PORT=80 - -# Check status -onebox service list - -# View logs -onebox service logs myapp + --port 80 ``` -### Install as Daemon +### Access the Web UI + +Open `http://localhost:3000` in your browser. + +**Default credentials:** +- Username: `admin` +- Password: `admin` + +⚠️ **Change the default password immediately after first login!** + +### Production Setup ```bash -# Install systemd service +# Install as systemd service sudo onebox daemon install # Start the daemon @@ -63,147 +92,395 @@ sudo onebox daemon start sudo onebox daemon logs ``` -### Access Web UI +## Architecture 🏗️ -The web UI is available at `http://localhost:3000` (or configured port). +Onebox is built with modern technologies for performance and developer experience: -Default credentials: -- Username: `admin` -- Password: `admin` (change immediately!) +``` +┌─────────────────────────────────────────────────┐ +│ Angular 18 Web UI │ +│ (Real-time WebSocket Updates) │ +└─────────────────┬───────────────────────────────┘ + │ HTTP/WS +┌─────────────────▼───────────────────────────────┐ +│ Deno HTTP Server (Port 3000) │ +│ REST API + WebSocket Broadcast │ +└─────────────────┬───────────────────────────────┘ + │ +┌─────────────────▼───────────────────────────────┐ +│ Native Reverse Proxy │ +│ HTTP (80) + HTTPS (443) + SNI + WS Proxy │ +└─────┬───────────────────────────────────────────┘ + │ + ├──► Docker Swarm Services + ├──► SSL Certificate Manager (Let's Encrypt) + ├──► Cloudflare DNS Manager + ├──► Built-in Docker Registry + └──► SQLite Database +``` -## CLI Reference +### Core Components + +- **Deno Runtime** - Modern TypeScript with built-in security +- **Native Reverse Proxy** - Custom HTTP/HTTPS proxy with TLS SNI support +- **Docker Swarm** - Container orchestration (NOT standalone containers) +- **SQLite Database** - Configuration, metrics, and user data +- **WebSocket Server** - Real-time bidirectional communication +- **Let's Encrypt** - Automatic SSL certificate management +- **Cloudflare API** - DNS record automation + +## CLI Reference 📖 ### Service Management ```bash -onebox service add --image --domain [--env KEY=VALUE] -onebox service remove +# Deploy a service +onebox service add --image --domain [--port ] [--env KEY=VALUE] + +# Deploy with Onebox Registry (auto-update on push) +onebox service add myapp --use-onebox-registry --domain myapp.example.com + +# List services +onebox service list + +# Control services onebox service start onebox service stop onebox service restart -onebox service list -onebox service logs [--follow] + +# Remove service +onebox service remove + +# View logs +onebox service logs +``` + +### Server Management + +```bash +# Start server (development) +onebox server --ephemeral # Runs in foreground with monitoring + +# Start server (production) +onebox daemon install # Install systemd service +onebox daemon start # Start daemon +onebox daemon stop # Stop daemon +onebox daemon logs # View logs ``` ### Registry Management ```bash -onebox registry add --url --username --password -onebox registry remove +# Add external registry credentials +onebox registry add --url registry.example.com --username user --password pass + +# List registries onebox registry list + +# Remove registry +onebox registry remove ``` ### DNS Management ```bash -onebox dns add --ip -onebox dns remove +# Add DNS record (requires Cloudflare config) +onebox dns add + +# List DNS records onebox dns list + +# Sync from Cloudflare onebox dns sync + +# Remove DNS record +onebox dns remove ``` ### SSL Management ```bash -onebox ssl renew [domain] -onebox ssl list +# Renew expiring certificates +onebox ssl renew + +# Force renew specific domain onebox ssl force-renew -``` -### Nginx Management - -```bash -onebox nginx reload -onebox nginx test -onebox nginx status -``` - -### Daemon Management - -```bash -onebox daemon install -onebox daemon start -onebox daemon stop -onebox daemon restart -onebox daemon logs -``` - -### User Management - -```bash -onebox user add --password [--role admin|user] -onebox user remove -onebox user list -onebox user passwd +# List certificates +onebox ssl list ``` ### Configuration ```bash +# Show all settings onebox config show + +# Set configuration value onebox config set + +# Example: Configure Cloudflare +onebox config set cloudflareAPIKey your-api-key +onebox config set cloudflareEmail your@email.com +onebox config set cloudflareZoneID your-zone-id ``` -### Metrics +### System Status ```bash -onebox metrics [service-name] +# Get full system status +onebox status ``` -## Architecture +## Configuration 🔧 -Onebox is built with Deno and compiles to a standalone binary for each platform: +### System Requirements -- **Deno Runtime** - Modern TypeScript with built-in security -- **SQLite** - Embedded database for configuration and metrics -- **Docker Engine** - Container runtime (required on host) -- **Nginx** - Reverse proxy and SSL termination -- **Cloudflare API** - DNS management -- **Let's Encrypt** - Free SSL certificates -- **Angular 18+** - Modern web interface - -## Requirements - -- **Linux** x64 or ARM64 (primary target) +- **Linux** (x64 or ARM64) - **Docker** installed and running -- **Nginx** installed -- **Root/sudo access** (for nginx, Docker, ports 80/443) -- **(Optional) Cloudflare account** for DNS management +- **Docker Swarm** initialized (`docker swarm init`) +- **Root/sudo access** for ports 80/443 +- **(Optional) Cloudflare account** for DNS automation -## Development +### Data Locations + +- **Database**: `./onebox.db` (or custom path) +- **SSL Certificates**: Managed by CertManager +- **Registry Data**: `./.nogit/registry-data` + +### Environment Variables + +```bash +# Database location +ONEBOX_DB_PATH=/path/to/onebox.db + +# HTTP server port (default: 3000) +ONEBOX_HTTP_PORT=3000 + +# Enable debug logging +ONEBOX_DEBUG=true +``` + +## Development 💻 + +### Setup ```bash # Clone repository git clone https://code.foss.global/serve.zone/onebox cd onebox -# Run in development mode +# Install dependencies (Deno handles this automatically) +deno task dev +``` + +### Tasks + +```bash +# Development server (auto-restart on changes) deno task dev # Run tests deno task test -# Compile for all platforms +# Watch mode for tests +deno task test:watch + +# Compile binaries for all platforms deno task compile ``` -## Configuration +### Project Structure -Onebox stores configuration in: -- **Database**: `/var/lib/onebox/onebox.db` -- **Nginx configs**: `/etc/nginx/sites-available/onebox-*` -- **SSL certificates**: `/etc/letsencrypt/live/` +``` +onebox/ +├── ts/ +│ ├── classes/ # Core implementations +│ │ ├── onebox.ts # Main coordinator +│ │ ├── reverseproxy.ts # Native HTTP/HTTPS proxy +│ │ ├── docker.ts # Docker Swarm API +│ │ ├── database.ts # SQLite storage +│ │ ├── httpserver.ts # REST API + WebSocket +│ │ ├── services.ts # Service orchestration +│ │ ├── certmanager.ts # SSL certificate management +│ │ ├── registry.ts # Built-in Docker registry +│ │ └── ... +│ ├── cli.ts # CLI router +│ ├── types.ts # TypeScript interfaces +│ └── plugins.ts # Dependency imports +├── ui/ # Angular web interface +├── test/ # Test files +├── mod.ts # Main entry point +└── deno.json # Deno configuration +``` -## Contributing +### API Endpoints -Contributions welcome! Please read the contributing guidelines first. +The HTTP server exposes the following endpoints: -## License +- `POST /api/auth/login` - User authentication (returns token) +- `GET /api/status` - System status (requires auth) +- `GET /api/services` - List all services (requires auth) +- `POST /api/services` - Create service (requires auth) +- `PUT /api/services/:id` - Update service (requires auth) +- `DELETE /api/services/:id` - Delete service (requires auth) +- `GET /api/ws` - WebSocket connection for real-time updates -MIT © Lossless GmbH +See `ts/classes/httpserver.ts` for complete API documentation. -## Links +### WebSocket Messages -- [Documentation](https://code.foss.global/serve.zone/onebox/src/branch/main/docs) -- [Issue Tracker](https://code.foss.global/serve.zone/onebox/issues) -- [Changelog](./changelog.md) +Real-time updates are broadcast via WebSocket: + +```typescript +// Service lifecycle updates +{ + type: 'service_update', + action: 'created' | 'updated' | 'deleted' | 'started' | 'stopped', + service: { id, name, status, ... } +} + +// Service status changes +{ + type: 'service_status', + service: { id, name, status, ... } +} + +// System status updates +{ + type: 'system_status', + status: { docker, reverseProxy, services, ... } +} +``` + +## Advanced Usage 🚀 + +### Using the Built-in Registry + +```bash +# Deploy a service with Onebox Registry +onebox service add myapp \ + --use-onebox-registry \ + --domain myapp.example.com \ + --auto-update-on-push + +# Get the registry token for pushing images +# (Token is automatically created and stored in database) + +# Push your image +docker tag myimage:latest localhost:4000/myapp:latest +docker push localhost:4000/myapp:latest + +# Service automatically updates! 🎉 +``` + +### Cloudflare DNS Integration + +```bash +# Configure Cloudflare (one-time setup) +onebox config set cloudflareAPIKey your-api-key +onebox config set cloudflareEmail your@email.com +onebox config set cloudflareZoneID your-zone-id + +# Deploy with automatic DNS +onebox service add myapp \ + --image nginx:latest \ + --domain myapp.example.com + +# DNS record is automatically created! +``` + +### SSL Certificate Management + +SSL certificates are automatically obtained and renewed: + +- Certificates are requested when a service with a domain is deployed +- Renewal happens automatically 30 days before expiry +- Certificates are hot-reloaded without downtime +- Force renewal: `onebox ssl force-renew ` + +### Monitoring and Metrics + +Metrics are collected every 60 seconds (configurable): + +```bash +# Set metrics interval (milliseconds) +onebox config set metricsInterval 30000 + +# View in web UI or query database directly +sqlite3 onebox.db "SELECT * FROM metrics WHERE service_id = 1 ORDER BY timestamp DESC LIMIT 10" +``` + +## Troubleshooting 🔧 + +### Docker Swarm Not Initialized + +```bash +# Initialize Docker Swarm +docker swarm init + +# Verify swarm mode +docker info | grep "Swarm: active" +``` + +### Port Already in Use + +```bash +# Check what's using port 80/443 +sudo lsof -i :80 +sudo lsof -i :443 + +# Kill the process or change Onebox ports +onebox config set httpPort 8080 +``` + +### SSL Certificate Issues + +```bash +# Check certificate status +onebox ssl list + +# Verify DNS is pointing to your server +dig +short yourdomain.com + +# Force certificate renewal +onebox ssl force-renew yourdomain.com +``` + +### WebSocket Connection Issues + +- Ensure firewall allows WebSocket connections +- Check browser console for connection errors +- Verify `/api/ws` endpoint is accessible + +### Service Not Starting + +```bash +# Check Docker logs +docker service logs + +# Check Onebox logs +onebox daemon logs + +# Verify image exists +docker images | grep +``` + +## License and Legal Information + +This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. + +**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file. + +### Trademarks + +This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH. + +### Company Information + +Task Venture Capital GmbH +Registered at District court Bremen HRB 35230 HB, Germany + +For any legal inquiries or if you require further information, please contact us via email at hello@task.vc. + +By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works. diff --git a/ts/classes/cloudflare-sync.ts b/ts/classes/cloudflare-sync.ts index a504b4a..819c05a 100644 --- a/ts/classes/cloudflare-sync.ts +++ b/ts/classes/cloudflare-sync.ts @@ -57,8 +57,8 @@ export class CloudflareDomainSync { try { logger.info('Starting Cloudflare zone synchronization...'); - // Fetch all zones from Cloudflare - const zones = await this.cloudflareAccount!.getZones(); + // Fetch all zones from Cloudflare (v6+ API uses convenience.listZones()) + const zones = await this.cloudflareAccount!.convenience.listZones(); logger.info(`Found ${zones.length} Cloudflare zone(s)`); const now = Date.now(); diff --git a/ts/classes/database.ts b/ts/classes/database.ts index c16968b..8037609 100644 --- a/ts/classes/database.ts +++ b/ts/classes/database.ts @@ -651,7 +651,13 @@ export class OneboxDatabase { if (!this.db) throw new Error('Database not initialized'); const rows = this.query('SELECT * FROM services WHERE name = ?', [name]); - return rows.length > 0 ? this.rowToService(rows[0]) : null; + if (rows.length > 0) { + logger.info(`getServiceByName: raw row data: ${JSON.stringify(rows[0])}`); + const service = this.rowToService(rows[0]); + logger.info(`getServiceByName: service object containerID: ${service.containerID}`); + return service; + } + return null; } getServiceByID(id: number): IService | null { diff --git a/ts/classes/docker.ts b/ts/classes/docker.ts index 7b651d8..c469446 100644 --- a/ts/classes/docker.ts +++ b/ts/classes/docker.ts @@ -40,8 +40,8 @@ export class OneboxDockerManager { */ private async ensureNetwork(): Promise { try { - const networks = await this.dockerClient!.getNetworks(); - const existingNetwork = networks.find((n: any) => n.name === this.networkName); + const networks = await this.dockerClient!.listNetworks(); + const existingNetwork = networks.find((n: any) => n.Name === this.networkName); if (!existingNetwork) { logger.info(`Creating Docker network: ${this.networkName}`); @@ -228,14 +228,12 @@ export class OneboxDockerManager { * Get network ID by name */ private async getNetworkID(networkName: string): Promise { - const networks = await this.dockerClient!.getNetworks(); - const network = networks.find((n: any) => - (n.name || n.Name) === networkName - ); + const networks = await this.dockerClient!.listNetworks(); + const network = networks.find((n: any) => n.Name === networkName); if (!network) { throw new Error(`Network not found: ${networkName}`); } - return network.id || network.Id; + return network.Id; } /** @@ -578,19 +576,32 @@ export class OneboxDockerManager { */ async getContainerStats(containerID: string): Promise { try { - const container = this.dockerClient!.getContainer(containerID); + const container = await this.dockerClient!.getContainerById(containerID); + + if (!container) { + // Container not found - this is expected for Swarm services where we have service ID instead of container ID + // Return null silently + return null; + } + const stats = await container.stats({ stream: false }); + // Validate stats structure + if (!stats || !stats.cpu_stats || !stats.cpu_stats.cpu_usage) { + logger.warn(`Invalid stats structure for container ${containerID}`); + return null; + } + // Calculate CPU percentage const cpuDelta = - stats.cpu_stats.cpu_usage.total_usage - stats.precpu_stats.cpu_usage.total_usage; - const systemDelta = stats.cpu_stats.system_cpu_usage - stats.precpu_stats.system_cpu_usage; + stats.cpu_stats.cpu_usage.total_usage - (stats.precpu_stats?.cpu_usage?.total_usage || 0); + const systemDelta = stats.cpu_stats.system_cpu_usage - (stats.precpu_stats?.system_cpu_usage || 0); const cpuPercent = systemDelta > 0 ? (cpuDelta / systemDelta) * stats.cpu_stats.online_cpus * 100 : 0; // Memory stats - const memoryUsed = stats.memory_stats.usage || 0; - const memoryLimit = stats.memory_stats.limit || 0; + const memoryUsed = stats.memory_stats?.usage || 0; + const memoryLimit = stats.memory_stats?.limit || 0; const memoryPercent = memoryLimit > 0 ? (memoryUsed / memoryLimit) * 100 : 0; // Network stats @@ -612,49 +623,88 @@ export class OneboxDockerManager { networkTx, }; } catch (error) { - logger.error(`Failed to get container stats ${containerID}: ${error.message}`); + // Don't log errors for container not found - this is expected for Swarm services + if (!error.message.includes('No such container') && !error.message.includes('not found')) { + logger.error(`Failed to get container stats ${containerID}: ${error.message}`); + } + return null; + } + } + + /** + * Helper: Get actual container ID for a Swarm service + * For Swarm services, we need to find the task/container that's actually running + */ + private async getContainerIdForService(serviceId: string): Promise { + try { + // List all containers and find one with the service label matching our service ID + const containers = await this.dockerClient!.listContainers(); + + // Find a container that belongs to this service + const serviceContainer = containers.find((container: any) => { + const labels = container.Labels || {}; + // Swarm services have a com.docker.swarm.service.id label + return labels['com.docker.swarm.service.id'] === serviceId; + }); + + if (serviceContainer) { + return serviceContainer.Id; + } + + return null; + } catch (error) { + logger.warn(`Failed to get container ID for service ${serviceId}: ${error.message}`); return null; } } /** * Get container logs + * Handles both regular containers and Swarm services */ async getContainerLogs( containerID: string, tail = 100 ): Promise<{ stdout: string; stderr: string }> { try { - const container = this.dockerClient!.getContainer(containerID); - const logs = await container.logs({ - stdout: true, - stderr: true, - tail, - timestamps: true, - }); + let actualContainerId = containerID; - // Parse logs (Docker returns them in a special format) - const stdout: string[] = []; - const stderr: string[] = []; + // Try to get container directly first + let container = await this.dockerClient!.getContainerById(containerID); - const lines = logs.toString().split('\n'); - for (const line of lines) { - if (line.length === 0) continue; - - // Docker log format: first byte indicates stream (1=stdout, 2=stderr) - const streamType = line.charCodeAt(0); - const content = line.slice(8); // Skip header (8 bytes) - - if (streamType === 1) { - stdout.push(content); - } else if (streamType === 2) { - stderr.push(content); + // If not found, it might be a service ID - try to get the actual container ID + if (!container) { + const serviceContainerId = await this.getContainerIdForService(containerID); + if (serviceContainerId) { + actualContainerId = serviceContainerId; + container = await this.dockerClient!.getContainerById(serviceContainerId); } } + if (!container) { + throw new Error(`Container not found: ${containerID}`); + } + + // Get logs as string (v5 handles demultiplexing automatically) + const logs = await container.logs({ + stdout: true, + stderr: true, + tail: tail, + timestamps: true, + }); + + // v5 should return a string, but let's handle edge cases + if (typeof logs !== 'string') { + logger.error(`Unexpected logs type: ${typeof logs}, constructor: ${logs?.constructor?.name}`); + logger.error(`Logs content: ${JSON.stringify(logs).slice(0, 500)}`); + // If it's not a string, something went wrong + throw new Error(`Unexpected log format: expected string, got ${typeof logs}`); + } + + // v5 returns already-parsed logs as a string return { - stdout: stdout.join('\n'), - stderr: stderr.join('\n'), + stdout: logs, + stderr: '', // v5 combines stdout/stderr into single string }; } catch (error) { logger.error(`Failed to get container logs ${containerID}: ${error.message}`); @@ -662,47 +712,15 @@ export class OneboxDockerManager { } } - /** - * Stream container logs (real-time) - */ - async streamContainerLogs( - containerID: string, - callback: (line: string, isError: boolean) => void - ): Promise { - try { - const container = this.dockerClient!.getContainer(containerID); - const stream = await container.logs({ - stdout: true, - stderr: true, - follow: true, - tail: 0, - timestamps: true, - }); - - stream.on('data', (chunk: Buffer) => { - const streamType = chunk[0]; - const content = chunk.slice(8).toString(); - callback(content, streamType === 2); - }); - - stream.on('error', (error: Error) => { - logger.error(`Log stream error for ${containerID}: ${error.message}`); - }); - } catch (error) { - logger.error(`Failed to stream container logs ${containerID}: ${error.message}`); - throw error; - } - } - /** * List all onebox-managed containers */ async listContainers(): Promise { try { - const containers = await this.dockerClient!.getContainers(); + const containers = await this.dockerClient!.listContainers(); // Filter for onebox-managed containers return containers.filter((c: any) => - c.labels && c.labels['managed-by'] === 'onebox' + c.Labels && c.Labels['managed-by'] === 'onebox' ); } catch (error) { logger.error(`Failed to list containers: ${error.message}`); @@ -724,14 +742,16 @@ export class OneboxDockerManager { /** * Get Docker version info + * Note: v5 API doesn't expose version() method, so we return a placeholder */ async getDockerVersion(): Promise { - try { - return await this.dockerClient!.version(); - } catch (error) { - logger.error(`Failed to get Docker version: ${error.message}`); - return null; - } + // v5 API doesn't have a version() method + // Return a basic structure for compatibility + return { + Version: 'N/A', + ApiVersion: 'N/A', + Note: 'Version info not available in @apiclient.xyz/docker v5' + }; } /** @@ -753,7 +773,12 @@ export class OneboxDockerManager { */ async getContainerIP(containerID: string): Promise { try { - const container = this.dockerClient!.getContainer(containerID); + const container = await this.dockerClient!.getContainerById(containerID); + + if (!container) { + throw new Error(`Container not found: ${containerID}`); + } + const info = await container.inspect(); const networks = info.NetworkSettings.Networks; @@ -776,7 +801,11 @@ export class OneboxDockerManager { cmd: string[] ): Promise<{ stdout: string; stderr: string; exitCode: number }> { try { - const container = this.dockerClient!.getContainer(containerID); + const container = await this.dockerClient!.getContainerById(containerID); + + if (!container) { + throw new Error(`Container not found: ${containerID}`); + } const exec = await container.exec({ Cmd: cmd, diff --git a/ts/classes/httpserver.ts b/ts/classes/httpserver.ts index 2c84ee4..d840573 100644 --- a/ts/classes/httpserver.ts +++ b/ts/classes/httpserver.ts @@ -76,6 +76,12 @@ export class OneboxHttpServer { return this.handleWebSocketUpgrade(req); } + // Log streaming WebSocket + if (path.startsWith('/api/services/') && path.endsWith('/logs/stream') && req.headers.get('upgrade') === 'websocket') { + const serviceName = path.split('/')[3]; + return this.handleLogStreamUpgrade(req, serviceName); + } + // Docker Registry v2 API (no auth required - registry handles it) if (path.startsWith('/v2/')) { return await this.oneboxRef.registry.handleRequest(req); @@ -107,25 +113,31 @@ export class OneboxHttpServer { filePath = '/index.html'; } - const fullPath = `./ui/dist${filePath}`; + const fullPath = `./ui/dist/ui/browser${filePath}`; // Read file const file = await Deno.readFile(fullPath); // Determine content type const contentType = this.getContentType(filePath); + // Prevent stale bundles in dev (no hashed filenames) while allowing long-lived caching for hashed prod assets + const isHashedAsset = /\.[a-f0-9]{8,}\./i.test(filePath); + const cacheControl = + filePath === '/index.html' || !isHashedAsset + ? 'no-cache' + : 'public, max-age=31536000, immutable'; return new Response(file, { headers: { 'Content-Type': contentType, - 'Cache-Control': filePath === '/index.html' ? 'no-cache' : 'public, max-age=3600', + 'Cache-Control': cacheControl, }, }); } catch (error) { // File not found - serve index.html for Angular routing if (error instanceof Deno.errors.NotFound) { try { - const indexFile = await Deno.readFile('./ui/dist/index.html'); + const indexFile = await Deno.readFile('./ui/dist/ui/browser/index.html'); return new Response(indexFile, { headers: { 'Content-Type': 'text/html', @@ -450,6 +462,8 @@ export class OneboxHttpServer { private async handleGetLogsRequest(name: string): Promise { try { const logs = await this.oneboxRef.services.getServiceLogs(name); + logger.log(`handleGetLogsRequest: logs type = ${typeof logs}, constructor = ${logs?.constructor?.name}`); + logger.log(`handleGetLogsRequest: logs value = ${String(logs).slice(0, 100)}`); return this.jsonResponse({ success: true, data: logs }); } catch (error) { logger.error(`Failed to get logs for service ${name}: ${error.message}`); @@ -824,6 +838,135 @@ export class OneboxHttpServer { return response; } + /** + * Handle WebSocket upgrade for log streaming + */ + private handleLogStreamUpgrade(req: Request, serviceName: string): Response { + const { socket, response } = Deno.upgradeWebSocket(req); + + socket.onopen = async () => { + logger.info(`Log stream WebSocket connected for service: ${serviceName}`); + + try { + // Get the service from database + const service = this.oneboxRef.database.getServiceByName(serviceName); + if (!service) { + socket.send(JSON.stringify({ error: 'Service not found' })); + socket.close(); + return; + } + + // Get the container (handle both direct container IDs and service IDs) + logger.info(`Looking up container for service ${serviceName}, containerID: ${service.containerID}`); + let container = await this.oneboxRef.docker.dockerClient!.getContainerById(service.containerID!); + logger.info(`Direct lookup result: ${container ? 'found' : 'null'}`); + + // If not found, it might be a service ID - try to get the actual container ID + if (!container) { + logger.info('Listing all containers to find matching service...'); + const containers = await this.oneboxRef.docker.dockerClient!.listContainers(); + logger.info(`Found ${containers.length} containers`); + + const serviceContainer = containers.find((c: any) => { + const labels = c.Labels || {}; + return labels['com.docker.swarm.service.id'] === service.containerID; + }); + + if (serviceContainer) { + logger.info(`Found matching container: ${serviceContainer.Id}`); + container = await this.oneboxRef.docker.dockerClient!.getContainerById(serviceContainer.Id); + logger.info(`Second lookup result: ${container ? 'found' : 'null'}`); + } else { + logger.error(`No container found with service label matching ${service.containerID}`); + } + } + + if (!container) { + logger.error(`Container not found for service ${serviceName}, containerID: ${service.containerID}`); + socket.send(JSON.stringify({ error: 'Container not found' })); + socket.close(); + return; + } + + // Start streaming logs + const logStream = await container.streamLogs({ + stdout: true, + stderr: true, + timestamps: true, + tail: 100, // Start with last 100 lines + }); + + // Send initial connection message + socket.send(JSON.stringify({ + type: 'connected', + serviceName: service.name, + })); + + // Demultiplex and pipe log data to WebSocket + // Docker streams use 8-byte headers: [STREAM_TYPE, 0, 0, 0, SIZE_BYTE1, SIZE_BYTE2, SIZE_BYTE3, SIZE_BYTE4] + let buffer = Buffer.alloc(0); + + logStream.on('data', (chunk: Buffer) => { + if (socket.readyState !== WebSocket.OPEN) return; + + // Append new data to buffer + buffer = Buffer.concat([buffer, chunk]); + + // Process complete frames + while (buffer.length >= 8) { + // Read frame size from header (bytes 4-7, big-endian) + const frameSize = buffer.readUInt32BE(4); + + // Check if we have the complete frame + if (buffer.length < 8 + frameSize) { + break; // Wait for more data + } + + // Extract the frame data (skip 8-byte header) + const frameData = buffer.slice(8, 8 + frameSize); + + // Send the clean log line + socket.send(frameData.toString('utf8')); + + // Remove processed frame from buffer + buffer = buffer.slice(8 + frameSize); + } + }); + + logStream.on('error', (error: Error) => { + logger.error(`Log stream error for ${serviceName}: ${error.message}`); + if (socket.readyState === WebSocket.OPEN) { + socket.send(JSON.stringify({ error: error.message })); + } + }); + + logStream.on('end', () => { + logger.info(`Log stream ended for ${serviceName}`); + socket.close(); + }); + + // Clean up on close + socket.onclose = () => { + logger.info(`Log stream WebSocket closed for ${serviceName}`); + logStream.destroy(); + }; + + } catch (error) { + logger.error(`Failed to start log stream for ${serviceName}: ${error.message}`); + if (socket.readyState === WebSocket.OPEN) { + socket.send(JSON.stringify({ error: error.message })); + socket.close(); + } + } + }; + + socket.onerror = (error) => { + logger.error(`Log stream WebSocket error: ${error}`); + }; + + return response; + } + /** * Broadcast message to all connected WebSocket clients */ diff --git a/ts/classes/registry.ts b/ts/classes/registry.ts index a8cd7f0..a159226 100644 --- a/ts/classes/registry.ts +++ b/ts/classes/registry.ts @@ -157,13 +157,22 @@ export class RegistryManager { } try { + // Check if getManifest method exists (API may have changed) + if (typeof this.registry.getManifest !== 'function') { + // Method not available in current API version + return null; + } + const manifest = await this.registry.getManifest(repository, tag); if (manifest && manifest.digest) { return manifest.digest; } return null; } catch (error) { - logger.warn(`Failed to get digest for ${repository}:${tag}: ${error.message}`); + // Only log if it's not a "not a function" error + if (!error.message.includes('not a function')) { + logger.warn(`Failed to get digest for ${repository}:${tag}: ${error.message}`); + } return null; } } diff --git a/ts/classes/services.ts b/ts/classes/services.ts index 4a83943..6b5c856 100644 --- a/ts/classes/services.ts +++ b/ts/classes/services.ts @@ -333,7 +333,13 @@ export class OneboxServicesManager { const logs = await this.docker.getContainerLogs(service.containerID, tail); - return `=== STDOUT ===\n${logs.stdout}\n\n=== STDERR ===\n${logs.stderr}`; + // Debug: check what we got + logger.log(`getServiceLogs: logs type = ${typeof logs}, constructor = ${logs?.constructor?.name}`); + logger.log(`getServiceLogs: logs.stdout type = ${typeof logs.stdout}`); + logger.log(`getServiceLogs: logs.stdout value = ${String(logs.stdout).slice(0, 100)}`); + + // v5 API returns combined stdout/stderr with proper formatting + return logs.stdout; } catch (error) { logger.error(`Failed to get logs for service ${name}: ${error.message}`); throw error; diff --git a/ui/.editorconfig b/ui/.editorconfig new file mode 100644 index 0000000..f166060 --- /dev/null +++ b/ui/.editorconfig @@ -0,0 +1,17 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true + +[*.ts] +quote_type = single +ij_typescript_use_double_quotes = false + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/ui/.gitignore b/ui/.gitignore index 048422d..cc7b141 100644 --- a/ui/.gitignore +++ b/ui/.gitignore @@ -1,14 +1,42 @@ -# Dependencies -node_modules/ +# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files. -# Build outputs -dist/ -.angular/ +# Compiled output +/dist +/tmp +/out-tsc +/bazel-out -# IDE -.vscode/ +# Node +/node_modules +npm-debug.log +yarn-error.log + +# IDEs and editors .idea/ +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace -# Misc +# Visual Studio Code +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +.history/* + +# Miscellaneous +/.angular/cache +.sass-cache/ +/connect.lock +/coverage +/libpeerconnection.log +testem.log +/typings + +# System files .DS_Store -*.log +Thumbs.db diff --git a/ui/README.md b/ui/README.md new file mode 100644 index 0000000..518720a --- /dev/null +++ b/ui/README.md @@ -0,0 +1,59 @@ +# Ui + +This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 19.2.19. + +## Development server + +To start a local development server, run: + +```bash +ng serve +``` + +Once the server is running, open your browser and navigate to `http://localhost:4200/`. The application will automatically reload whenever you modify any of the source files. + +## Code scaffolding + +Angular CLI includes powerful code scaffolding tools. To generate a new component, run: + +```bash +ng generate component component-name +``` + +For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run: + +```bash +ng generate --help +``` + +## Building + +To build the project run: + +```bash +ng build +``` + +This will compile your project and store the build artifacts in the `dist/` directory. By default, the production build optimizes your application for performance and speed. + +## Running unit tests + +To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command: + +```bash +ng test +``` + +## Running end-to-end tests + +For end-to-end (e2e) testing, run: + +```bash +ng e2e +``` + +Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs. + +## Additional Resources + +For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page. diff --git a/ui/angular.json b/ui/angular.json index bf75e24..a16426f 100644 --- a/ui/angular.json +++ b/ui/angular.json @@ -3,29 +3,26 @@ "version": 1, "newProjectRoot": "projects", "projects": { - "onebox-ui": { + "ui": { "projectType": "application", - "schematics": { - "@schematics/angular:component": { - "style": "css", - "standalone": true - } - }, + "schematics": {}, "root": "", "sourceRoot": "src", "prefix": "app", "architect": { "build": { - "builder": "@angular-devkit/build-angular:browser", + "builder": "@angular-devkit/build-angular:application", "options": { - "outputPath": "dist", + "outputPath": "dist/ui", "index": "src/index.html", - "main": "src/main.ts", - "polyfills": ["zone.js"], + "browser": "src/main.ts", + "polyfills": [], "tsConfig": "tsconfig.app.json", "assets": [ - "src/favicon.ico", - "src/assets" + { + "glob": "**/*", + "input": "public" + } ], "styles": [ "src/styles.css" @@ -37,39 +34,59 @@ "budgets": [ { "type": "initial", - "maximumWarning": "500kb", - "maximumError": "1mb" + "maximumWarning": "500kB", + "maximumError": "1MB" }, { "type": "anyComponentStyle", - "maximumWarning": "2kb", - "maximumError": "4kb" + "maximumWarning": "4kB", + "maximumError": "8kB" } ], "outputHashing": "all" }, "development": { - "buildOptimizer": false, "optimization": false, - "vendorChunk": true, "extractLicenses": false, - "sourceMap": true, - "namedChunks": true + "sourceMap": true } }, "defaultConfiguration": "production" }, "serve": { "builder": "@angular-devkit/build-angular:dev-server", + "options": { + "proxyConfig": "proxy.conf.json" + }, "configurations": { "production": { - "buildTarget": "onebox-ui:build:production" + "buildTarget": "ui:build:production" }, "development": { - "buildTarget": "onebox-ui:build:development" + "buildTarget": "ui:build:development" } }, "defaultConfiguration": "development" + }, + "extract-i18n": { + "builder": "@angular-devkit/build-angular:extract-i18n" + }, + "test": { + "builder": "@angular-devkit/build-angular:karma", + "options": { + "polyfills": [], + "tsConfig": "tsconfig.spec.json", + "assets": [ + { + "glob": "**/*", + "input": "public" + } + ], + "styles": [ + "src/styles.css" + ], + "scripts": [] + } } } } diff --git a/ui/package.json b/ui/package.json index 6cf9436..2168d21 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,37 +1,41 @@ { - "name": "onebox-ui", - "version": "1.0.0", + "name": "ui", + "version": "0.0.0", "scripts": { "ng": "ng", - "start": "ng serve --proxy-config proxy.conf.json", - "build": "ng build --configuration production", + "start": "ng serve", + "build": "ng build", "watch": "ng build --watch --configuration development", "test": "ng test" }, "private": true, "dependencies": { - "@angular/animations": "^18.0.0", - "@angular/common": "^18.0.0", - "@angular/compiler": "^18.0.0", - "@angular/core": "^18.0.0", - "@angular/forms": "^18.0.0", - "@angular/platform-browser": "^18.0.0", - "@angular/platform-browser-dynamic": "^18.0.0", - "@angular/router": "^18.0.0", - "chart.js": "^4.4.0", - "ng2-charts": "^6.0.0", + "@angular/common": "^19.2.0", + "@angular/compiler": "^19.2.0", + "@angular/core": "^19.2.0", + "@angular/forms": "^19.2.0", + "@angular/platform-browser": "^19.2.0", + "@angular/platform-browser-dynamic": "^19.2.0", + "@angular/router": "^19.2.0", + "autoprefixer": "^10.4.22", + "postcss": "^8.5.6", "rxjs": "~7.8.0", + "tailwindcss": "^3.4.18", "tslib": "^2.3.0", - "zone.js": "~0.14.3" + "zone.js": "~0.15.0" }, "devDependencies": { - "@angular-devkit/build-angular": "^18.0.0", - "@angular/cli": "^18.0.0", - "@angular/compiler-cli": "^18.0.0", - "@types/node": "^20.11.0", - "autoprefixer": "^10.4.19", - "postcss": "^8.4.38", - "tailwindcss": "^3.4.3", - "typescript": "~5.4.0" + "@angular-devkit/build-angular": "^19.2.19", + "@angular/cli": "^19.2.19", + "@angular/compiler-cli": "^19.2.0", + "@types/jasmine": "~5.1.0", + "@types/node": "^24.10.1", + "jasmine-core": "~5.6.0", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.2.0", + "karma-coverage": "~2.2.0", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.1.0", + "typescript": "~5.7.2" } } diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 8e8818d..759c864 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -8,70 +8,82 @@ importers: .: dependencies: - '@angular/animations': - specifier: ^18.0.0 - version: 18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)) '@angular/common': - specifier: ^18.0.0 - version: 18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2) + specifier: ^19.2.0 + version: 19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) '@angular/compiler': - specifier: ^18.0.0 - version: 18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)) + specifier: ^19.2.0 + version: 19.2.15 '@angular/core': - specifier: ^18.0.0 - version: 18.2.14(rxjs@7.8.2)(zone.js@0.14.10) + specifier: ^19.2.0 + version: 19.2.15(rxjs@7.8.2)(zone.js@0.15.1) '@angular/forms': - specifier: ^18.0.0 - version: 18.2.14(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(@angular/platform-browser@18.2.14(@angular/animations@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(rxjs@7.8.2) + specifier: ^19.2.0 + version: 19.2.15(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.15(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) '@angular/platform-browser': - specifier: ^18.0.0 - version: 18.2.14(@angular/animations@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)) + specifier: ^19.2.0 + version: 19.2.15(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1)) '@angular/platform-browser-dynamic': - specifier: ^18.0.0 - version: 18.2.14(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/compiler@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(@angular/platform-browser@18.2.14(@angular/animations@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))) + specifier: ^19.2.0 + version: 19.2.15(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.15)(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.15(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))) '@angular/router': - specifier: ^18.0.0 - version: 18.2.14(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(@angular/platform-browser@18.2.14(@angular/animations@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(rxjs@7.8.2) - chart.js: - specifier: ^4.4.0 - version: 4.5.1 - ng2-charts: - specifier: ^6.0.0 - version: 6.0.1(@angular/cdk@20.2.13(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(@angular/platform-browser@18.2.14(@angular/animations@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(chart.js@4.5.1)(rxjs@7.8.2) + specifier: ^19.2.0 + version: 19.2.15(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.15(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + autoprefixer: + specifier: ^10.4.22 + version: 10.4.22(postcss@8.5.6) + postcss: + specifier: ^8.5.6 + version: 8.5.6 rxjs: specifier: ~7.8.0 version: 7.8.2 + tailwindcss: + specifier: ^3.4.18 + version: 3.4.18 tslib: specifier: ^2.3.0 version: 2.8.1 zone.js: - specifier: ~0.14.3 - version: 0.14.10 + specifier: ~0.15.0 + version: 0.15.1 devDependencies: '@angular-devkit/build-angular': - specifier: ^18.0.0 - version: 18.2.21(@angular/compiler-cli@18.2.14(@angular/compiler@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(typescript@5.4.5))(@types/node@20.19.25)(chokidar@3.6.0)(tailwindcss@3.4.18)(typescript@5.4.5) + specifier: ^19.2.19 + version: 19.2.19(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.7.3))(@angular/compiler@19.2.15)(@types/node@24.10.1)(chokidar@4.0.3)(jiti@1.21.7)(karma@6.4.4)(tailwindcss@3.4.18)(typescript@5.7.3)(vite@6.4.1(@types/node@24.10.1)(jiti@1.21.7)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)) '@angular/cli': - specifier: ^18.0.0 - version: 18.2.21(chokidar@3.6.0) + specifier: ^19.2.19 + version: 19.2.19(@types/node@24.10.1)(chokidar@4.0.3) '@angular/compiler-cli': - specifier: ^18.0.0 - version: 18.2.14(@angular/compiler@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(typescript@5.4.5) + specifier: ^19.2.0 + version: 19.2.15(@angular/compiler@19.2.15)(typescript@5.7.3) + '@types/jasmine': + specifier: ~5.1.0 + version: 5.1.13 '@types/node': - specifier: ^20.11.0 - version: 20.19.25 - autoprefixer: - specifier: ^10.4.19 - version: 10.4.22(postcss@8.5.6) - postcss: - specifier: ^8.4.38 - version: 8.5.6 - tailwindcss: - specifier: ^3.4.3 - version: 3.4.18 + specifier: ^24.10.1 + version: 24.10.1 + jasmine-core: + specifier: ~5.6.0 + version: 5.6.0 + karma: + specifier: ~6.4.0 + version: 6.4.4 + karma-chrome-launcher: + specifier: ~3.2.0 + version: 3.2.0 + karma-coverage: + specifier: ~2.2.0 + version: 2.2.1 + karma-jasmine: + specifier: ~5.1.0 + version: 5.1.0(karma@6.4.4) + karma-jasmine-html-reporter: + specifier: ~2.1.0 + version: 2.1.0(jasmine-core@5.6.0)(karma-jasmine@5.1.0(karma@6.4.4))(karma@6.4.4) typescript: - specifier: ~5.4.0 - version: 5.4.5 + specifier: ~5.7.2 + version: 5.7.3 packages: @@ -83,27 +95,28 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular-devkit/architect@0.1802.21': - resolution: {integrity: sha512-+Ll+xtpKwZ3iLWN/YypvnCZV/F0MVbP+/7ZpMR+Xv/uB0OmribhBVj9WGaCd9I/bGgoYBw8wBV/NFNCKkf0k3Q==} + '@angular-devkit/architect@0.1902.19': + resolution: {integrity: sha512-iexYDIYpGAeAU7T60bGcfrGwtq1bxpZixYxWuHYiaD1b5baQgNSfd1isGEOh37GgDNsf4In9i2LOLPm0wBdtgQ==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - '@angular-devkit/build-angular@18.2.21': - resolution: {integrity: sha512-0pJfURFpEUV2USgZ2TL3nNAaJmF9bICx9OVddBoC+F9FeOpVKxkcVIb+c8Km5zHFo1iyVtPZ6Rb25vFk9Zm/ug==} + '@angular-devkit/build-angular@19.2.19': + resolution: {integrity: sha512-uIxi6Vzss6+ycljVhkyPUPWa20w8qxJL9lEn0h6+sX/fhM8Djt0FHIuTQjoX58EoMaQ/1jrXaRaGimkbaFcG9A==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: - '@angular/compiler-cli': ^18.0.0 - '@angular/localize': ^18.0.0 - '@angular/platform-server': ^18.0.0 - '@angular/service-worker': ^18.0.0 - '@web/test-runner': ^0.18.0 + '@angular/compiler-cli': ^19.0.0 || ^19.2.0-next.0 + '@angular/localize': ^19.0.0 || ^19.2.0-next.0 + '@angular/platform-server': ^19.0.0 || ^19.2.0-next.0 + '@angular/service-worker': ^19.0.0 || ^19.2.0-next.0 + '@angular/ssr': ^19.2.19 + '@web/test-runner': ^0.20.0 browser-sync: ^3.0.2 jest: ^29.5.0 jest-environment-jsdom: ^29.5.0 karma: ^6.3.0 - ng-packagr: ^18.0.0 + ng-packagr: ^19.0.0 || ^19.2.0-next.0 protractor: ^7.0.0 - tailwindcss: ^2.0.0 || ^3.0.0 - typescript: '>=5.4 <5.6' + tailwindcss: ^2.0.0 || ^3.0.0 || ^4.0.0 + typescript: '>=5.5 <5.9' peerDependenciesMeta: '@angular/localize': optional: true @@ -111,6 +124,8 @@ packages: optional: true '@angular/service-worker': optional: true + '@angular/ssr': + optional: true '@web/test-runner': optional: true browser-sync: @@ -128,44 +143,42 @@ packages: tailwindcss: optional: true - '@angular-devkit/build-webpack@0.1802.21': - resolution: {integrity: sha512-2jSVRhA3N4Elg8OLcBktgi+CMSjlAm/bBQJE6TQYbdQWnniuT7JAWUHA/iPf7MYlQE5qj4rnAni1CI/c1Bk4HQ==} + '@angular-devkit/build-webpack@0.1902.19': + resolution: {integrity: sha512-x2tlGg5CsUveFzuRuqeHknSbGirSAoRynEh+KqPRGK0G3WpMViW/M8SuVurecasegfIrDWtYZ4FnVxKqNbKwXQ==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: webpack: ^5.30.0 webpack-dev-server: ^5.0.2 - '@angular-devkit/core@18.2.21': - resolution: {integrity: sha512-Lno6GNbJME85wpc/uqn+wamBxvfZJZFYSH8+oAkkyjU/hk8r5+X8DuyqsKAa0m8t46zSTUsonHsQhVe5vgrZeQ==} + '@angular-devkit/core@19.2.19': + resolution: {integrity: sha512-JbLL+4IMLMBgjLZlnPG4lYDfz4zGrJ/s6Aoon321NJKuw1Kb1k5KpFu9dUY0BqLIe8xPQ2UJBpI+xXdK5MXMHQ==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: - chokidar: ^3.5.2 + chokidar: ^4.0.0 peerDependenciesMeta: chokidar: optional: true - '@angular-devkit/schematics@18.2.21': - resolution: {integrity: sha512-yuC2vN4VL48JhnsaOa9J/o0Jl+cxOklRNQp5J2/ypMuRROaVCrZAPiX+ChSHh++kHYMpj8+ggNrrUwRNfMKACQ==} + '@angular-devkit/schematics@19.2.19': + resolution: {integrity: sha512-J4Jarr0SohdrHcb40gTL4wGPCQ952IMWF1G/MSAQfBAPvA9ZKApYhpxcY7PmehVePve+ujpus1dGsJ7dPxz8Kg==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - '@angular/animations@18.2.14': - resolution: {integrity: sha512-Kp/MWShoYYO+R3lrrZbZgszbbLGVXHB+39mdJZwnIuZMDkeL3JsIBlSOzyJRTnpS1vITc+9jgHvP/6uKbMrW1Q==} - engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} - peerDependencies: - '@angular/core': 18.2.14 - - '@angular/build@18.2.21': - resolution: {integrity: sha512-uvq3qP4cByJrUkV1ri0v3x6LxOFt4fDKiQdNwbQAqdxtfRs3ssEIoCGns4t89sTWXv6VZWBNDcDIKK9/Fa9mmg==} + '@angular/build@19.2.19': + resolution: {integrity: sha512-SFzQ1bRkNFiOVu+aaz+9INmts7tDUrsHLEr9HmARXr9qk5UmR8prlw39p2u+Bvi6/lCiJ18TZMQQl9mGyr63lg==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: - '@angular/compiler-cli': ^18.0.0 - '@angular/localize': ^18.0.0 - '@angular/platform-server': ^18.0.0 - '@angular/service-worker': ^18.0.0 + '@angular/compiler': ^19.0.0 || ^19.2.0-next.0 + '@angular/compiler-cli': ^19.0.0 || ^19.2.0-next.0 + '@angular/localize': ^19.0.0 || ^19.2.0-next.0 + '@angular/platform-server': ^19.0.0 || ^19.2.0-next.0 + '@angular/service-worker': ^19.0.0 || ^19.2.0-next.0 + '@angular/ssr': ^19.2.19 + karma: ^6.4.0 less: ^4.2.0 + ng-packagr: ^19.0.0 || ^19.2.0-next.0 postcss: ^8.4.0 - tailwindcss: ^2.0.0 || ^3.0.0 - typescript: '>=5.4 <5.6' + tailwindcss: ^2.0.0 || ^3.0.0 || ^4.0.0 + typescript: '>=5.5 <5.9' peerDependenciesMeta: '@angular/localize': optional: true @@ -173,92 +186,86 @@ packages: optional: true '@angular/service-worker': optional: true + '@angular/ssr': + optional: true + karma: + optional: true less: optional: true + ng-packagr: + optional: true postcss: optional: true tailwindcss: optional: true - '@angular/cdk@20.2.13': - resolution: {integrity: sha512-h1jTkCmJ/rEQQMkxgKFMCBOrMfjZEnppgdekNmSTerwdVp4vdosTDTzFH/kwiOGFeRClffmvqQ2XLG8mQOKOtA==} - peerDependencies: - '@angular/common': ^20.0.0 || ^21.0.0 - '@angular/core': ^20.0.0 || ^21.0.0 - rxjs: ^6.5.3 || ^7.4.0 - - '@angular/cli@18.2.21': - resolution: {integrity: sha512-efweY4p8awRTbHs+HKdg6s44hl7Y0gdVlXYi3HeY8Z5JDC0abbka0K6sA/MrV9AXvn/5ovxYbxiL3AsOApjTpg==} + '@angular/cli@19.2.19': + resolution: {integrity: sha512-e9tAzFNOL4mMWfMnpC9Up83OCTOp2siIj8W41FCp8jfoEnw79AXDDLh3d70kOayiObchksTJVShslTogLUyhMw==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true - '@angular/common@18.2.14': - resolution: {integrity: sha512-ZPRswzaVRiqcfZoowuAM22Hr2/z10ajWOUoFDoQ9tWqz/fH/773kJv2F9VvePIekgNPCzaizqv9gF6tGNqaAwg==} + '@angular/common@19.2.15': + resolution: {integrity: sha512-aVa/ctBYH/4qgA7r4sS7TV+/DzRYmcS+3d6l89pNKUXkI8gpmsd+r3FjccaemX4Wqru1QOrMvC+i+e7IBIVv0g==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} peerDependencies: - '@angular/core': 18.2.14 + '@angular/core': 19.2.15 rxjs: ^6.5.3 || ^7.4.0 - '@angular/compiler-cli@18.2.14': - resolution: {integrity: sha512-BmmjyrFSBSYkm0tBSqpu4cwnJX/b/XvhM36mj2k8jah3tNS5zLDDx5w6tyHmaPJa/1D95MlXx2h6u7K9D+Mhew==} + '@angular/compiler-cli@19.2.15': + resolution: {integrity: sha512-4r5tvGA2Ok3o8wROZBkF9qNKS7L0AEpdBIkAVJbLw2rBY2SlyycFIRYyV2+D1lJ1jq/f9U7uN6oon0MjTvNYkA==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} hasBin: true peerDependencies: - '@angular/compiler': 18.2.14 - typescript: '>=5.4 <5.6' + '@angular/compiler': 19.2.15 + typescript: '>=5.5 <5.9' - '@angular/compiler@18.2.14': - resolution: {integrity: sha512-Mpq3v/mztQzGAQAAFV+wAI1hlXxZ0m8eDBgaN2kD3Ue+r4S6bLm1Vlryw0iyUnt05PcFIdxPT6xkcphq5pl6lw==} + '@angular/compiler@19.2.15': + resolution: {integrity: sha512-hMHZU6/03xG0tbPDIm1hbVSTFLnRkGYfh+xdBwUMnIFYYTS0QJ2hdPfEZKCJIXm+fz9IAI5MPdDTfeyp0sgaHQ==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} - peerDependencies: - '@angular/core': 18.2.14 - peerDependenciesMeta: - '@angular/core': - optional: true - '@angular/core@18.2.14': - resolution: {integrity: sha512-BIPrCs93ZZTY9ym7yfoTgAQ5rs706yoYeAdrgc8kh/bDbM9DawxKlgeKBx2FLt09Y0YQ1bFhKVp0cV4gDEaMxQ==} + '@angular/core@19.2.15': + resolution: {integrity: sha512-PxhzCwwm23N4Mq6oV7UPoYiJF4r6FzGhRSxOBBlEp322k7zEQbIxd/XO6F3eoG73qC1UsOXMYYv6GnQpx42y3A==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} peerDependencies: rxjs: ^6.5.3 || ^7.4.0 - zone.js: ~0.14.10 + zone.js: ~0.15.0 - '@angular/forms@18.2.14': - resolution: {integrity: sha512-fZVwXctmBJa5VdopJae/T9MYKPXNd04+6j4k/6X819y+9fiyWLJt2QicSc5Rc+YD9mmhXag3xaljlrnotf9VGA==} + '@angular/forms@19.2.15': + resolution: {integrity: sha512-pZDElcYPmNzPxvWJpZQCIizsNApDIfk9xLJE4I8hzLISfWGbQvfjuuarDAuQZEXudeLXoDOstDXkDja40muLGg==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} peerDependencies: - '@angular/common': 18.2.14 - '@angular/core': 18.2.14 - '@angular/platform-browser': 18.2.14 + '@angular/common': 19.2.15 + '@angular/core': 19.2.15 + '@angular/platform-browser': 19.2.15 rxjs: ^6.5.3 || ^7.4.0 - '@angular/platform-browser-dynamic@18.2.14': - resolution: {integrity: sha512-QOv+o89u8HLN0LG8faTIVHKBxfkOBHVDB0UuXy19+HJofWZGGvho+vGjV0/IAkhZnMC4Sxdoy/mOHP2ytALX3A==} + '@angular/platform-browser-dynamic@19.2.15': + resolution: {integrity: sha512-dKy0SS395FCh8cW9AQ8nf4Wn3XlONaH7z50T1bGxm3eOoRqjxJYyIeIlEbDdJakMz4QPR3dGr81HleZd8TJumQ==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} peerDependencies: - '@angular/common': 18.2.14 - '@angular/compiler': 18.2.14 - '@angular/core': 18.2.14 - '@angular/platform-browser': 18.2.14 + '@angular/common': 19.2.15 + '@angular/compiler': 19.2.15 + '@angular/core': 19.2.15 + '@angular/platform-browser': 19.2.15 - '@angular/platform-browser@18.2.14': - resolution: {integrity: sha512-W+JTxI25su3RiZVZT3Yrw6KNUCmOIy7OZIZ+612skPgYK2f2qil7VclnW1oCwG896h50cMJU/lnAfxZxefQgyQ==} + '@angular/platform-browser@19.2.15': + resolution: {integrity: sha512-OelQ6weCjon8kZD8kcqNzwugvZJurjS3uMJCwsA2vXmP/3zJ31SWtNqE2zLT1R2csVuwnp0h+nRMgq+pINU7Rg==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} peerDependencies: - '@angular/animations': 18.2.14 - '@angular/common': 18.2.14 - '@angular/core': 18.2.14 + '@angular/animations': 19.2.15 + '@angular/common': 19.2.15 + '@angular/core': 19.2.15 peerDependenciesMeta: '@angular/animations': optional: true - '@angular/router@18.2.14': - resolution: {integrity: sha512-v/gweh8MBjjDfh1QssuyjISa+6SVVIvIZox7MaMs81RkaoVHwS9grDtPud1pTKHzms2KxSVpvwwyvkRJQplueg==} + '@angular/router@19.2.15': + resolution: {integrity: sha512-0TM1D8S7RQ00drKy7hA/ZLBY14dUBqFBgm06djcNcOjNzVAtgkeV0i+0Smq9tCC7UsGKdpZu4RgfYjHATBNlTQ==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} peerDependencies: - '@angular/common': 18.2.14 - '@angular/core': 18.2.14 - '@angular/platform-browser': 18.2.14 + '@angular/common': 19.2.15 + '@angular/core': 19.2.15 + '@angular/platform-browser': 19.2.15 rxjs: ^6.5.3 || ^7.4.0 '@babel/code-frame@7.27.1': @@ -269,14 +276,18 @@ packages: resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} engines: {node: '>=6.9.0'} - '@babel/core@7.25.2': - resolution: {integrity: sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==} - engines: {node: '>=6.9.0'} - '@babel/core@7.26.10': resolution: {integrity: sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==} engines: {node: '>=6.9.0'} + '@babel/core@7.26.9': + resolution: {integrity: sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.5': + resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} + engines: {node: '>=6.9.0'} + '@babel/generator@7.26.10': resolution: {integrity: sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==} engines: {node: '>=6.9.0'} @@ -285,10 +296,6 @@ packages: resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} engines: {node: '>=6.9.0'} - '@babel/helper-annotate-as-pure@7.24.7': - resolution: {integrity: sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==} - engines: {node: '>=6.9.0'} - '@babel/helper-annotate-as-pure@7.25.9': resolution: {integrity: sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==} engines: {node: '>=6.9.0'} @@ -431,8 +438,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-attributes@7.24.7': - resolution: {integrity: sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==} + '@babel/plugin-syntax-import-attributes@7.26.0': + resolution: {integrity: sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -782,360 +789,319 @@ packages: resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} - '@discoveryjs/json-ext@0.6.1': - resolution: {integrity: sha512-boghen8F0Q8D+0/Q1/1r6DUEieUJ8w2a1gIknExMSHBsJFOr2+0KUfHiVYBvucPwl3+RU5PFBK833FjFCh3BhA==} + '@colors/colors@1.5.0': + resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} + engines: {node: '>=0.1.90'} + + '@discoveryjs/json-ext@0.6.3': + resolution: {integrity: sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==} engines: {node: '>=14.17.0'} - '@esbuild/aix-ppc64@0.21.5': - resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [aix] - - '@esbuild/aix-ppc64@0.23.0': - resolution: {integrity: sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==} + '@esbuild/aix-ppc64@0.25.4': + resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.21.5': - resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm64@0.23.0': - resolution: {integrity: sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==} + '@esbuild/android-arm64@0.25.4': + resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.21.5': - resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} - engines: {node: '>=12'} - cpu: [arm] - os: [android] - - '@esbuild/android-arm@0.23.0': - resolution: {integrity: sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==} + '@esbuild/android-arm@0.25.4': + resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.21.5': - resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} - engines: {node: '>=12'} - cpu: [x64] - os: [android] - - '@esbuild/android-x64@0.23.0': - resolution: {integrity: sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==} + '@esbuild/android-x64@0.25.4': + resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.21.5': - resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} - engines: {node: '>=12'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-arm64@0.23.0': - resolution: {integrity: sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==} + '@esbuild/darwin-arm64@0.25.4': + resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.21.5': - resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} - engines: {node: '>=12'} - cpu: [x64] - os: [darwin] - - '@esbuild/darwin-x64@0.23.0': - resolution: {integrity: sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==} + '@esbuild/darwin-x64@0.25.4': + resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.21.5': - resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} - engines: {node: '>=12'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-arm64@0.23.0': - resolution: {integrity: sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==} + '@esbuild/freebsd-arm64@0.25.4': + resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.21.5': - resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.23.0': - resolution: {integrity: sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==} + '@esbuild/freebsd-x64@0.25.4': + resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.21.5': - resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} - engines: {node: '>=12'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm64@0.23.0': - resolution: {integrity: sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==} + '@esbuild/linux-arm64@0.25.4': + resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.21.5': - resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} - engines: {node: '>=12'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-arm@0.23.0': - resolution: {integrity: sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==} + '@esbuild/linux-arm@0.25.4': + resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.21.5': - resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} - engines: {node: '>=12'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-ia32@0.23.0': - resolution: {integrity: sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==} + '@esbuild/linux-ia32@0.25.4': + resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.21.5': - resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} - engines: {node: '>=12'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-loong64@0.23.0': - resolution: {integrity: sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==} + '@esbuild/linux-loong64@0.25.4': + resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.21.5': - resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} - engines: {node: '>=12'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-mips64el@0.23.0': - resolution: {integrity: sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==} + '@esbuild/linux-mips64el@0.25.4': + resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.21.5': - resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} - engines: {node: '>=12'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-ppc64@0.23.0': - resolution: {integrity: sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==} + '@esbuild/linux-ppc64@0.25.4': + resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.21.5': - resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} - engines: {node: '>=12'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-riscv64@0.23.0': - resolution: {integrity: sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==} + '@esbuild/linux-riscv64@0.25.4': + resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.21.5': - resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} - engines: {node: '>=12'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-s390x@0.23.0': - resolution: {integrity: sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==} + '@esbuild/linux-s390x@0.25.4': + resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.21.5': - resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} - engines: {node: '>=12'} - cpu: [x64] - os: [linux] - - '@esbuild/linux-x64@0.23.0': - resolution: {integrity: sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==} + '@esbuild/linux-x64@0.25.4': + resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.21.5': - resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} - engines: {node: '>=12'} - cpu: [x64] + '@esbuild/netbsd-arm64@0.25.4': + resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==} + engines: {node: '>=18'} + cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.23.0': - resolution: {integrity: sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==} + '@esbuild/netbsd-x64@0.25.4': + resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.23.0': - resolution: {integrity: sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==} + '@esbuild/openbsd-arm64@0.25.4': + resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.21.5': - resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} - engines: {node: '>=12'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.23.0': - resolution: {integrity: sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==} + '@esbuild/openbsd-x64@0.25.4': + resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.21.5': - resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} - engines: {node: '>=12'} - cpu: [x64] - os: [sunos] - - '@esbuild/sunos-x64@0.23.0': - resolution: {integrity: sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==} + '@esbuild/sunos-x64@0.25.4': + resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.21.5': - resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} - engines: {node: '>=12'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-arm64@0.23.0': - resolution: {integrity: sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==} + '@esbuild/win32-arm64@0.25.4': + resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.21.5': - resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} - engines: {node: '>=12'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-ia32@0.23.0': - resolution: {integrity: sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==} + '@esbuild/win32-ia32@0.25.4': + resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.21.5': - resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} - engines: {node: '>=12'} - cpu: [x64] - os: [win32] - - '@esbuild/win32-x64@0.23.0': - resolution: {integrity: sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==} + '@esbuild/win32-x64@0.25.4': + resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@inquirer/checkbox@2.5.0': - resolution: {integrity: sha512-sMgdETOfi2dUHT8r7TT1BTKOwNvdDGFDXYWtQ2J69SvlYNntk9I/gJe7r5yvMwwsuKnYbuRs3pNhx4tgNck5aA==} + '@inquirer/ansi@1.0.2': + resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} engines: {node: '>=18'} - '@inquirer/confirm@3.1.22': - resolution: {integrity: sha512-gsAKIOWBm2Q87CDfs9fEo7wJT3fwWIJfnDGMn9Qy74gBnNFOACDNfhUzovubbJjWnKLGBln7/NcSmZwj5DuEXg==} + '@inquirer/checkbox@4.3.2': + resolution: {integrity: sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==} engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/confirm@3.2.0': - resolution: {integrity: sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==} + '@inquirer/confirm@5.1.21': + resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==} engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/core@9.2.1': - resolution: {integrity: sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==} + '@inquirer/confirm@5.1.6': + resolution: {integrity: sha512-6ZXYK3M1XmaVBZX6FCfChgtponnL0R6I7k8Nu+kaoNkT828FVZTcca1MqmWQipaW2oNREQl5AaPCUOOCVNdRMw==} engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/editor@2.2.0': - resolution: {integrity: sha512-9KHOpJ+dIL5SZli8lJ6xdaYLPPzB8xB9GZItg39MBybzhxA16vxmszmQFrRwbOA918WA2rvu8xhDEg/p6LXKbw==} + '@inquirer/core@10.3.2': + resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==} engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/expand@2.3.0': - resolution: {integrity: sha512-qnJsUcOGCSG1e5DTOErmv2BPQqrtT6uzqn1vI/aYGiPKq+FgslGZmtdnXbhuI7IlT7OByDoEEqdnhUnVR2hhLw==} + '@inquirer/editor@4.2.23': + resolution: {integrity: sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==} engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/expand@4.0.23': + resolution: {integrity: sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/external-editor@1.0.3': + resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} + engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true '@inquirer/figures@1.0.15': resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} engines: {node: '>=18'} - '@inquirer/input@2.3.0': - resolution: {integrity: sha512-XfnpCStx2xgh1LIRqPXrTNEEByqQWoxsWYzNRSEUxJ5c6EQlhMogJ3vHKu8aXuTacebtaZzMAHwEL0kAflKOBw==} + '@inquirer/input@4.3.1': + resolution: {integrity: sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==} engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/number@1.1.0': - resolution: {integrity: sha512-ilUnia/GZUtfSZy3YEErXLJ2Sljo/mf9fiKc08n18DdwdmDbOzRcTv65H1jjDvlsAuvdFXf4Sa/aL7iw/NanVA==} + '@inquirer/number@3.0.23': + resolution: {integrity: sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==} engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/password@2.2.0': - resolution: {integrity: sha512-5otqIpgsPYIshqhgtEwSspBQE40etouR8VIxzpJkv9i0dVHIpyhiivbkH9/dGiMLdyamT54YRdGJLfl8TFnLHg==} + '@inquirer/password@4.0.23': + resolution: {integrity: sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==} engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/prompts@5.3.8': - resolution: {integrity: sha512-b2BudQY/Si4Y2a0PdZZL6BeJtl8llgeZa7U2j47aaJSCeAl1e4UI7y8a9bSkO3o/ZbZrgT5muy/34JbsjfIWxA==} + '@inquirer/prompts@7.3.2': + resolution: {integrity: sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==} engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/rawlist@2.3.0': - resolution: {integrity: sha512-zzfNuINhFF7OLAtGHfhwOW2TlYJyli7lOUoJUXw/uyklcwalV6WRXBXtFIicN8rTRK1XTiPWB4UY+YuW8dsnLQ==} + '@inquirer/rawlist@4.1.11': + resolution: {integrity: sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==} engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/search@1.1.0': - resolution: {integrity: sha512-h+/5LSj51dx7hp5xOn4QFnUaKeARwUCLs6mIhtkJ0JYPBLmEYjdHSYh7I6GrLg9LwpJ3xeX0FZgAG1q0QdCpVQ==} + '@inquirer/search@3.2.2': + resolution: {integrity: sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==} engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true - '@inquirer/select@2.5.0': - resolution: {integrity: sha512-YmDobTItPP3WcEI86GvPo+T2sRHkxxOq/kXmsBjHS5BVXUgvgZ5AfJjkvQvZr03T81NnI3KrrRuMzeuYUQRFOA==} + '@inquirer/select@4.4.2': + resolution: {integrity: sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==} engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true '@inquirer/type@1.5.5': resolution: {integrity: sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==} engines: {node: '>=18'} - '@inquirer/type@2.0.0': - resolution: {integrity: sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==} + '@inquirer/type@3.0.10': + resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==} engines: {node: '>=18'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@isaacs/fs-minipass@4.0.1': + resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==} + engines: {node: '>=18.0.0'} + '@istanbuljs/schema@0.1.3': resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} @@ -1143,6 +1109,9 @@ packages: '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} @@ -1192,45 +1161,42 @@ packages: peerDependencies: tslib: '2' - '@kurkle/color@0.3.4': - resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==} - '@leichtgewicht/ip-codec@2.0.5': resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} - '@listr2/prompt-adapter-inquirer@2.0.15': - resolution: {integrity: sha512-MZrGem/Ujjd4cPTLYDfCZK2iKKeiO/8OX13S6jqxldLs0Prf2aGqVlJ77nMBqMv7fzqgXEgjrNHLXcKR8l9lOg==} + '@listr2/prompt-adapter-inquirer@2.0.18': + resolution: {integrity: sha512-0hz44rAcrphyXcA8IS7EJ2SCoaBZD2u5goE8S/e+q/DL+dOGpqpcLidVOFeLG3VgML62SXmfRLAhWt0zL1oW4Q==} engines: {node: '>=18.0.0'} peerDependencies: - '@inquirer/prompts': '>= 3 < 6' + '@inquirer/prompts': '>= 3 < 8' - '@lmdb/lmdb-darwin-arm64@3.0.13': - resolution: {integrity: sha512-uiKPB0Fv6WEEOZjruu9a6wnW/8jrjzlZbxXscMB8kuCJ1k6kHpcBnuvaAWcqhbI7rqX5GKziwWEdD+wi2gNLfA==} + '@lmdb/lmdb-darwin-arm64@3.2.6': + resolution: {integrity: sha512-yF/ih9EJJZc72psFQbwnn8mExIWfTnzWJg+N02hnpXtDPETYLmQswIMBn7+V88lfCaFrMozJsUvcEQIkEPU0Gg==} cpu: [arm64] os: [darwin] - '@lmdb/lmdb-darwin-x64@3.0.13': - resolution: {integrity: sha512-bEVIIfK5mSQoG1R19qA+fJOvCB+0wVGGnXHT3smchBVahYBdlPn2OsZZKzlHWfb1E+PhLBmYfqB5zQXFP7hJig==} + '@lmdb/lmdb-darwin-x64@3.2.6': + resolution: {integrity: sha512-5BbCumsFLbCi586Bb1lTWQFkekdQUw8/t8cy++Uq251cl3hbDIGEwD9HAwh8H6IS2F6QA9KdKmO136LmipRNkg==} cpu: [x64] os: [darwin] - '@lmdb/lmdb-linux-arm64@3.0.13': - resolution: {integrity: sha512-afbVrsMgZ9dUTNUchFpj5VkmJRxvht/u335jUJ7o23YTbNbnpmXif3VKQGCtnjSh+CZaqm6N3CPG8KO3zwyZ1Q==} + '@lmdb/lmdb-linux-arm64@3.2.6': + resolution: {integrity: sha512-l5VmJamJ3nyMmeD1ANBQCQqy7do1ESaJQfKPSm2IG9/ADZryptTyCj8N6QaYgIWewqNUrcbdMkJajRQAt5Qjfg==} cpu: [arm64] os: [linux] - '@lmdb/lmdb-linux-arm@3.0.13': - resolution: {integrity: sha512-Yml1KlMzOnXj/tnW7yX8U78iAzTk39aILYvCPbqeewAq1kSzl+w59k/fiVkTBfvDi/oW/5YRxL+Fq+Y1Fr1r2Q==} + '@lmdb/lmdb-linux-arm@3.2.6': + resolution: {integrity: sha512-+6XgLpMb7HBoWxXj+bLbiiB4s0mRRcDPElnRS3LpWRzdYSe+gFk5MT/4RrVNqd2MESUDmb53NUXw1+BP69bjiQ==} cpu: [arm] os: [linux] - '@lmdb/lmdb-linux-x64@3.0.13': - resolution: {integrity: sha512-vOtxu0xC0SLdQ2WRXg8Qgd8T32ak4SPqk5zjItRszrJk2BdeXqfGxBJbP7o4aOvSPSmSSv46Lr1EP4HXU8v7Kg==} + '@lmdb/lmdb-linux-x64@3.2.6': + resolution: {integrity: sha512-nDYT8qN9si5+onHYYaI4DiauDMx24OAiuZAUsEqrDy+ja/3EbpXPX/VAkMV8AEaQhy3xc4dRC+KcYIvOFefJ4Q==} cpu: [x64] os: [linux] - '@lmdb/lmdb-win32-x64@3.0.13': - resolution: {integrity: sha512-UCrMJQY/gJnOl3XgbWRZZUvGGBuKy6i0YNSptgMzHBjs+QYDYR1Mt/RLTOPy4fzzves65O1EDmlL//OzEqoLlA==} + '@lmdb/lmdb-win32-x64@3.2.6': + resolution: {integrity: sha512-XlqVtILonQnG+9fH2N3Aytria7P/1fwDgDhl29rde96uH2sLB8CHORIf2PfuLVzFQJ7Uqp8py9AYwr3ZUCFfWg==} cpu: [x64] os: [win32] @@ -1264,12 +1230,118 @@ packages: cpu: [x64] os: [win32] - '@ngtools/webpack@18.2.21': - resolution: {integrity: sha512-mfLT7lXbyJRlsazuPyuF5AGsMcgzRJRwsDlgxFbiy1DBlaF1chRFsXrKYj1gQ/WXQWNcEd11aedU0Rt+iCNDVw==} + '@napi-rs/nice-android-arm-eabi@1.1.1': + resolution: {integrity: sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [android] + + '@napi-rs/nice-android-arm64@1.1.1': + resolution: {integrity: sha512-blG0i7dXgbInN5urONoUCNf+DUEAavRffrO7fZSeoRMJc5qD+BJeNcpr54msPF6qfDD6kzs9AQJogZvT2KD5nw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@napi-rs/nice-darwin-arm64@1.1.1': + resolution: {integrity: sha512-s/E7w45NaLqTGuOjC2p96pct4jRfo61xb9bU1unM/MJ/RFkKlJyJDx7OJI/O0ll/hrfpqKopuAFDV8yo0hfT7A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@napi-rs/nice-darwin-x64@1.1.1': + resolution: {integrity: sha512-dGoEBnVpsdcC+oHHmW1LRK5eiyzLwdgNQq3BmZIav+9/5WTZwBYX7r5ZkQC07Nxd3KHOCkgbHSh4wPkH1N1LiQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@napi-rs/nice-freebsd-x64@1.1.1': + resolution: {integrity: sha512-kHv4kEHAylMYmlNwcQcDtXjklYp4FCf0b05E+0h6nDHsZ+F0bDe04U/tXNOqrx5CmIAth4vwfkjjUmp4c4JktQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@napi-rs/nice-linux-arm-gnueabihf@1.1.1': + resolution: {integrity: sha512-E1t7K0efyKXZDoZg1LzCOLxgolxV58HCkaEkEvIYQx12ht2pa8hoBo+4OB3qh7e+QiBlp1SRf+voWUZFxyhyqg==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@napi-rs/nice-linux-arm64-gnu@1.1.1': + resolution: {integrity: sha512-CIKLA12DTIZlmTaaKhQP88R3Xao+gyJxNWEn04wZwC2wmRapNnxCUZkVwggInMJvtVElA+D4ZzOU5sX4jV+SmQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@napi-rs/nice-linux-arm64-musl@1.1.1': + resolution: {integrity: sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@napi-rs/nice-linux-ppc64-gnu@1.1.1': + resolution: {integrity: sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==} + engines: {node: '>= 10'} + cpu: [ppc64] + os: [linux] + + '@napi-rs/nice-linux-riscv64-gnu@1.1.1': + resolution: {integrity: sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + + '@napi-rs/nice-linux-s390x-gnu@1.1.1': + resolution: {integrity: sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==} + engines: {node: '>= 10'} + cpu: [s390x] + os: [linux] + + '@napi-rs/nice-linux-x64-gnu@1.1.1': + resolution: {integrity: sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@napi-rs/nice-linux-x64-musl@1.1.1': + resolution: {integrity: sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@napi-rs/nice-openharmony-arm64@1.1.1': + resolution: {integrity: sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [openharmony] + + '@napi-rs/nice-win32-arm64-msvc@1.1.1': + resolution: {integrity: sha512-uoTb4eAvM5B2aj/z8j+Nv8OttPf2m+HVx3UjA5jcFxASvNhQriyCQF1OB1lHL43ZhW+VwZlgvjmP5qF3+59atA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@napi-rs/nice-win32-ia32-msvc@1.1.1': + resolution: {integrity: sha512-CNQqlQT9MwuCsg1Vd/oKXiuH+TcsSPJmlAFc5frFyX/KkOh0UpBLEj7aoY656d5UKZQMQFP7vJNa1DNUNORvug==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@napi-rs/nice-win32-x64-msvc@1.1.1': + resolution: {integrity: sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@napi-rs/nice@1.1.1': + resolution: {integrity: sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==} + engines: {node: '>= 10'} + + '@ngtools/webpack@19.2.19': + resolution: {integrity: sha512-R9aeTrOBiRVl8I698JWPniUAAEpSvzc8SUGWSM5UXWMcHnWqd92cOnJJ1aXDGJZKXrbhMhCBx9Dglmcks5IDpg==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: - '@angular/compiler-cli': ^18.0.0 - typescript: '>=5.4 <5.6' + '@angular/compiler-cli': ^19.0.0 || ^19.2.0-next.0 + typescript: '>=5.5 <5.9' webpack: ^5.54.0 '@nodelib/fs.scandir@2.1.5': @@ -1284,166 +1356,376 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@npmcli/agent@2.2.2': - resolution: {integrity: sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==} - engines: {node: ^16.14.0 || >=18.0.0} + '@npmcli/agent@3.0.0': + resolution: {integrity: sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==} + engines: {node: ^18.17.0 || >=20.5.0} - '@npmcli/fs@3.1.1': - resolution: {integrity: sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@npmcli/fs@4.0.0': + resolution: {integrity: sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==} + engines: {node: ^18.17.0 || >=20.5.0} - '@npmcli/git@5.0.8': - resolution: {integrity: sha512-liASfw5cqhjNW9UFd+ruwwdEf/lbOAQjLL2XY2dFW/bkJheXDYZgOyul/4gVvEV4BWkTXjYGmDqMw9uegdbJNQ==} - engines: {node: ^16.14.0 || >=18.0.0} + '@npmcli/git@6.0.3': + resolution: {integrity: sha512-GUYESQlxZRAdhs3UhbB6pVRNUELQOHXwK9ruDkwmCv2aZ5y0SApQzUJCg02p3A7Ue2J5hxvlk1YI53c00NmRyQ==} + engines: {node: ^18.17.0 || >=20.5.0} - '@npmcli/installed-package-contents@2.1.0': - resolution: {integrity: sha512-c8UuGLeZpm69BryRykLuKRyKFZYJsZSCT4aVY5ds4omyZqJ172ApzgfKJ5eV/r3HgLdUYgFVe54KSFVjKoe27w==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@npmcli/installed-package-contents@3.0.0': + resolution: {integrity: sha512-fkxoPuFGvxyrH+OQzyTkX2LUEamrF4jZSmxjAtPPHHGO0dqsQ8tTKjnIS8SAnPHdk2I03BDtSMR5K/4loKg79Q==} + engines: {node: ^18.17.0 || >=20.5.0} hasBin: true - '@npmcli/node-gyp@3.0.0': - resolution: {integrity: sha512-gp8pRXC2oOxu0DUE1/M3bYtb1b3/DbJ5aM113+XJBgfXdussRAsX0YOrOhdd8WvnAR6auDBvJomGAkLKA5ydxA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + '@npmcli/node-gyp@4.0.0': + resolution: {integrity: sha512-+t5DZ6mO/QFh78PByMq1fGSAub/agLJZDRfJRMeOSNCt8s9YVlTjmGpIPwPhvXTGUIJk+WszlT0rQa1W33yzNA==} + engines: {node: ^18.17.0 || >=20.5.0} - '@npmcli/package-json@5.2.1': - resolution: {integrity: sha512-f7zYC6kQautXHvNbLEWgD/uGu1+xCn9izgqBfgItWSx22U0ZDekxN08A1vM8cTxj/cRVe0Q94Ode+tdoYmIOOQ==} - engines: {node: ^16.14.0 || >=18.0.0} + '@npmcli/package-json@6.2.0': + resolution: {integrity: sha512-rCNLSB/JzNvot0SEyXqWZ7tX2B5dD2a1br2Dp0vSYVo5jh8Z0EZ7lS9TsZ1UtziddB1UfNUaMCc538/HztnJGA==} + engines: {node: ^18.17.0 || >=20.5.0} - '@npmcli/promise-spawn@7.0.2': - resolution: {integrity: sha512-xhfYPXoV5Dy4UkY0D+v2KkwvnDfiA/8Mt3sWCGI/hM03NsYIH8ZaG6QzS9x7pje5vHZBZJ2v6VRFVTWACnqcmQ==} - engines: {node: ^16.14.0 || >=18.0.0} + '@npmcli/promise-spawn@8.0.3': + resolution: {integrity: sha512-Yb00SWaL4F8w+K8YGhQ55+xE4RUNdMHV43WZGsiTM92gS+lC0mGsn7I4hLug7pbao035S6bj3Y3w0cUNGLfmkg==} + engines: {node: ^18.17.0 || >=20.5.0} - '@npmcli/redact@2.0.1': - resolution: {integrity: sha512-YgsR5jCQZhVmTJvjduTOIHph0L73pK8xwMVaDY0PatySqVM9AZj93jpoXYSJqfHFxFkN9dmqTw6OiqExsS3LPw==} - engines: {node: ^16.14.0 || >=18.0.0} + '@npmcli/redact@3.2.2': + resolution: {integrity: sha512-7VmYAmk4csGv08QzrDKScdzn11jHPFGyqJW39FyPgPuAp3zIaUmuCo1yxw9aGs+NEJuTGQ9Gwqpt93vtJubucg==} + engines: {node: ^18.17.0 || >=20.5.0} - '@npmcli/run-script@8.1.0': - resolution: {integrity: sha512-y7efHHwghQfk28G2z3tlZ67pLG0XdfYbcVG26r7YIXALRsrVQcTq4/tdenSmdOrEsNahIYA/eh8aEVROWGFUDg==} - engines: {node: ^16.14.0 || >=18.0.0} + '@npmcli/run-script@9.1.0': + resolution: {integrity: sha512-aoNSbxtkePXUlbZB+anS1LqsJdctG5n3UVhfU47+CDdwMi6uNTBMF9gPcQRnqghQd2FGzcwwIFBruFMxjhBewg==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@parcel/watcher-android-arm64@2.5.1': + resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.5.1': + resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.5.1': + resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-freebsd-x64@2.5.1': + resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [freebsd] + + '@parcel/watcher-linux-arm-glibc@2.5.1': + resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm-musl@2.5.1': + resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-arm64-musl@2.5.1': + resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-x64-glibc@2.5.1': + resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-linux-x64-musl@2.5.1': + resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-win32-arm64@2.5.1': + resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-ia32@2.5.1': + resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} + engines: {node: '>= 10.0.0'} + cpu: [ia32] + os: [win32] + + '@parcel/watcher-win32-x64@2.5.1': + resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.5.1': + resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} + engines: {node: '>= 10.0.0'} '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@rollup/rollup-android-arm-eabi@4.22.4': - resolution: {integrity: sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==} + '@rollup/rollup-android-arm-eabi@4.34.8': + resolution: {integrity: sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.22.4': - resolution: {integrity: sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==} + '@rollup/rollup-android-arm-eabi@4.53.3': + resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.34.8': + resolution: {integrity: sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.22.4': - resolution: {integrity: sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==} + '@rollup/rollup-android-arm64@4.53.3': + resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.34.8': + resolution: {integrity: sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.22.4': - resolution: {integrity: sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==} + '@rollup/rollup-darwin-arm64@4.53.3': + resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.34.8': + resolution: {integrity: sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==} cpu: [x64] os: [darwin] - '@rollup/rollup-linux-arm-gnueabihf@4.22.4': - resolution: {integrity: sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==} + '@rollup/rollup-darwin-x64@4.53.3': + resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.34.8': + resolution: {integrity: sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-arm64@4.53.3': + resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.34.8': + resolution: {integrity: sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.53.3': + resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.34.8': + resolution: {integrity: sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.22.4': - resolution: {integrity: sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==} + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.22.4': - resolution: {integrity: sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==} + '@rollup/rollup-linux-arm-musleabihf@4.34.8': + resolution: {integrity: sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.34.8': + resolution: {integrity: sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.22.4': - resolution: {integrity: sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==} + '@rollup/rollup-linux-arm64-gnu@4.53.3': + resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.22.4': - resolution: {integrity: sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==} + '@rollup/rollup-linux-arm64-musl@4.34.8': + resolution: {integrity: sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.53.3': + resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.53.3': + resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loongarch64-gnu@4.34.8': + resolution: {integrity: sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-powerpc64le-gnu@4.34.8': + resolution: {integrity: sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.22.4': - resolution: {integrity: sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==} + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.34.8': + resolution: {integrity: sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.22.4': - resolution: {integrity: sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==} + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.53.3': + resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.34.8': + resolution: {integrity: sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.22.4': - resolution: {integrity: sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==} + '@rollup/rollup-linux-s390x-gnu@4.53.3': + resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.34.8': + resolution: {integrity: sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.22.4': - resolution: {integrity: sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==} + '@rollup/rollup-linux-x64-gnu@4.53.3': + resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.22.4': - resolution: {integrity: sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==} + '@rollup/rollup-linux-x64-musl@4.34.8': + resolution: {integrity: sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.53.3': + resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.53.3': + resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.34.8': + resolution: {integrity: sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.22.4': - resolution: {integrity: sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==} + '@rollup/rollup-win32-arm64-msvc@4.53.3': + resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.34.8': + resolution: {integrity: sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.22.4': - resolution: {integrity: sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==} + '@rollup/rollup-win32-ia32-msvc@4.53.3': + resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.53.3': + resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} cpu: [x64] os: [win32] - '@schematics/angular@18.2.21': - resolution: {integrity: sha512-5Ai+NEflQZi67y4NsQ3o04iEp7zT0/BUFVCrJ3CueU3uYQGs8jrN1Lk6tvQ9c5HzGcTDrMXuTrCswyR9o6ecpA==} + '@rollup/rollup-win32-x64-msvc@4.34.8': + resolution: {integrity: sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.53.3': + resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} + cpu: [x64] + os: [win32] + + '@schematics/angular@19.2.19': + resolution: {integrity: sha512-6/0pvbPCY4UHeB4lnM/5r250QX5gcLgOYbR5FdhFu+22mOPHfWpRc5tNuY9kCephDHzAHjo6fTW1vefOOmA4jw==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - '@sigstore/bundle@2.3.2': - resolution: {integrity: sha512-wueKWDk70QixNLB363yHc2D2ItTgYiMTdPwK8D9dKQMR3ZQ0c35IxP5xnwQ8cNLoCgCRcHf14kE+CLIvNX1zmA==} - engines: {node: ^16.14.0 || >=18.0.0} - - '@sigstore/core@1.1.0': - resolution: {integrity: sha512-JzBqdVIyqm2FRQCulY6nbQzMpJJpSiJ8XXWMhtOX9eKgaXXpfNOF53lzQEjIydlStnd/eFtuC1dW4VYdD93oRg==} - engines: {node: ^16.14.0 || >=18.0.0} - - '@sigstore/protobuf-specs@0.3.3': - resolution: {integrity: sha512-RpacQhBlwpBWd7KEJsRKcBQalbV28fvkxwTOJIqhIuDysMMaJW47V4OqW30iJB9uRpqOSxxEAQFdr8tTattReQ==} + '@sigstore/bundle@3.1.0': + resolution: {integrity: sha512-Mm1E3/CmDDCz3nDhFKTuYdB47EdRFRQMOE/EAbiG1MJW77/w1b3P7Qx7JSrVJs8PfwOLOVcKQCHErIwCTyPbag==} engines: {node: ^18.17.0 || >=20.5.0} - '@sigstore/sign@2.3.2': - resolution: {integrity: sha512-5Vz5dPVuunIIvC5vBb0APwo7qKA4G9yM48kPWJT+OEERs40md5GoUR1yedwpekWZ4m0Hhw44m6zU+ObsON+iDA==} - engines: {node: ^16.14.0 || >=18.0.0} + '@sigstore/core@2.0.0': + resolution: {integrity: sha512-nYxaSb/MtlSI+JWcwTHQxyNmWeWrUXJJ/G4liLrGG7+tS4vAz6LF3xRXqLH6wPIVUoZQel2Fs4ddLx4NCpiIYg==} + engines: {node: ^18.17.0 || >=20.5.0} - '@sigstore/tuf@2.3.4': - resolution: {integrity: sha512-44vtsveTPUpqhm9NCrbU8CWLe3Vck2HO1PNLw7RIajbB7xhtn5RBPm1VNSCMwqGYHhDsBJG8gDF0q4lgydsJvw==} - engines: {node: ^16.14.0 || >=18.0.0} + '@sigstore/protobuf-specs@0.4.3': + resolution: {integrity: sha512-fk2zjD9117RL9BjqEwF7fwv7Q/P9yGsMV4MUJZ/DocaQJ6+3pKr+syBq1owU5Q5qGw5CUbXzm+4yJ2JVRDQeSA==} + engines: {node: ^18.17.0 || >=20.5.0} - '@sigstore/verify@1.2.1': - resolution: {integrity: sha512-8iKx79/F73DKbGfRf7+t4dqrc0bRr0thdPrxAtCKWRm/F0tG71i6O1rvlnScncJLLBZHn3h8M3c1BSUAb9yu8g==} - engines: {node: ^16.14.0 || >=18.0.0} + '@sigstore/sign@3.1.0': + resolution: {integrity: sha512-knzjmaOHOov1Ur7N/z4B1oPqZ0QX5geUfhrVaqVlu+hl0EAoL4o+l0MSULINcD5GCWe3Z0+YJO8ues6vFlW0Yw==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@sigstore/tuf@3.1.1': + resolution: {integrity: sha512-eFFvlcBIoGwVkkwmTi/vEQFSva3xs5Ot3WmBcjgjVdiaoelBLQaQ/ZBfhlG0MnG0cmTYScPpk7eDdGDWUcFUmg==} + engines: {node: ^18.17.0 || >=20.5.0} + + '@sigstore/verify@2.1.1': + resolution: {integrity: sha512-hVJD77oT67aowHxwT4+M6PGOp+E2LtLdTK3+FC0lBO9T7sYwItDMXZ7Z07IDCvR1M717a4axbIWckrW67KMP/w==} + engines: {node: ^18.17.0 || >=20.5.0} '@sindresorhus/merge-streams@2.3.0': resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} engines: {node: '>=18'} + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + '@tufjs/canonical-json@2.0.0': resolution: {integrity: sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==} engines: {node: ^16.14.0 || >=18.0.0} - '@tufjs/models@2.0.1': - resolution: {integrity: sha512-92F7/SFyufn4DXsha9+QfKnN03JGqtMFMXgSHbZOo8JG59WkTni7UzAouNQDf7AuP9OAMxVOPQcqG3sB7w+kkg==} - engines: {node: ^16.14.0 || >=18.0.0} + '@tufjs/models@3.0.1': + resolution: {integrity: sha512-UUYHISyhCU3ZgN8yaear3cGATHb3SMuKHsQ/nVbHXcmnBf+LzQ/cQfhNG+rfaSHgqGKNEm2cOCLVLELStUQ1JA==} + engines: {node: ^18.17.0 || >=20.5.0} '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} @@ -1457,8 +1739,17 @@ packages: '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} - '@types/estree@1.0.5': - resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + '@types/cors@2.8.19': + resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} + + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -1475,23 +1766,20 @@ packages: '@types/http-proxy@1.17.17': resolution: {integrity: sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==} + '@types/jasmine@5.1.13': + resolution: {integrity: sha512-MYCcDkruFc92LeYZux5BC0dmqo2jk+M5UIZ4/oFnAPCXN9mCcQhLyj7F3/Za7rocVyt5YRr1MmqJqFlvQ9LVcg==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - '@types/mute-stream@0.0.4': - resolution: {integrity: sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==} - '@types/node-forge@1.3.14': resolution: {integrity: sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==} - '@types/node@20.19.25': - resolution: {integrity: sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==} - - '@types/node@22.19.1': - resolution: {integrity: sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==} + '@types/node@24.10.1': + resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} '@types/qs@6.14.0': resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} @@ -1517,17 +1805,14 @@ packages: '@types/sockjs@0.3.36': resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==} - '@types/wrap-ansi@3.0.0': - resolution: {integrity: sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==} - '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@vitejs/plugin-basic-ssl@1.1.0': - resolution: {integrity: sha512-wO4Dk/rm8u7RNhOf95ZzcEmC9rYOncYgvq4z3duaJrCgjN8BxAnDVyndanfcJZ0O6XZzHz6Q0hTimxTg8Y9g/A==} - engines: {node: '>=14.6.0'} + '@vitejs/plugin-basic-ssl@1.2.0': + resolution: {integrity: sha512-mkQnxTkcldAzIsomk1UuLfAu9n+kpQ3JbHcpCp7d2Oo6ITtji8pHS3QToOWjhPFvNQSnhlkAjmGbhv2QvwO/7Q==} + engines: {node: '>=14.21.3'} peerDependencies: - vite: ^3.0.0 || ^4.0.0 || ^5.0.0 + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -1583,19 +1868,14 @@ packages: '@yarnpkg/lockfile@1.1.0': resolution: {integrity: sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==} - abbrev@2.0.0: - resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + abbrev@3.0.1: + resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} + engines: {node: ^18.17.0 || >=20.5.0} accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} - acorn-import-attributes@1.9.5: - resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} - peerDependencies: - acorn: ^8 - acorn@8.15.0: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} @@ -1609,10 +1889,6 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} - aggregate-error@3.1.0: - resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} - engines: {node: '>=8'} - ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -1629,19 +1905,11 @@ packages: ajv: optional: true - ajv-keywords@3.5.2: - resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} - peerDependencies: - ajv: ^6.9.1 - ajv-keywords@5.1.0: resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} peerDependencies: ajv: ^8.8.2 - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} @@ -1649,10 +1917,6 @@ packages: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} - ansi-escapes@4.3.2: - resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} - engines: {node: '>=8'} - ansi-escapes@7.2.0: resolution: {integrity: sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==} engines: {node: '>=18'} @@ -1708,8 +1972,8 @@ packages: peerDependencies: postcss: ^8.1.0 - babel-loader@9.1.3: - resolution: {integrity: sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==} + babel-loader@9.2.1: + resolution: {integrity: sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==} engines: {node: '>= 14.15.0'} peerDependencies: '@babel/core': ^7.12.0 @@ -1736,13 +2000,21 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.8.29: - resolution: {integrity: sha512-sXdt2elaVnhpDNRDz+1BDx1JQoJRuNk7oVlAlbGiFkLikHCAQiccexF/9e91zVi6RCgqspl04aP+6Cnl9zRLrA==} + base64id@2.0.0: + resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} + engines: {node: ^4.5.0 || >= 5.9} + + baseline-browser-mapping@2.8.31: + resolution: {integrity: sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==} hasBin: true batch@0.6.1: resolution: {integrity: sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=} + beasties@0.3.2: + resolution: {integrity: sha512-p4AF8uYzm9Fwu8m/hSVTCPXrRBPmB34hQpHsec2KOaR9CZmgoU8IOv4Cvwq4hgz2p4hLMNbsdNl5XeA6XbAQwA==} + engines: {node: '>=14.0.0'} + big.js@5.2.2: resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} @@ -1763,6 +2035,9 @@ packages: boolbase@1.0.0: resolution: {integrity: sha1-aN/1++YMUes3cl6p4+0xDcwed24=} + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} @@ -1789,9 +2064,9 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} - cacache@18.0.4: - resolution: {integrity: sha512-B+L5iIa9mgcjLbliir2th36yEwPftrzteHYujzsx3dFP/31GCHcIeS8f5MGd80odLOjaOvSpU3EEAmRQptkxLQ==} - engines: {node: ^16.14.0 || >=18.0.0} + cacache@19.0.1: + resolution: {integrity: sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==} + engines: {node: ^18.17.0 || >=20.5.0} call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} @@ -1809,19 +2084,15 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - caniuse-lite@1.0.30001755: - resolution: {integrity: sha512-44V+Jm6ctPj7R52Na4TLi3Zri4dWUljJd+RDm+j8LtNCc/ihLCT+X1TzoOAkRETEWqjuLnh9581Tl80FvK7jVA==} + caniuse-lite@1.0.30001757: + resolution: {integrity: sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chardet@0.7.0: - resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} - - chart.js@4.5.1: - resolution: {integrity: sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==} - engines: {pnpm: '>=8'} + chardet@2.1.1: + resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} @@ -1835,14 +2106,14 @@ packages: resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} engines: {node: '>=10'} + chownr@3.0.0: + resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==} + engines: {node: '>=18'} + chrome-trace-event@1.0.4: resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} - clean-stack@2.2.0: - resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} - engines: {node: '>=6'} - cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -1863,6 +2134,9 @@ packages: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -1903,10 +2177,17 @@ packages: resolution: {integrity: sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==} engines: {node: '>= 0.8.0'} + concat-map@0.0.1: + resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} + connect-history-api-fallback@2.0.0: resolution: {integrity: sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==} engines: {node: '>=0.8'} + connect@3.7.0: + resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==} + engines: {node: '>= 0.10.0'} + content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -1928,6 +2209,10 @@ packages: resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} engines: {node: '>= 0.6'} + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + copy-anything@2.0.6: resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} @@ -1937,12 +2222,16 @@ packages: peerDependencies: webpack: ^5.1.0 - core-js-compat@3.46.0: - resolution: {integrity: sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law==} + core-js-compat@3.47.0: + resolution: {integrity: sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==} core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + cosmiconfig@9.0.0: resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} engines: {node: '>=14'} @@ -1952,10 +2241,6 @@ packages: typescript: optional: true - critters@0.0.24: - resolution: {integrity: sha512-Oyqew0FGM0wYUSNqR0L6AteO5MpMoUU0rhKRieXeiKs+PmRTxiJMyaunYB2KF6fQ3dzChXKCpbFOEJx3OQ1v/Q==} - deprecated: Ownership of Critters has moved to the Nuxt team, who will be maintaining the project going forward. If you'd like to keep using Critters, please switch to the actively-maintained fork at https://github.com/danielroe/beasties - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1984,6 +2269,13 @@ packages: engines: {node: '>=4'} hasBin: true + custom-event@1.0.1: + resolution: {integrity: sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==} + + date-format@4.0.14: + resolution: {integrity: sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==} + engines: {node: '>=4.0'} + debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -1992,6 +2284,15 @@ packages: supports-color: optional: true + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} @@ -2028,6 +2329,11 @@ packages: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + detect-libc@1.0.3: + resolution: {integrity: sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=} + engines: {node: '>=0.10'} + hasBin: true + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -2035,6 +2341,9 @@ packages: detect-node@2.1.0: resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} + di@0.0.1: + resolution: {integrity: sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==} + didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} @@ -2045,6 +2354,9 @@ packages: resolution: {integrity: sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==} engines: {node: '>=6'} + dom-serialize@2.2.1: + resolution: {integrity: sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==} + dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} @@ -2068,8 +2380,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=} - electron-to-chromium@1.5.254: - resolution: {integrity: sha512-DcUsWpVhv9svsKRxnSCZ86SjD+sp32SGidNB37KpqXJncp1mfUgKbHvBomE89WJDbfVKw1mdv5+ikrvd43r+Bg==} + electron-to-chromium@1.5.259: + resolution: {integrity: sha512-I+oLXgpEJzD6Cwuwt1gYjxsDmu/S/Kd41mmLA3O+/uH2pFRO/DvOjUyGozL8j3KeLV6WyZ7ssPwELMsXCcsJAQ==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -2095,10 +2407,22 @@ packages: encoding@0.1.13: resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + + engine.io@6.6.4: + resolution: {integrity: sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==} + engines: {node: '>=10.2.0'} + enhanced-resolve@5.18.3: resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} + ent@2.2.2: + resolution: {integrity: sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw==} + engines: {node: '>= 0.4'} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -2140,18 +2464,13 @@ packages: resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} engines: {node: '>= 0.4'} - esbuild-wasm@0.23.0: - resolution: {integrity: sha512-6jP8UmWy6R6TUUV8bMuC3ZyZ6lZKI56x0tkxyCIqWwRRJ/DgeQKneh/Oid5EoGoPFLrGNkz47ZEtWAYuiY/u9g==} + esbuild-wasm@0.25.4: + resolution: {integrity: sha512-2HlCS6rNvKWaSKhWaG/YIyRsTsL3gUrMP2ToZMBIjw9LM7vVcIs+rz8kE2vExvTJgvM8OKPqNpcHawY/BQc/qQ==} engines: {node: '>=18'} hasBin: true - esbuild@0.21.5: - resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} - engines: {node: '>=12'} - hasBin: true - - esbuild@0.23.0: - resolution: {integrity: sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==} + esbuild@0.25.4: + resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==} engines: {node: '>=18'} hasBin: true @@ -2203,24 +2522,16 @@ packages: resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} engines: {node: '>= 0.10.0'} - external-editor@3.1.0: - resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} - engines: {node: '>=4'} + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - fast-glob@3.3.2: - resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} - engines: {node: '>=8.6.0'} - fast-glob@3.3.3: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - fast-uri@3.1.0: resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} @@ -2231,10 +2542,23 @@ packages: resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} engines: {node: '>=0.8.0'} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + finalhandler@1.1.2: + resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} + engines: {node: '>= 0.8'} + finalhandler@1.3.1: resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} engines: {node: '>= 0.8'} @@ -2251,6 +2575,9 @@ packages: resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} hasBin: true + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + follow-redirects@1.15.11: resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} engines: {node: '>=4.0'} @@ -2278,6 +2605,10 @@ packages: resolution: {integrity: sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=} engines: {node: '>= 0.6'} + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + fs-minipass@2.1.0: resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} engines: {node: '>= 8'} @@ -2286,6 +2617,9 @@ packages: resolution: {integrity: sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + fs.realpath@1.0.0: + resolution: {integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8=} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -2331,10 +2665,13 @@ packages: glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} hasBin: true + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + globby@14.1.0: resolution: {integrity: sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==} engines: {node: '>=18'} @@ -2357,19 +2694,26 @@ packages: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + hasown@2.0.2: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hosted-git-info@7.0.2: - resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} - engines: {node: ^16.14.0 || >=18.0.0} + hosted-git-info@8.1.0: + resolution: {integrity: sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==} + engines: {node: ^18.17.0 || >=20.5.0} hpack.js@2.1.6: resolution: {integrity: sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==} - htmlparser2@8.0.2: - resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + htmlparser2@10.0.0: + resolution: {integrity: sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==} http-cache-semantics@4.2.0: resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} @@ -2409,10 +2753,6 @@ packages: resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} engines: {node: '>=8.0.0'} - https-proxy-agent@7.0.5: - resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==} - engines: {node: '>= 14'} - https-proxy-agent@7.0.6: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} @@ -2429,6 +2769,10 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} + iconv-lite@0.7.0: + resolution: {integrity: sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==} + engines: {node: '>=0.10.0'} + icss-utils@5.1.0: resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} engines: {node: ^10 || ^12 || >= 14} @@ -2438,9 +2782,9 @@ packages: ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore-walk@6.0.5: - resolution: {integrity: sha512-VuuG0wCnjhnylG1ABXT3dAuIpTNDs/G8jlpmwXY03fXoXy/8ZK8/T+hMzt8L4WnrLCJgdybqgPagnF/f97cg3A==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ignore-walk@7.0.0: + resolution: {integrity: sha512-T4gbf83A4NH95zvhVYZc+qWocBBGlpzUXLPGurJggw/WIOwicfXJChLDP/iBZnN5WqROSu5Bm3hhle4z8a8YGQ==} + engines: {node: ^18.17.0 || >=20.5.0} ignore@7.0.5: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} @@ -2451,8 +2795,8 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - immutable@4.3.7: - resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} + immutable@5.1.4: + resolution: {integrity: sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -2462,9 +2806,8 @@ packages: resolution: {integrity: sha1-khi5srkoojixPcT7a21XbyMUU+o=} engines: {node: '>=0.8.19'} - indent-string@4.0.0: - resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} - engines: {node: '>=8'} + inflight@1.0.6: + resolution: {integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=} inherits@2.0.3: resolution: {integrity: sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=} @@ -2472,9 +2815,9 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - ini@4.1.3: - resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ini@5.0.0: + resolution: {integrity: sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==} + engines: {node: ^18.17.0 || >=20.5.0} ip-address@10.1.0: resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} @@ -2533,9 +2876,6 @@ packages: resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} engines: {node: '>=8'} - is-lambda@1.0.1: - resolution: {integrity: sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU=} - is-network-error@1.3.0: resolution: {integrity: sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw==} engines: {node: '>=16'} @@ -2556,6 +2896,10 @@ packages: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + is-unicode-supported@0.1.0: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} @@ -2570,6 +2914,10 @@ packages: isarray@1.0.0: resolution: {integrity: sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=} + isbinaryfile@4.0.10: + resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==} + engines: {node: '>= 8.0.0'} + isexe@2.0.0: resolution: {integrity: sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=} @@ -2585,13 +2933,35 @@ packages: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} + istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + istanbul-lib-instrument@6.0.3: resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} engines: {node: '>=10'} + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jasmine-core@4.6.1: + resolution: {integrity: sha512-VYz/BjjmC3klLJlLwA4Kw8ytk0zDSmbbDLNs794VnWmkcCB7I9aAL/D48VNQtmITyPvea2C3jdUMfc3kAoy0PQ==} + + jasmine-core@5.6.0: + resolution: {integrity: sha512-niVlkeYVRwKFpmfWg6suo6H9CrNnydfBLEqefM5UjibYS+UoTjZdmvPJSiuyrRLGnFj1eYRhFd/ch+5hSlsFVA==} + jest-worker@27.5.1: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} @@ -2615,12 +2985,9 @@ packages: json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-parse-even-better-errors@3.0.2: - resolution: {integrity: sha512-fi0NG4bPjCHunUJffmLd0gxssIgkNmArMvis4iNah6Owg1MCJjWhEcDLmsK6iGkJq3tHwbDkTlce70/tmXN4cQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-parse-even-better-errors@4.0.0: + resolution: {integrity: sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==} + engines: {node: ^18.17.0 || >=20.5.0} json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} @@ -2633,13 +3000,41 @@ packages: jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} + jsonfile@4.0.0: + resolution: {integrity: sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=} + jsonparse@1.3.1: resolution: {integrity: sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=} engines: {'0': node >= 0.2.0} + karma-chrome-launcher@3.2.0: + resolution: {integrity: sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==} + + karma-coverage@2.2.1: + resolution: {integrity: sha512-yj7hbequkQP2qOSb20GuNSIyE//PgJWHwC2IydLE6XRtsnaflv+/OSGNssPjobYUlhVVagy99TQpqUt3vAUG7A==} + engines: {node: '>=10.0.0'} + + karma-jasmine-html-reporter@2.1.0: + resolution: {integrity: sha512-sPQE1+nlsn6Hwb5t+HHwyy0A1FNCVKuL1192b+XNauMYWThz2kweiBVW1DqloRpVvZIJkIoHVB7XRpK78n1xbQ==} + peerDependencies: + jasmine-core: ^4.0.0 || ^5.0.0 + karma: ^6.0.0 + karma-jasmine: ^5.0.0 + + karma-jasmine@5.1.0: + resolution: {integrity: sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==} + engines: {node: '>=12'} + peerDependencies: + karma: ^6.0.0 + karma-source-map-support@1.4.0: resolution: {integrity: sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A==} + karma@6.4.4: + resolution: {integrity: sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==} + engines: {node: '>= 10'} + hasBin: true + kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -2660,8 +3055,8 @@ packages: webpack: optional: true - less@4.2.0: - resolution: {integrity: sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==} + less@4.2.2: + resolution: {integrity: sha512-tkuLHQlvWUTeQ3doAqnHbNn8T6WX1KA8yvbKG9x4VtKtIjHsVKQZCH11zRgAfbDAXC2UNIg/K9BYAAcEzUIrNg==} engines: {node: '>=6'} hasBin: true @@ -2680,12 +3075,12 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - listr2@8.2.4: - resolution: {integrity: sha512-opevsywziHd3zHCVQGAj8zu+Z3yHNkkoYhWIGnq54RrCVwLz0MozotJEDnKsIBLvkfLGN6BLOyAeRrYI0pKA4g==} + listr2@8.2.5: + resolution: {integrity: sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==} engines: {node: '>=18.0.0'} - lmdb@3.0.13: - resolution: {integrity: sha512-UGe+BbaSUQtAMZobTb4nHvFMrmvuAQKSeaqAX2meTEQjfsbpl5sxdHD8T72OnwD4GU9uwNhYXIVe4QGs8N9Zyw==} + lmdb@3.2.6: + resolution: {integrity: sha512-SuHqzPl7mYStna8WRotY8XX/EUZBjjv3QyKIByeCLFfC9uXT/OIHByEcA07PzbMfQAM0KYJtLgtpMRlIe5dErQ==} hasBin: true loader-runner@4.3.1: @@ -2704,12 +3099,12 @@ packages: resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - lodash-es@4.17.21: - resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==} - lodash.debounce@4.0.8: resolution: {integrity: sha1-gteb/zCmfEAF/9XiUVMArZyk168=} + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + log-symbols@4.1.0: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} @@ -2718,22 +3113,30 @@ packages: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} + log4js@6.9.1: + resolution: {integrity: sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==} + engines: {node: '>=8.0'} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - magic-string@0.30.11: - resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} make-dir@2.1.0: resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} engines: {node: '>=6'} - make-fetch-happen@13.0.1: - resolution: {integrity: sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==} - engines: {node: ^16.14.0 || >=18.0.0} + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + make-fetch-happen@14.0.3: + resolution: {integrity: sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==} + engines: {node: ^18.17.0 || >=20.5.0} math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} @@ -2781,6 +3184,11 @@ packages: engines: {node: '>=4'} hasBin: true + mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -2789,8 +3197,8 @@ packages: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} - mini-css-extract-plugin@2.9.0: - resolution: {integrity: sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==} + mini-css-extract-plugin@2.9.2: + resolution: {integrity: sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==} engines: {node: '>= 12.13.0'} peerDependencies: webpack: ^5.0.0 @@ -2798,17 +3206,23 @@ packages: minimalistic-assert@1.0.1: resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass-collect@2.0.1: resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} engines: {node: '>=16 || 14 >=14.17'} - minipass-fetch@3.0.5: - resolution: {integrity: sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + minipass-fetch@4.0.1: + resolution: {integrity: sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==} + engines: {node: ^18.17.0 || >=20.5.0} minipass-flush@1.0.5: resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} @@ -2838,13 +3252,21 @@ packages: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} + minizlib@3.1.0: + resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} + engines: {node: '>= 18'} + + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} hasBin: true - mrmime@2.0.0: - resolution: {integrity: sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==} + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} ms@2.0.0: @@ -2868,6 +3290,10 @@ packages: resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + mute-stream@2.0.0: + resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} + engines: {node: ^18.17.0 || >=20.5.0} + mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} @@ -2889,29 +3315,19 @@ packages: resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} engines: {node: '>= 0.6'} + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} - ng2-charts@6.0.1: - resolution: {integrity: sha512-pO7evbvHqjiKB7zqE12tCKWQI9gmQ8DVOEaWBBLlxJabc4fLGk7o9t4jC4+Q9pJiQrTtQkugm0dIPQ4PFHUaWA==} - peerDependencies: - '@angular/cdk': '>=17.0.0' - '@angular/common': '>=17.0.0' - '@angular/core': '>=17.0.0' - '@angular/platform-browser': '>=17.0.0' - chart.js: ^3.4.0 || ^4.0.0 - rxjs: ^6.5.3 || ^7.4.0 - - nice-napi@1.0.2: - resolution: {integrity: sha512-px/KnJAJZf5RuBGcfD+Sp2pAKq0ytz8j+1NehvgIGFkvtvFrDM3T8E4x/JJODXK9WZow8RRGrbA9QQ3hs+pDhA==} - os: ['!win32'] - - node-addon-api@3.2.1: - resolution: {integrity: sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==} - node-addon-api@6.1.0: resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + node-forge@1.3.1: resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} engines: {node: '>= 6.13.0'} @@ -2920,27 +3336,19 @@ packages: resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} hasBin: true - node-gyp-build@4.8.4: - resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} - hasBin: true - - node-gyp@10.3.1: - resolution: {integrity: sha512-Pp3nFHBThHzVtNY7U6JfPjvT/DTE8+o/4xKsLQtBoU+j2HLsGlhcfzflAoUreaJbNmYnX+LlLi0qjV8kpyO6xQ==} - engines: {node: ^16.14.0 || >=18.0.0} + node-gyp@11.5.0: + resolution: {integrity: sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ==} + engines: {node: ^18.17.0 || >=20.5.0} hasBin: true node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} - nopt@7.2.1: - resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + nopt@8.1.0: + resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==} + engines: {node: ^18.17.0 || >=20.5.0} hasBin: true - normalize-package-data@6.0.2: - resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} - engines: {node: ^16.14.0 || >=18.0.0} - normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} @@ -2949,33 +3357,33 @@ packages: resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} engines: {node: '>=0.10.0'} - npm-bundled@3.0.1: - resolution: {integrity: sha512-+AvaheE/ww1JEwRHOrn4WHNzOxGtVp+adrg2AeZS/7KuxGUYFuBta98wYpfHBbJp6Tg6j1NKSEVHNcfZzJHQwQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-bundled@4.0.0: + resolution: {integrity: sha512-IxaQZDMsqfQ2Lz37VvyyEtKLe8FsRZuysmedy/N06TU1RyVppYKXrO4xIhR0F+7ubIBox6Q7nir6fQI3ej39iA==} + engines: {node: ^18.17.0 || >=20.5.0} - npm-install-checks@6.3.0: - resolution: {integrity: sha512-W29RiK/xtpCGqn6f3ixfRYGk+zRyr+Ew9F2E20BfXxT5/euLdA/Nm7fO7OeTGuAmTs30cpgInyJ0cYe708YTZw==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-install-checks@7.1.2: + resolution: {integrity: sha512-z9HJBCYw9Zr8BqXcllKIs5nI+QggAImbBdHphOzVYrz2CB4iQ6FzWyKmlqDZua+51nAu7FcemlbTc9VgQN5XDQ==} + engines: {node: ^18.17.0 || >=20.5.0} - npm-normalize-package-bin@3.0.1: - resolution: {integrity: sha512-dMxCf+zZ+3zeQZXKxmyuCKlIDPGuv8EF940xbkC4kQVDTtqoh6rJFO+JTKSA6/Rwi0getWmtuy4Itup0AMcaDQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-normalize-package-bin@4.0.0: + resolution: {integrity: sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==} + engines: {node: ^18.17.0 || >=20.5.0} - npm-package-arg@11.0.3: - resolution: {integrity: sha512-sHGJy8sOC1YraBywpzQlIKBE4pBbGbiF95U6Auspzyem956E0+FtDtsx1ZxlOJkQCZ1AFXAY/yuvtFYrOxF+Bw==} - engines: {node: ^16.14.0 || >=18.0.0} + npm-package-arg@12.0.2: + resolution: {integrity: sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==} + engines: {node: ^18.17.0 || >=20.5.0} - npm-packlist@8.0.2: - resolution: {integrity: sha512-shYrPFIS/JLP4oQmAwDyk5HcyysKW8/JLTEA32S0Z5TzvpaeeX2yMFfoK1fjEBnCBvVyIB/Jj/GBFdm0wsgzbA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + npm-packlist@9.0.0: + resolution: {integrity: sha512-8qSayfmHJQTx3nJWYbbUmflpyarbLMBc6LCAjYsiGtXxDB68HaZpb8re6zeaLGxZzDuMdhsg70jryJe+RrItVQ==} + engines: {node: ^18.17.0 || >=20.5.0} - npm-pick-manifest@9.1.0: - resolution: {integrity: sha512-nkc+3pIIhqHVQr085X9d2JzPzLyjzQS96zbruppqC9aZRm/x8xx6xhI98gHtsfELP2bE+loHq8ZaHFHhe+NauA==} - engines: {node: ^16.14.0 || >=18.0.0} + npm-pick-manifest@10.0.0: + resolution: {integrity: sha512-r4fFa4FqYY8xaM7fHecQ9Z2nE9hgNfJR+EmoKv0+chvzWkBcORX3r0FpTByP+CbOVJDladMXnPQGVN8PBLGuTQ==} + engines: {node: ^18.17.0 || >=20.5.0} - npm-registry-fetch@17.1.0: - resolution: {integrity: sha512-5+bKQRH0J1xG1uZ1zMNvxW0VEyoNWgJpY9UDuluPFLKDfJ9u2JmmjmTJV1srBGQOROfdBMiVvnH2Zvpbm+xkVA==} - engines: {node: ^16.14.0 || >=18.0.0} + npm-registry-fetch@18.0.2: + resolution: {integrity: sha512-LeVMZBBVy+oQb5R6FDV9OlJCcWDU+al10oKpe+nsvcHnG24Z3uM3SvJYKfGJlfGjVU8v9liejCrUR/M5HO5NEQ==} + engines: {node: ^18.17.0 || >=20.5.0} nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} @@ -2995,6 +3403,10 @@ packages: obuf@1.1.2: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + on-finished@2.3.0: + resolution: {integrity: sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=} + engines: {node: '>= 0.8'} + on-finished@2.4.1: resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} engines: {node: '>= 0.8'} @@ -3003,6 +3415,9 @@ packages: resolution: {integrity: sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==} engines: {node: '>= 0.8'} + once@1.4.0: + resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=} + onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} @@ -3022,10 +3437,6 @@ packages: ordered-binary@1.6.0: resolution: {integrity: sha512-IQh2aMfMIDbPjI/8a3Edr+PiOpcsB7yo8NdW7aHWVaoR/pcDldunMvnnwbk/auPGqmKeAdxtZl7MHX/QmPwhvQ==} - os-tmpdir@1.0.2: - resolution: {integrity: sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=} - engines: {node: '>=0.10.0'} - p-limit@4.0.0: resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -3034,9 +3445,9 @@ packages: resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - p-map@4.0.0: - resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} - engines: {node: '>=10'} + p-map@7.0.4: + resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} + engines: {node: '>=18'} p-retry@6.2.1: resolution: {integrity: sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==} @@ -3045,9 +3456,9 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - pacote@18.0.6: - resolution: {integrity: sha512-+eK3G27SMwsB8kLIuj4h1FUhHtwiEUo21Tw8wNjmvdlpOEr613edv+8FUsTj/4F/VN5ywGE19X18N7CC2EJk6A==} - engines: {node: ^16.14.0 || >=18.0.0} + pacote@20.0.0: + resolution: {integrity: sha512-pRjC5UFwZCgx9kUFDVM9YEahv4guZ1nSLqwmWiLUnDbGsjs+U5w7z6Uc8HNR1a6x8qnu5y9xtGE6D1uAuYz+0A==} + engines: {node: ^18.17.0 || >=20.5.0} hasBin: true parent-module@1.0.1: @@ -3071,9 +3482,6 @@ packages: parse5@7.3.0: resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} - parse5@8.0.0: - resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} - parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} @@ -3082,6 +3490,10 @@ packages: resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + path-is-absolute@1.0.1: + resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=} + engines: {node: '>=0.10.0'} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -3111,6 +3523,10 @@ packages: resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + pify@2.3.0: resolution: {integrity: sha1-7RQaasBDqEnqWISY59yosVMw6Qw=} engines: {node: '>=0.10.0'} @@ -3123,8 +3539,8 @@ packages: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} - piscina@4.6.1: - resolution: {integrity: sha512-z30AwWGtQE+Apr+2WBZensP2lIvwoaMcOPkQlIEmSGMJNUvaYACylPYrQM6wSdUNJlnDVMSpLv7xTMJqlVshOA==} + piscina@4.8.0: + resolution: {integrity: sha512-EZJb+ZxDrQf3dihsUL7p42pjNyrNIFJCrRHPMgxu/svsj+P3xS3fuEWp7k2+rfsavfl1N0G29b1HGs7J0m8rZA==} pkg-dir@7.0.0: resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} @@ -3217,29 +3633,21 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.4.41: - resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==} + postcss@8.5.2: + resolution: {integrity: sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==} engines: {node: ^10 || ^12 || >=14} postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} - proc-log@4.2.0: - resolution: {integrity: sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + proc-log@5.0.0: + resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} + engines: {node: ^18.17.0 || >=20.5.0} process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - promise-inflight@1.0.1: - resolution: {integrity: sha1-mEcocL8igTL8vdhoEputEsPAKeM=} - peerDependencies: - bluebird: '*' - peerDependenciesMeta: - bluebird: - optional: true - promise-retry@2.0.1: resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} engines: {node: '>=10'} @@ -3251,9 +3659,12 @@ packages: prr@1.0.1: resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} + punycode@1.4.1: + resolution: {integrity: sha1-wNWmOycYgArY4esPpSachN1BhF4=} + + qjobs@1.2.0: + resolution: {integrity: sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==} + engines: {node: '>=0.9'} qs@6.13.0: resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} @@ -3337,13 +3748,14 @@ packages: resolution: {integrity: sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==} engines: {node: '>=12'} - resolve@1.22.11: - resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} engines: {node: '>= 0.4'} hasBin: true - resolve@1.22.8: - resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} hasBin: true restore-cursor@3.1.0: @@ -3369,8 +3781,17 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rollup@4.22.4: - resolution: {integrity: sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==} + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + + rollup@4.34.8: + resolution: {integrity: sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rollup@4.53.3: + resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -3393,11 +3814,15 @@ packages: safe-buffer@5.2.1: resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sass-loader@16.0.0: - resolution: {integrity: sha512-n13Z+3rU9A177dk4888czcVFiC8CL9dii4qpXWUg3YIIgZEvi9TCFKjOQcbK0kJM7DJu9VucrZFddvNfYCPwtw==} + sass-loader@16.0.5: + resolution: {integrity: sha512-oL+CMBXrj6BZ/zOq4os+UECPL+bWqt6OAC6DWS8Ln8GZRcMDjlJ4JC3FBDuHJdYaFWIdKNIBYmtZtK2MaMkNIw==} engines: {node: '>= 18.12.0'} peerDependencies: '@rspack/core': 0.x || 1.x @@ -3417,18 +3842,14 @@ packages: webpack: optional: true - sass@1.77.6: - resolution: {integrity: sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==} + sass@1.85.0: + resolution: {integrity: sha512-3ToiC1xZ1Y8aU7+CkgCI/tqyuPXEmYGJXO7H4uqp0xkLXUqp88rQQ4j1HmP37xSJLbCJPaIiv+cT1y+grssrww==} engines: {node: '>=14.0.0'} hasBin: true sax@1.4.3: resolution: {integrity: sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==} - schema-utils@3.3.0: - resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} - engines: {node: '>= 10.13.0'} - schema-utils@4.3.3: resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} engines: {node: '>= 10.13.0'} @@ -3448,8 +3869,8 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.6.3: - resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + semver@7.7.1: + resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} engines: {node: '>=10'} hasBin: true @@ -3518,9 +3939,9 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} - sigstore@2.3.1: - resolution: {integrity: sha512-8G+/XDU8wNsJOQS5ysDVO0Etg9/2uA5gR9l4ZwijjlwxBcrU6RPfwi2+jJmbP+Ap1Hlp/nVAaEO4Fj22/SL2gQ==} - engines: {node: ^16.14.0 || >=18.0.0} + sigstore@3.1.0: + resolution: {integrity: sha512-ZpzWAFHIFqyFE56dXqgX/DkDRZdz+rRcjoIk/RQU4IX0wiCv1l8S7ZrXDHcCc+uaf+6o7w3h2l3g6GYG5TKN9Q==} + engines: {node: ^18.17.0 || >=20.5.0} slash@5.1.0: resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} @@ -3538,6 +3959,17 @@ packages: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + socket.io-adapter@2.5.5: + resolution: {integrity: sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==} + + socket.io-parser@4.2.4: + resolution: {integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==} + engines: {node: '>=10.0.0'} + + socket.io@4.8.1: + resolution: {integrity: sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==} + engines: {node: '>=10.2.0'} + sockjs@0.3.24: resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} @@ -3589,9 +4021,9 @@ packages: resolution: {integrity: sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==} engines: {node: '>=6.0.0'} - ssri@10.0.6: - resolution: {integrity: sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + ssri@12.0.0: + resolution: {integrity: sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==} + engines: {node: ^18.17.0 || >=20.5.0} statuses@1.5.0: resolution: {integrity: sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=} @@ -3601,6 +4033,10 @@ packages: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} + streamroller@3.1.5: + resolution: {integrity: sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==} + engines: {node: '>=8.0'} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -3627,8 +4063,8 @@ packages: resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} engines: {node: '>=12'} - sucrase@3.35.0: - resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true @@ -3661,6 +4097,10 @@ packages: resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} engines: {node: '>=10'} + tar@7.5.2: + resolution: {integrity: sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==} + engines: {node: '>=18'} + terser-webpack-plugin@5.3.14: resolution: {integrity: sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==} engines: {node: '>= 10.13.0'} @@ -3677,8 +4117,8 @@ packages: uglify-js: optional: true - terser@5.31.6: - resolution: {integrity: sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==} + terser@5.39.0: + resolution: {integrity: sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==} engines: {node: '>=10'} hasBin: true @@ -3698,9 +4138,13 @@ packages: thunky@1.1.0: resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} - tmp@0.0.33: - resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} - engines: {node: '>=0.6.0'} + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tmp@0.2.5: + resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} + engines: {node: '>=14.14'} to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} @@ -3723,19 +4167,12 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - tslib@2.6.3: - resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} - tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tuf-js@2.2.1: - resolution: {integrity: sha512-GwIJau9XaA8nLVbUXsN3IlFi7WmQ48gBUrl3FTkkL/XLu/POhBzfmX9hd33FNMX1qAsfl6ozO1iMmW9NC8YniA==} - engines: {node: ^16.14.0 || >=18.0.0} - - type-fest@0.21.3: - resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} - engines: {node: '>=10'} + tuf-js@3.1.0: + resolution: {integrity: sha512-3T3T04WzowbwV2FDiGXBbr81t64g1MUGGJRgT4x5o97N+8ArdhVCAF9IxFrxuSJmM3E5Asn7nKHkao0ibcZXAg==} + engines: {node: ^18.17.0 || >=20.5.0} type-is@1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} @@ -3744,13 +4181,17 @@ packages: typed-assert@1.0.9: resolution: {integrity: sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==} - typescript@5.4.5: - resolution: {integrity: sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==} + typescript@5.7.3: + resolution: {integrity: sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==} engines: {node: '>=14.17'} hasBin: true - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + ua-parser-js@0.7.41: + resolution: {integrity: sha512-O3oYyCMPYgNNHuO7Jjk3uacJWZF8loBgwrfd/5LE/HyZ3lUIOdniQ7DNXJcIgZbwioZxk0fLfI4EVnetdiX5jg==} + hasBin: true + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} unicode-canonical-property-names-ecmascript@2.0.1: resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} @@ -3772,13 +4213,17 @@ packages: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} - unique-filename@3.0.0: - resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + unique-filename@4.0.0: + resolution: {integrity: sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==} + engines: {node: ^18.17.0 || >=20.5.0} - unique-slug@4.0.0: - resolution: {integrity: sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + unique-slug@5.0.0: + resolution: {integrity: sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==} + engines: {node: ^18.17.0 || >=20.5.0} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} unpipe@1.0.0: resolution: {integrity: sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=} @@ -3790,9 +4235,6 @@ packages: peerDependencies: browserslist: '>= 4.21.0' - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - util-deprecate@1.0.2: resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=} @@ -3807,30 +4249,35 @@ packages: validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} - validate-npm-package-name@5.0.1: - resolution: {integrity: sha512-OljLrQ9SQdOUqTaQxqL5dEfZWrXExyyWsozYlAWFawPVNuD83igl7uJD2RTkNMbniIYgt8l81eCJGIdQF7avLQ==} - engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + validate-npm-package-name@6.0.2: + resolution: {integrity: sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==} + engines: {node: ^18.17.0 || >=20.5.0} vary@1.1.2: resolution: {integrity: sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=} engines: {node: '>= 0.8'} - vite@5.4.21: - resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==} - engines: {node: ^18.0.0 || >=20.0.0} + vite@6.4.1: + resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' less: '*' lightningcss: ^1.21.0 sass: '*' sass-embedded: '*' stylus: '*' sugarss: '*' - terser: ^5.4.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 peerDependenciesMeta: '@types/node': optional: true + jiti: + optional: true less: optional: true lightningcss: @@ -3845,9 +4292,21 @@ packages: optional: true terser: optional: true + tsx: + optional: true + yaml: + optional: true - watchpack@2.4.1: - resolution: {integrity: sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==} + void-elements@2.0.1: + resolution: {integrity: sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==} + engines: {node: '>=0.10.0'} + + watchpack@2.4.2: + resolution: {integrity: sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==} + engines: {node: '>=10.13.0'} + + watchpack@2.4.4: + resolution: {integrity: sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==} engines: {node: '>=10.13.0'} wbuf@1.7.3: @@ -3899,8 +4358,8 @@ packages: html-webpack-plugin: optional: true - webpack@5.94.0: - resolution: {integrity: sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==} + webpack@5.98.0: + resolution: {integrity: sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -3917,14 +4376,18 @@ packages: resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} engines: {node: '>=0.8.0'} + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true - which@4.0.0: - resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==} - engines: {node: ^16.13.0 || >=18.0.0} + which@5.0.0: + resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==} + engines: {node: ^18.17.0 || >=20.5.0} hasBin: true wildcard@2.0.1: @@ -3946,6 +4409,21 @@ packages: resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} engines: {node: '>=18'} + wrappy@1.0.2: + resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=} + + ws@8.17.1: + resolution: {integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==} + 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 + ws@8.18.3: resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} @@ -3968,10 +4446,22 @@ packages: yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yallist@5.0.0: + resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} + engines: {node: '>=18'} + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + yargs@17.7.2: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} @@ -3984,8 +4474,8 @@ packages: resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} engines: {node: '>=18'} - zone.js@0.14.10: - resolution: {integrity: sha512-YGAhaO7J5ywOXW6InXNlLmfU194F8lVgu7bRntUF3TiG8Y3nBK0x1UJJuHUP/e8IyihkjCYqhCScpSwnlaSRkQ==} + zone.js@0.15.1: + resolution: {integrity: sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==} snapshots: @@ -3996,21 +4486,21 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@angular-devkit/architect@0.1802.21(chokidar@3.6.0)': + '@angular-devkit/architect@0.1902.19(chokidar@4.0.3)': dependencies: - '@angular-devkit/core': 18.2.21(chokidar@3.6.0) + '@angular-devkit/core': 19.2.19(chokidar@4.0.3) rxjs: 7.8.1 transitivePeerDependencies: - chokidar - '@angular-devkit/build-angular@18.2.21(@angular/compiler-cli@18.2.14(@angular/compiler@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(typescript@5.4.5))(@types/node@20.19.25)(chokidar@3.6.0)(tailwindcss@3.4.18)(typescript@5.4.5)': + '@angular-devkit/build-angular@19.2.19(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.7.3))(@angular/compiler@19.2.15)(@types/node@24.10.1)(chokidar@4.0.3)(jiti@1.21.7)(karma@6.4.4)(tailwindcss@3.4.18)(typescript@5.7.3)(vite@6.4.1(@types/node@24.10.1)(jiti@1.21.7)(less@4.2.2)(sass@1.85.0)(terser@5.39.0))': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.1802.21(chokidar@3.6.0) - '@angular-devkit/build-webpack': 0.1802.21(chokidar@3.6.0)(webpack-dev-server@5.2.2(webpack@5.94.0))(webpack@5.94.0(esbuild@0.23.0)) - '@angular-devkit/core': 18.2.21(chokidar@3.6.0) - '@angular/build': 18.2.21(@angular/compiler-cli@18.2.14(@angular/compiler@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(typescript@5.4.5))(@types/node@20.19.25)(chokidar@3.6.0)(less@4.2.0)(postcss@8.4.41)(tailwindcss@3.4.18)(terser@5.31.6)(typescript@5.4.5) - '@angular/compiler-cli': 18.2.14(@angular/compiler@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(typescript@5.4.5) + '@angular-devkit/architect': 0.1902.19(chokidar@4.0.3) + '@angular-devkit/build-webpack': 0.1902.19(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.98.0))(webpack@5.98.0) + '@angular-devkit/core': 19.2.19(chokidar@4.0.3) + '@angular/build': 19.2.19(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.7.3))(@angular/compiler@19.2.15)(@types/node@24.10.1)(chokidar@4.0.3)(jiti@1.21.7)(karma@6.4.4)(less@4.2.2)(postcss@8.5.2)(tailwindcss@3.4.18)(terser@5.39.0)(typescript@5.7.3) + '@angular/compiler-cli': 19.2.15(@angular/compiler@19.2.15)(typescript@5.7.3) '@babel/core': 7.26.10 '@babel/generator': 7.26.10 '@babel/helper-annotate-as-pure': 7.25.9 @@ -4020,57 +4510,54 @@ snapshots: '@babel/plugin-transform-runtime': 7.26.10(@babel/core@7.26.10) '@babel/preset-env': 7.26.9(@babel/core@7.26.10) '@babel/runtime': 7.26.10 - '@discoveryjs/json-ext': 0.6.1 - '@ngtools/webpack': 18.2.21(@angular/compiler-cli@18.2.14(@angular/compiler@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(typescript@5.4.5))(typescript@5.4.5)(webpack@5.94.0(esbuild@0.23.0)) + '@discoveryjs/json-ext': 0.6.3 + '@ngtools/webpack': 19.2.19(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.7.3))(typescript@5.7.3)(webpack@5.98.0) + '@vitejs/plugin-basic-ssl': 1.2.0(vite@6.4.1(@types/node@24.10.1)(jiti@1.21.7)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)) ansi-colors: 4.1.3 - autoprefixer: 10.4.20(postcss@8.4.41) - babel-loader: 9.1.3(@babel/core@7.26.10)(webpack@5.94.0(esbuild@0.23.0)) + autoprefixer: 10.4.20(postcss@8.5.2) + babel-loader: 9.2.1(@babel/core@7.26.10)(webpack@5.98.0) browserslist: 4.28.0 - copy-webpack-plugin: 12.0.2(webpack@5.94.0(esbuild@0.23.0)) - critters: 0.0.24 - css-loader: 7.1.2(webpack@5.94.0(esbuild@0.23.0)) - esbuild-wasm: 0.23.0 - fast-glob: 3.3.2 + copy-webpack-plugin: 12.0.2(webpack@5.98.0) + css-loader: 7.1.2(webpack@5.98.0) + esbuild-wasm: 0.25.4 + fast-glob: 3.3.3 http-proxy-middleware: 3.0.5 - https-proxy-agent: 7.0.5 istanbul-lib-instrument: 6.0.3 jsonc-parser: 3.3.1 karma-source-map-support: 1.4.0 - less: 4.2.0 - less-loader: 12.2.0(less@4.2.0)(webpack@5.94.0(esbuild@0.23.0)) - license-webpack-plugin: 4.0.2(webpack@5.94.0(esbuild@0.23.0)) + less: 4.2.2 + less-loader: 12.2.0(less@4.2.2)(webpack@5.98.0) + license-webpack-plugin: 4.0.2(webpack@5.98.0) loader-utils: 3.3.1 - magic-string: 0.30.11 - mini-css-extract-plugin: 2.9.0(webpack@5.94.0(esbuild@0.23.0)) - mrmime: 2.0.0 + mini-css-extract-plugin: 2.9.2(webpack@5.98.0) open: 10.1.0 ora: 5.4.1 - parse5-html-rewriting-stream: 7.0.0 picomatch: 4.0.2 - piscina: 4.6.1 - postcss: 8.4.41 - postcss-loader: 8.1.1(postcss@8.4.41)(typescript@5.4.5)(webpack@5.94.0(esbuild@0.23.0)) + piscina: 4.8.0 + postcss: 8.5.2 + postcss-loader: 8.1.1(postcss@8.5.2)(typescript@5.7.3)(webpack@5.98.0) resolve-url-loader: 5.0.0 rxjs: 7.8.1 - sass: 1.77.6 - sass-loader: 16.0.0(sass@1.77.6)(webpack@5.94.0(esbuild@0.23.0)) - semver: 7.6.3 - source-map-loader: 5.0.0(webpack@5.94.0(esbuild@0.23.0)) + sass: 1.85.0 + sass-loader: 16.0.5(sass@1.85.0)(webpack@5.98.0) + semver: 7.7.1 + source-map-loader: 5.0.0(webpack@5.98.0) source-map-support: 0.5.21 - terser: 5.31.6 + terser: 5.39.0 tree-kill: 1.2.2 - tslib: 2.6.3 - typescript: 5.4.5 - watchpack: 2.4.1 - webpack: 5.94.0(esbuild@0.23.0) - webpack-dev-middleware: 7.4.2(webpack@5.94.0) - webpack-dev-server: 5.2.2(webpack@5.94.0) + tslib: 2.8.1 + typescript: 5.7.3 + webpack: 5.98.0(esbuild@0.25.4) + webpack-dev-middleware: 7.4.2(webpack@5.98.0) + webpack-dev-server: 5.2.2(webpack@5.98.0) webpack-merge: 6.0.1 - webpack-subresource-integrity: 5.1.0(webpack@5.94.0(esbuild@0.23.0)) + webpack-subresource-integrity: 5.1.0(webpack@5.98.0) optionalDependencies: - esbuild: 0.23.0 + esbuild: 0.25.4 + karma: 6.4.4 tailwindcss: 3.4.18 transitivePeerDependencies: + - '@angular/compiler' - '@rspack/core' - '@swc/core' - '@types/node' @@ -4078,26 +4565,30 @@ snapshots: - chokidar - debug - html-webpack-plugin + - jiti - lightningcss - node-sass - sass-embedded - stylus - sugarss - supports-color + - tsx - uglify-js - utf-8-validate + - vite - webpack-cli + - yaml - '@angular-devkit/build-webpack@0.1802.21(chokidar@3.6.0)(webpack-dev-server@5.2.2(webpack@5.94.0))(webpack@5.94.0(esbuild@0.23.0))': + '@angular-devkit/build-webpack@0.1902.19(chokidar@4.0.3)(webpack-dev-server@5.2.2(webpack@5.98.0))(webpack@5.98.0)': dependencies: - '@angular-devkit/architect': 0.1802.21(chokidar@3.6.0) + '@angular-devkit/architect': 0.1902.19(chokidar@4.0.3) rxjs: 7.8.1 - webpack: 5.94.0(esbuild@0.23.0) - webpack-dev-server: 5.2.2(webpack@5.94.0) + webpack: 5.98.0(esbuild@0.25.4) + webpack-dev-server: 5.2.2(webpack@5.98.0) transitivePeerDependencies: - chokidar - '@angular-devkit/core@18.2.21(chokidar@3.6.0)': + '@angular-devkit/core@19.2.19(chokidar@4.0.3)': dependencies: ajv: 8.17.1 ajv-formats: 3.0.1(ajv@8.17.1) @@ -4106,160 +4597,150 @@ snapshots: rxjs: 7.8.1 source-map: 0.7.4 optionalDependencies: - chokidar: 3.6.0 + chokidar: 4.0.3 - '@angular-devkit/schematics@18.2.21(chokidar@3.6.0)': + '@angular-devkit/schematics@19.2.19(chokidar@4.0.3)': dependencies: - '@angular-devkit/core': 18.2.21(chokidar@3.6.0) + '@angular-devkit/core': 19.2.19(chokidar@4.0.3) jsonc-parser: 3.3.1 - magic-string: 0.30.11 + magic-string: 0.30.17 ora: 5.4.1 rxjs: 7.8.1 transitivePeerDependencies: - chokidar - '@angular/animations@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))': - dependencies: - '@angular/core': 18.2.14(rxjs@7.8.2)(zone.js@0.14.10) - tslib: 2.8.1 - - '@angular/build@18.2.21(@angular/compiler-cli@18.2.14(@angular/compiler@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(typescript@5.4.5))(@types/node@20.19.25)(chokidar@3.6.0)(less@4.2.0)(postcss@8.4.41)(tailwindcss@3.4.18)(terser@5.31.6)(typescript@5.4.5)': + '@angular/build@19.2.19(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.7.3))(@angular/compiler@19.2.15)(@types/node@24.10.1)(chokidar@4.0.3)(jiti@1.21.7)(karma@6.4.4)(less@4.2.2)(postcss@8.5.2)(tailwindcss@3.4.18)(terser@5.39.0)(typescript@5.7.3)': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.1802.21(chokidar@3.6.0) - '@angular/compiler-cli': 18.2.14(@angular/compiler@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(typescript@5.4.5) - '@babel/core': 7.25.2 - '@babel/helper-annotate-as-pure': 7.24.7 + '@angular-devkit/architect': 0.1902.19(chokidar@4.0.3) + '@angular/compiler': 19.2.15 + '@angular/compiler-cli': 19.2.15(@angular/compiler@19.2.15)(typescript@5.7.3) + '@babel/core': 7.26.10 + '@babel/helper-annotate-as-pure': 7.25.9 '@babel/helper-split-export-declaration': 7.24.7 - '@babel/plugin-syntax-import-attributes': 7.24.7(@babel/core@7.25.2) - '@inquirer/confirm': 3.1.22 - '@vitejs/plugin-basic-ssl': 1.1.0(vite@5.4.21(@types/node@20.19.25)(less@4.2.0)(sass@1.77.6)(terser@5.31.6)) + '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.10) + '@inquirer/confirm': 5.1.6(@types/node@24.10.1) + '@vitejs/plugin-basic-ssl': 1.2.0(vite@6.4.1(@types/node@24.10.1)(jiti@1.21.7)(less@4.2.2)(sass@1.85.0)(terser@5.39.0)) + beasties: 0.3.2 browserslist: 4.28.0 - critters: 0.0.24 - esbuild: 0.23.0 - fast-glob: 3.3.2 - https-proxy-agent: 7.0.5 - listr2: 8.2.4 - lmdb: 3.0.13 - magic-string: 0.30.11 - mrmime: 2.0.0 + esbuild: 0.25.4 + fast-glob: 3.3.3 + https-proxy-agent: 7.0.6 + istanbul-lib-instrument: 6.0.3 + listr2: 8.2.5 + magic-string: 0.30.17 + mrmime: 2.0.1 parse5-html-rewriting-stream: 7.0.0 picomatch: 4.0.2 - piscina: 4.6.1 - rollup: 4.22.4 - sass: 1.77.6 - semver: 7.6.3 - typescript: 5.4.5 - vite: 5.4.21(@types/node@20.19.25)(less@4.2.0)(sass@1.77.6)(terser@5.31.6) - watchpack: 2.4.1 + piscina: 4.8.0 + rollup: 4.34.8 + sass: 1.85.0 + semver: 7.7.1 + source-map-support: 0.5.21 + typescript: 5.7.3 + vite: 6.4.1(@types/node@24.10.1)(jiti@1.21.7)(less@4.2.2)(sass@1.85.0)(terser@5.39.0) + watchpack: 2.4.2 optionalDependencies: - less: 4.2.0 - postcss: 8.4.41 + karma: 6.4.4 + less: 4.2.2 + lmdb: 3.2.6 + postcss: 8.5.2 tailwindcss: 3.4.18 transitivePeerDependencies: - '@types/node' - chokidar + - jiti - lightningcss - sass-embedded - stylus - sugarss - supports-color - terser + - tsx + - yaml - '@angular/cdk@20.2.13(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2)': + '@angular/cli@19.2.19(@types/node@24.10.1)(chokidar@4.0.3)': dependencies: - '@angular/common': 18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2) - '@angular/core': 18.2.14(rxjs@7.8.2)(zone.js@0.14.10) - parse5: 8.0.0 - rxjs: 7.8.2 - tslib: 2.8.1 - - '@angular/cli@18.2.21(chokidar@3.6.0)': - dependencies: - '@angular-devkit/architect': 0.1802.21(chokidar@3.6.0) - '@angular-devkit/core': 18.2.21(chokidar@3.6.0) - '@angular-devkit/schematics': 18.2.21(chokidar@3.6.0) - '@inquirer/prompts': 5.3.8 - '@listr2/prompt-adapter-inquirer': 2.0.15(@inquirer/prompts@5.3.8) - '@schematics/angular': 18.2.21(chokidar@3.6.0) + '@angular-devkit/architect': 0.1902.19(chokidar@4.0.3) + '@angular-devkit/core': 19.2.19(chokidar@4.0.3) + '@angular-devkit/schematics': 19.2.19(chokidar@4.0.3) + '@inquirer/prompts': 7.3.2(@types/node@24.10.1) + '@listr2/prompt-adapter-inquirer': 2.0.18(@inquirer/prompts@7.3.2(@types/node@24.10.1)) + '@schematics/angular': 19.2.19(chokidar@4.0.3) '@yarnpkg/lockfile': 1.1.0 - ini: 4.1.3 + ini: 5.0.0 jsonc-parser: 3.3.1 - listr2: 8.2.4 - npm-package-arg: 11.0.3 - npm-pick-manifest: 9.1.0 - pacote: 18.0.6 - resolve: 1.22.8 - semver: 7.6.3 + listr2: 8.2.5 + npm-package-arg: 12.0.2 + npm-pick-manifest: 10.0.0 + pacote: 20.0.0 + resolve: 1.22.10 + semver: 7.7.1 symbol-observable: 4.0.0 yargs: 17.7.2 transitivePeerDependencies: - - bluebird + - '@types/node' - chokidar - supports-color - '@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2)': + '@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': dependencies: - '@angular/core': 18.2.14(rxjs@7.8.2)(zone.js@0.14.10) + '@angular/core': 19.2.15(rxjs@7.8.2)(zone.js@0.15.1) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/compiler-cli@18.2.14(@angular/compiler@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(typescript@5.4.5)': + '@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.7.3)': dependencies: - '@angular/compiler': 18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)) - '@babel/core': 7.25.2 + '@angular/compiler': 19.2.15 + '@babel/core': 7.26.9 '@jridgewell/sourcemap-codec': 1.5.5 chokidar: 4.0.3 convert-source-map: 1.9.0 reflect-metadata: 0.2.2 semver: 7.7.3 tslib: 2.8.1 - typescript: 5.4.5 + typescript: 5.7.3 yargs: 17.7.2 transitivePeerDependencies: - supports-color - '@angular/compiler@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))': + '@angular/compiler@19.2.15': dependencies: tslib: 2.8.1 - optionalDependencies: - '@angular/core': 18.2.14(rxjs@7.8.2)(zone.js@0.14.10) - '@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)': + '@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1)': dependencies: rxjs: 7.8.2 tslib: 2.8.1 - zone.js: 0.14.10 + zone.js: 0.15.1 - '@angular/forms@18.2.14(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(@angular/platform-browser@18.2.14(@angular/animations@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(rxjs@7.8.2)': + '@angular/forms@19.2.15(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.15(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)': dependencies: - '@angular/common': 18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2) - '@angular/core': 18.2.14(rxjs@7.8.2)(zone.js@0.14.10) - '@angular/platform-browser': 18.2.14(@angular/animations@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)) + '@angular/common': 19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 19.2.15(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/platform-browser': 19.2.15(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1)) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/platform-browser-dynamic@18.2.14(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/compiler@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(@angular/platform-browser@18.2.14(@angular/animations@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))': + '@angular/platform-browser-dynamic@19.2.15(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@19.2.15)(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.15(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1)))': dependencies: - '@angular/common': 18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2) - '@angular/compiler': 18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)) - '@angular/core': 18.2.14(rxjs@7.8.2)(zone.js@0.14.10) - '@angular/platform-browser': 18.2.14(@angular/animations@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)) + '@angular/common': 19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/compiler': 19.2.15 + '@angular/core': 19.2.15(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/platform-browser': 19.2.15(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1)) tslib: 2.8.1 - '@angular/platform-browser@18.2.14(@angular/animations@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))': + '@angular/platform-browser@19.2.15(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))': dependencies: - '@angular/common': 18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2) - '@angular/core': 18.2.14(rxjs@7.8.2)(zone.js@0.14.10) + '@angular/common': 19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 19.2.15(rxjs@7.8.2)(zone.js@0.15.1) tslib: 2.8.1 - optionalDependencies: - '@angular/animations': 18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)) - '@angular/router@18.2.14(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(@angular/platform-browser@18.2.14(@angular/animations@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(rxjs@7.8.2)': + '@angular/router@19.2.15(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@19.2.15(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)': dependencies: - '@angular/common': 18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2) - '@angular/core': 18.2.14(rxjs@7.8.2)(zone.js@0.14.10) - '@angular/platform-browser': 18.2.14(@angular/animations@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)) + '@angular/common': 19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 19.2.15(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/platform-browser': 19.2.15(@angular/common@19.2.15(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@19.2.15(rxjs@7.8.2)(zone.js@0.15.1)) rxjs: 7.8.2 tslib: 2.8.1 @@ -4271,26 +4752,6 @@ snapshots: '@babel/compat-data@7.28.5': {} - '@babel/core@7.25.2': - dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.5 - '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.25.2) - '@babel/helpers': 7.28.4 - '@babel/parser': 7.28.5 - '@babel/template': 7.27.2 - '@babel/traverse': 7.28.5 - '@babel/types': 7.28.5 - convert-source-map: 2.0.0 - debug: 4.4.3 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - '@babel/core@7.26.10': dependencies: '@ampproject/remapping': 2.3.0 @@ -4311,6 +4772,46 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/core@7.26.9': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.26.9) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/core@7.28.5': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.5 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.5) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + '@babel/generator@7.26.10': dependencies: '@babel/parser': 7.28.5 @@ -4327,10 +4828,6 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 - '@babel/helper-annotate-as-pure@7.24.7': - dependencies: - '@babel/types': 7.28.5 - '@babel/helper-annotate-as-pure@7.25.9': dependencies: '@babel/types': 7.28.5 @@ -4394,18 +4891,27 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.3(@babel/core@7.25.2)': + '@babel/helper-module-transforms@7.28.3(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.26.10 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.3(@babel/core@7.26.10)': + '@babel/helper-module-transforms@7.28.3(@babel/core@7.26.9)': dependencies: - '@babel/core': 7.26.10 + '@babel/core': 7.26.9 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.5)': + dependencies: + '@babel/core': 7.28.5 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 '@babel/traverse': 7.28.5 @@ -4514,9 +5020,9 @@ snapshots: '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-attributes@7.24.7(@babel/core@7.25.2)': + '@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.10)': dependencies: - '@babel/core': 7.25.2 + '@babel/core': 7.26.10 '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.26.10)': @@ -4937,7 +5443,7 @@ snapshots: babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.26.10) babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.26.10) babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.26.10) - core-js-compat: 3.46.0 + core-js-compat: 3.47.0 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -4976,253 +5482,220 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@discoveryjs/json-ext@0.6.1': {} + '@colors/colors@1.5.0': {} - '@esbuild/aix-ppc64@0.21.5': + '@discoveryjs/json-ext@0.6.3': {} + + '@esbuild/aix-ppc64@0.25.4': optional: true - '@esbuild/aix-ppc64@0.23.0': + '@esbuild/android-arm64@0.25.4': optional: true - '@esbuild/android-arm64@0.21.5': + '@esbuild/android-arm@0.25.4': optional: true - '@esbuild/android-arm64@0.23.0': + '@esbuild/android-x64@0.25.4': optional: true - '@esbuild/android-arm@0.21.5': + '@esbuild/darwin-arm64@0.25.4': optional: true - '@esbuild/android-arm@0.23.0': + '@esbuild/darwin-x64@0.25.4': optional: true - '@esbuild/android-x64@0.21.5': + '@esbuild/freebsd-arm64@0.25.4': optional: true - '@esbuild/android-x64@0.23.0': + '@esbuild/freebsd-x64@0.25.4': optional: true - '@esbuild/darwin-arm64@0.21.5': + '@esbuild/linux-arm64@0.25.4': optional: true - '@esbuild/darwin-arm64@0.23.0': + '@esbuild/linux-arm@0.25.4': optional: true - '@esbuild/darwin-x64@0.21.5': + '@esbuild/linux-ia32@0.25.4': optional: true - '@esbuild/darwin-x64@0.23.0': + '@esbuild/linux-loong64@0.25.4': optional: true - '@esbuild/freebsd-arm64@0.21.5': + '@esbuild/linux-mips64el@0.25.4': optional: true - '@esbuild/freebsd-arm64@0.23.0': + '@esbuild/linux-ppc64@0.25.4': optional: true - '@esbuild/freebsd-x64@0.21.5': + '@esbuild/linux-riscv64@0.25.4': optional: true - '@esbuild/freebsd-x64@0.23.0': + '@esbuild/linux-s390x@0.25.4': optional: true - '@esbuild/linux-arm64@0.21.5': + '@esbuild/linux-x64@0.25.4': optional: true - '@esbuild/linux-arm64@0.23.0': + '@esbuild/netbsd-arm64@0.25.4': optional: true - '@esbuild/linux-arm@0.21.5': + '@esbuild/netbsd-x64@0.25.4': optional: true - '@esbuild/linux-arm@0.23.0': + '@esbuild/openbsd-arm64@0.25.4': optional: true - '@esbuild/linux-ia32@0.21.5': + '@esbuild/openbsd-x64@0.25.4': optional: true - '@esbuild/linux-ia32@0.23.0': + '@esbuild/sunos-x64@0.25.4': optional: true - '@esbuild/linux-loong64@0.21.5': + '@esbuild/win32-arm64@0.25.4': optional: true - '@esbuild/linux-loong64@0.23.0': + '@esbuild/win32-ia32@0.25.4': optional: true - '@esbuild/linux-mips64el@0.21.5': + '@esbuild/win32-x64@0.25.4': optional: true - '@esbuild/linux-mips64el@0.23.0': - optional: true + '@inquirer/ansi@1.0.2': {} - '@esbuild/linux-ppc64@0.21.5': - optional: true - - '@esbuild/linux-ppc64@0.23.0': - optional: true - - '@esbuild/linux-riscv64@0.21.5': - optional: true - - '@esbuild/linux-riscv64@0.23.0': - optional: true - - '@esbuild/linux-s390x@0.21.5': - optional: true - - '@esbuild/linux-s390x@0.23.0': - optional: true - - '@esbuild/linux-x64@0.21.5': - optional: true - - '@esbuild/linux-x64@0.23.0': - optional: true - - '@esbuild/netbsd-x64@0.21.5': - optional: true - - '@esbuild/netbsd-x64@0.23.0': - optional: true - - '@esbuild/openbsd-arm64@0.23.0': - optional: true - - '@esbuild/openbsd-x64@0.21.5': - optional: true - - '@esbuild/openbsd-x64@0.23.0': - optional: true - - '@esbuild/sunos-x64@0.21.5': - optional: true - - '@esbuild/sunos-x64@0.23.0': - optional: true - - '@esbuild/win32-arm64@0.21.5': - optional: true - - '@esbuild/win32-arm64@0.23.0': - optional: true - - '@esbuild/win32-ia32@0.21.5': - optional: true - - '@esbuild/win32-ia32@0.23.0': - optional: true - - '@esbuild/win32-x64@0.21.5': - optional: true - - '@esbuild/win32-x64@0.23.0': - optional: true - - '@inquirer/checkbox@2.5.0': + '@inquirer/checkbox@4.3.2(@types/node@24.10.1)': dependencies: - '@inquirer/core': 9.2.1 + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@24.10.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 1.5.5 - ansi-escapes: 4.3.2 + '@inquirer/type': 3.0.10(@types/node@24.10.1) yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.10.1 - '@inquirer/confirm@3.1.22': + '@inquirer/confirm@5.1.21(@types/node@24.10.1)': dependencies: - '@inquirer/core': 9.2.1 - '@inquirer/type': 1.5.5 + '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.1) + optionalDependencies: + '@types/node': 24.10.1 - '@inquirer/confirm@3.2.0': + '@inquirer/confirm@5.1.6(@types/node@24.10.1)': dependencies: - '@inquirer/core': 9.2.1 - '@inquirer/type': 1.5.5 + '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.1) + optionalDependencies: + '@types/node': 24.10.1 - '@inquirer/core@9.2.1': + '@inquirer/core@10.3.2(@types/node@24.10.1)': dependencies: + '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 2.0.0 - '@types/mute-stream': 0.0.4 - '@types/node': 22.19.1 - '@types/wrap-ansi': 3.0.0 - ansi-escapes: 4.3.2 + '@inquirer/type': 3.0.10(@types/node@24.10.1) cli-width: 4.1.0 - mute-stream: 1.0.0 + mute-stream: 2.0.0 signal-exit: 4.1.0 - strip-ansi: 6.0.1 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.10.1 - '@inquirer/editor@2.2.0': + '@inquirer/editor@4.2.23(@types/node@24.10.1)': dependencies: - '@inquirer/core': 9.2.1 - '@inquirer/type': 1.5.5 - external-editor: 3.1.0 + '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/external-editor': 1.0.3(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.1) + optionalDependencies: + '@types/node': 24.10.1 - '@inquirer/expand@2.3.0': + '@inquirer/expand@4.0.23(@types/node@24.10.1)': dependencies: - '@inquirer/core': 9.2.1 - '@inquirer/type': 1.5.5 + '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.1) yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.10.1 + + '@inquirer/external-editor@1.0.3(@types/node@24.10.1)': + dependencies: + chardet: 2.1.1 + iconv-lite: 0.7.0 + optionalDependencies: + '@types/node': 24.10.1 '@inquirer/figures@1.0.15': {} - '@inquirer/input@2.3.0': + '@inquirer/input@4.3.1(@types/node@24.10.1)': dependencies: - '@inquirer/core': 9.2.1 - '@inquirer/type': 1.5.5 + '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.1) + optionalDependencies: + '@types/node': 24.10.1 - '@inquirer/number@1.1.0': + '@inquirer/number@3.0.23(@types/node@24.10.1)': dependencies: - '@inquirer/core': 9.2.1 - '@inquirer/type': 1.5.5 + '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.1) + optionalDependencies: + '@types/node': 24.10.1 - '@inquirer/password@2.2.0': + '@inquirer/password@4.0.23(@types/node@24.10.1)': dependencies: - '@inquirer/core': 9.2.1 - '@inquirer/type': 1.5.5 - ansi-escapes: 4.3.2 + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.1) + optionalDependencies: + '@types/node': 24.10.1 - '@inquirer/prompts@5.3.8': + '@inquirer/prompts@7.3.2(@types/node@24.10.1)': dependencies: - '@inquirer/checkbox': 2.5.0 - '@inquirer/confirm': 3.2.0 - '@inquirer/editor': 2.2.0 - '@inquirer/expand': 2.3.0 - '@inquirer/input': 2.3.0 - '@inquirer/number': 1.1.0 - '@inquirer/password': 2.2.0 - '@inquirer/rawlist': 2.3.0 - '@inquirer/search': 1.1.0 - '@inquirer/select': 2.5.0 + '@inquirer/checkbox': 4.3.2(@types/node@24.10.1) + '@inquirer/confirm': 5.1.21(@types/node@24.10.1) + '@inquirer/editor': 4.2.23(@types/node@24.10.1) + '@inquirer/expand': 4.0.23(@types/node@24.10.1) + '@inquirer/input': 4.3.1(@types/node@24.10.1) + '@inquirer/number': 3.0.23(@types/node@24.10.1) + '@inquirer/password': 4.0.23(@types/node@24.10.1) + '@inquirer/rawlist': 4.1.11(@types/node@24.10.1) + '@inquirer/search': 3.2.2(@types/node@24.10.1) + '@inquirer/select': 4.4.2(@types/node@24.10.1) + optionalDependencies: + '@types/node': 24.10.1 - '@inquirer/rawlist@2.3.0': + '@inquirer/rawlist@4.1.11(@types/node@24.10.1)': dependencies: - '@inquirer/core': 9.2.1 - '@inquirer/type': 1.5.5 + '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.1) yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.10.1 - '@inquirer/search@1.1.0': + '@inquirer/search@3.2.2(@types/node@24.10.1)': dependencies: - '@inquirer/core': 9.2.1 + '@inquirer/core': 10.3.2(@types/node@24.10.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 1.5.5 + '@inquirer/type': 3.0.10(@types/node@24.10.1) yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.10.1 - '@inquirer/select@2.5.0': + '@inquirer/select@4.4.2(@types/node@24.10.1)': dependencies: - '@inquirer/core': 9.2.1 + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@24.10.1) '@inquirer/figures': 1.0.15 - '@inquirer/type': 1.5.5 - ansi-escapes: 4.3.2 + '@inquirer/type': 3.0.10(@types/node@24.10.1) yoctocolors-cjs: 2.1.3 + optionalDependencies: + '@types/node': 24.10.1 '@inquirer/type@1.5.5': dependencies: mute-stream: 1.0.0 - '@inquirer/type@2.0.0': - dependencies: - mute-stream: 1.0.0 + '@inquirer/type@3.0.10(@types/node@24.10.1)': + optionalDependencies: + '@types/node': 24.10.1 '@isaacs/cliui@8.0.2': dependencies: @@ -5233,6 +5706,10 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@isaacs/fs-minipass@4.0.1': + dependencies: + minipass: 7.1.2 + '@istanbuljs/schema@0.1.3': {} '@jridgewell/gen-mapping@0.3.13': @@ -5240,6 +5717,11 @@ snapshots: '@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/source-map@0.3.11': @@ -5290,31 +5772,29 @@ snapshots: '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) tslib: 2.8.1 - '@kurkle/color@0.3.4': {} - '@leichtgewicht/ip-codec@2.0.5': {} - '@listr2/prompt-adapter-inquirer@2.0.15(@inquirer/prompts@5.3.8)': + '@listr2/prompt-adapter-inquirer@2.0.18(@inquirer/prompts@7.3.2(@types/node@24.10.1))': dependencies: - '@inquirer/prompts': 5.3.8 + '@inquirer/prompts': 7.3.2(@types/node@24.10.1) '@inquirer/type': 1.5.5 - '@lmdb/lmdb-darwin-arm64@3.0.13': + '@lmdb/lmdb-darwin-arm64@3.2.6': optional: true - '@lmdb/lmdb-darwin-x64@3.0.13': + '@lmdb/lmdb-darwin-x64@3.2.6': optional: true - '@lmdb/lmdb-linux-arm64@3.0.13': + '@lmdb/lmdb-linux-arm64@3.2.6': optional: true - '@lmdb/lmdb-linux-arm@3.0.13': + '@lmdb/lmdb-linux-arm@3.2.6': optional: true - '@lmdb/lmdb-linux-x64@3.0.13': + '@lmdb/lmdb-linux-x64@3.2.6': optional: true - '@lmdb/lmdb-win32-x64@3.0.13': + '@lmdb/lmdb-win32-x64@3.2.6': optional: true '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': @@ -5335,11 +5815,83 @@ snapshots: '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': optional: true - '@ngtools/webpack@18.2.21(@angular/compiler-cli@18.2.14(@angular/compiler@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(typescript@5.4.5))(typescript@5.4.5)(webpack@5.94.0(esbuild@0.23.0))': + '@napi-rs/nice-android-arm-eabi@1.1.1': + optional: true + + '@napi-rs/nice-android-arm64@1.1.1': + optional: true + + '@napi-rs/nice-darwin-arm64@1.1.1': + optional: true + + '@napi-rs/nice-darwin-x64@1.1.1': + optional: true + + '@napi-rs/nice-freebsd-x64@1.1.1': + optional: true + + '@napi-rs/nice-linux-arm-gnueabihf@1.1.1': + optional: true + + '@napi-rs/nice-linux-arm64-gnu@1.1.1': + optional: true + + '@napi-rs/nice-linux-arm64-musl@1.1.1': + optional: true + + '@napi-rs/nice-linux-ppc64-gnu@1.1.1': + optional: true + + '@napi-rs/nice-linux-riscv64-gnu@1.1.1': + optional: true + + '@napi-rs/nice-linux-s390x-gnu@1.1.1': + optional: true + + '@napi-rs/nice-linux-x64-gnu@1.1.1': + optional: true + + '@napi-rs/nice-linux-x64-musl@1.1.1': + optional: true + + '@napi-rs/nice-openharmony-arm64@1.1.1': + optional: true + + '@napi-rs/nice-win32-arm64-msvc@1.1.1': + optional: true + + '@napi-rs/nice-win32-ia32-msvc@1.1.1': + optional: true + + '@napi-rs/nice-win32-x64-msvc@1.1.1': + optional: true + + '@napi-rs/nice@1.1.1': + optionalDependencies: + '@napi-rs/nice-android-arm-eabi': 1.1.1 + '@napi-rs/nice-android-arm64': 1.1.1 + '@napi-rs/nice-darwin-arm64': 1.1.1 + '@napi-rs/nice-darwin-x64': 1.1.1 + '@napi-rs/nice-freebsd-x64': 1.1.1 + '@napi-rs/nice-linux-arm-gnueabihf': 1.1.1 + '@napi-rs/nice-linux-arm64-gnu': 1.1.1 + '@napi-rs/nice-linux-arm64-musl': 1.1.1 + '@napi-rs/nice-linux-ppc64-gnu': 1.1.1 + '@napi-rs/nice-linux-riscv64-gnu': 1.1.1 + '@napi-rs/nice-linux-s390x-gnu': 1.1.1 + '@napi-rs/nice-linux-x64-gnu': 1.1.1 + '@napi-rs/nice-linux-x64-musl': 1.1.1 + '@napi-rs/nice-openharmony-arm64': 1.1.1 + '@napi-rs/nice-win32-arm64-msvc': 1.1.1 + '@napi-rs/nice-win32-ia32-msvc': 1.1.1 + '@napi-rs/nice-win32-x64-msvc': 1.1.1 + optional: true + + '@ngtools/webpack@19.2.19(@angular/compiler-cli@19.2.15(@angular/compiler@19.2.15)(typescript@5.7.3))(typescript@5.7.3)(webpack@5.98.0)': dependencies: - '@angular/compiler-cli': 18.2.14(@angular/compiler@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(typescript@5.4.5) - typescript: 5.4.5 - webpack: 5.94.0(esbuild@0.23.0) + '@angular/compiler-cli': 19.2.15(@angular/compiler@19.2.15)(typescript@5.7.3) + typescript: 5.7.3 + webpack: 5.98.0(esbuild@0.25.4) '@nodelib/fs.scandir@2.1.5': dependencies: @@ -5353,7 +5905,7 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@npmcli/agent@2.2.2': + '@npmcli/agent@3.0.0': dependencies: agent-base: 7.1.4 http-proxy-agent: 7.0.2 @@ -5363,157 +5915,289 @@ snapshots: transitivePeerDependencies: - supports-color - '@npmcli/fs@3.1.1': + '@npmcli/fs@4.0.0': dependencies: - semver: 7.6.3 + semver: 7.7.1 - '@npmcli/git@5.0.8': + '@npmcli/git@6.0.3': dependencies: - '@npmcli/promise-spawn': 7.0.2 - ini: 4.1.3 + '@npmcli/promise-spawn': 8.0.3 + ini: 5.0.0 lru-cache: 10.4.3 - npm-pick-manifest: 9.1.0 - proc-log: 4.2.0 - promise-inflight: 1.0.1 + npm-pick-manifest: 10.0.0 + proc-log: 5.0.0 promise-retry: 2.0.1 - semver: 7.6.3 - which: 4.0.0 + semver: 7.7.1 + which: 5.0.0 + + '@npmcli/installed-package-contents@3.0.0': + dependencies: + npm-bundled: 4.0.0 + npm-normalize-package-bin: 4.0.0 + + '@npmcli/node-gyp@4.0.0': {} + + '@npmcli/package-json@6.2.0': + dependencies: + '@npmcli/git': 6.0.3 + glob: 10.5.0 + hosted-git-info: 8.1.0 + json-parse-even-better-errors: 4.0.0 + proc-log: 5.0.0 + semver: 7.7.1 + validate-npm-package-license: 3.0.4 + + '@npmcli/promise-spawn@8.0.3': + dependencies: + which: 5.0.0 + + '@npmcli/redact@3.2.2': {} + + '@npmcli/run-script@9.1.0': + dependencies: + '@npmcli/node-gyp': 4.0.0 + '@npmcli/package-json': 6.2.0 + '@npmcli/promise-spawn': 8.0.3 + node-gyp: 11.5.0 + proc-log: 5.0.0 + which: 5.0.0 transitivePeerDependencies: - - bluebird - - '@npmcli/installed-package-contents@2.1.0': - dependencies: - npm-bundled: 3.0.1 - npm-normalize-package-bin: 3.0.1 - - '@npmcli/node-gyp@3.0.0': {} - - '@npmcli/package-json@5.2.1': - dependencies: - '@npmcli/git': 5.0.8 - glob: 10.4.5 - hosted-git-info: 7.0.2 - json-parse-even-better-errors: 3.0.2 - normalize-package-data: 6.0.2 - proc-log: 4.2.0 - semver: 7.6.3 - transitivePeerDependencies: - - bluebird - - '@npmcli/promise-spawn@7.0.2': - dependencies: - which: 4.0.0 - - '@npmcli/redact@2.0.1': {} - - '@npmcli/run-script@8.1.0': - dependencies: - '@npmcli/node-gyp': 3.0.0 - '@npmcli/package-json': 5.2.1 - '@npmcli/promise-spawn': 7.0.2 - node-gyp: 10.3.1 - proc-log: 4.2.0 - which: 4.0.0 - transitivePeerDependencies: - - bluebird - supports-color + '@parcel/watcher-android-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-arm64@2.5.1': + optional: true + + '@parcel/watcher-darwin-x64@2.5.1': + optional: true + + '@parcel/watcher-freebsd-x64@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.5.1': + optional: true + + '@parcel/watcher-linux-x64-musl@2.5.1': + optional: true + + '@parcel/watcher-win32-arm64@2.5.1': + optional: true + + '@parcel/watcher-win32-ia32@2.5.1': + optional: true + + '@parcel/watcher-win32-x64@2.5.1': + optional: true + + '@parcel/watcher@2.5.1': + dependencies: + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.8 + node-addon-api: 7.1.1 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.5.1 + '@parcel/watcher-darwin-arm64': 2.5.1 + '@parcel/watcher-darwin-x64': 2.5.1 + '@parcel/watcher-freebsd-x64': 2.5.1 + '@parcel/watcher-linux-arm-glibc': 2.5.1 + '@parcel/watcher-linux-arm-musl': 2.5.1 + '@parcel/watcher-linux-arm64-glibc': 2.5.1 + '@parcel/watcher-linux-arm64-musl': 2.5.1 + '@parcel/watcher-linux-x64-glibc': 2.5.1 + '@parcel/watcher-linux-x64-musl': 2.5.1 + '@parcel/watcher-win32-arm64': 2.5.1 + '@parcel/watcher-win32-ia32': 2.5.1 + '@parcel/watcher-win32-x64': 2.5.1 + optional: true + '@pkgjs/parseargs@0.11.0': optional: true - '@rollup/rollup-android-arm-eabi@4.22.4': + '@rollup/rollup-android-arm-eabi@4.34.8': optional: true - '@rollup/rollup-android-arm64@4.22.4': + '@rollup/rollup-android-arm-eabi@4.53.3': optional: true - '@rollup/rollup-darwin-arm64@4.22.4': + '@rollup/rollup-android-arm64@4.34.8': optional: true - '@rollup/rollup-darwin-x64@4.22.4': + '@rollup/rollup-android-arm64@4.53.3': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.22.4': + '@rollup/rollup-darwin-arm64@4.34.8': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.22.4': + '@rollup/rollup-darwin-arm64@4.53.3': optional: true - '@rollup/rollup-linux-arm64-gnu@4.22.4': + '@rollup/rollup-darwin-x64@4.34.8': optional: true - '@rollup/rollup-linux-arm64-musl@4.22.4': + '@rollup/rollup-darwin-x64@4.53.3': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.22.4': + '@rollup/rollup-freebsd-arm64@4.34.8': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.22.4': + '@rollup/rollup-freebsd-arm64@4.53.3': optional: true - '@rollup/rollup-linux-s390x-gnu@4.22.4': + '@rollup/rollup-freebsd-x64@4.34.8': optional: true - '@rollup/rollup-linux-x64-gnu@4.22.4': + '@rollup/rollup-freebsd-x64@4.53.3': optional: true - '@rollup/rollup-linux-x64-musl@4.22.4': + '@rollup/rollup-linux-arm-gnueabihf@4.34.8': optional: true - '@rollup/rollup-win32-arm64-msvc@4.22.4': + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': optional: true - '@rollup/rollup-win32-ia32-msvc@4.22.4': + '@rollup/rollup-linux-arm-musleabihf@4.34.8': optional: true - '@rollup/rollup-win32-x64-msvc@4.22.4': + '@rollup/rollup-linux-arm-musleabihf@4.53.3': optional: true - '@schematics/angular@18.2.21(chokidar@3.6.0)': + '@rollup/rollup-linux-arm64-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.34.8': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.53.3': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-loongarch64-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-powerpc64le-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.53.3': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.34.8': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-x64-musl@4.34.8': + optional: true + + '@rollup/rollup-linux-x64-musl@4.53.3': + optional: true + + '@rollup/rollup-openharmony-arm64@4.53.3': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.34.8': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.53.3': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.34.8': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.53.3': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.53.3': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.34.8': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.53.3': + optional: true + + '@schematics/angular@19.2.19(chokidar@4.0.3)': dependencies: - '@angular-devkit/core': 18.2.21(chokidar@3.6.0) - '@angular-devkit/schematics': 18.2.21(chokidar@3.6.0) + '@angular-devkit/core': 19.2.19(chokidar@4.0.3) + '@angular-devkit/schematics': 19.2.19(chokidar@4.0.3) jsonc-parser: 3.3.1 transitivePeerDependencies: - chokidar - '@sigstore/bundle@2.3.2': + '@sigstore/bundle@3.1.0': dependencies: - '@sigstore/protobuf-specs': 0.3.3 + '@sigstore/protobuf-specs': 0.4.3 - '@sigstore/core@1.1.0': {} + '@sigstore/core@2.0.0': {} - '@sigstore/protobuf-specs@0.3.3': {} + '@sigstore/protobuf-specs@0.4.3': {} - '@sigstore/sign@2.3.2': + '@sigstore/sign@3.1.0': dependencies: - '@sigstore/bundle': 2.3.2 - '@sigstore/core': 1.1.0 - '@sigstore/protobuf-specs': 0.3.3 - make-fetch-happen: 13.0.1 - proc-log: 4.2.0 + '@sigstore/bundle': 3.1.0 + '@sigstore/core': 2.0.0 + '@sigstore/protobuf-specs': 0.4.3 + make-fetch-happen: 14.0.3 + proc-log: 5.0.0 promise-retry: 2.0.1 transitivePeerDependencies: - supports-color - '@sigstore/tuf@2.3.4': + '@sigstore/tuf@3.1.1': dependencies: - '@sigstore/protobuf-specs': 0.3.3 - tuf-js: 2.2.1 + '@sigstore/protobuf-specs': 0.4.3 + tuf-js: 3.1.0 transitivePeerDependencies: - supports-color - '@sigstore/verify@1.2.1': + '@sigstore/verify@2.1.1': dependencies: - '@sigstore/bundle': 2.3.2 - '@sigstore/core': 1.1.0 - '@sigstore/protobuf-specs': 0.3.3 + '@sigstore/bundle': 3.1.0 + '@sigstore/core': 2.0.0 + '@sigstore/protobuf-specs': 0.4.3 '@sindresorhus/merge-streams@2.3.0': {} + '@socket.io/component-emitter@3.1.2': {} + '@tufjs/canonical-json@2.0.0': {} - '@tufjs/models@2.0.1': + '@tufjs/models@3.0.1': dependencies: '@tufjs/canonical-json': 2.0.0 minimatch: 9.0.5 @@ -5521,28 +6205,42 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 20.19.25 + '@types/node': 24.10.1 '@types/bonjour@3.5.13': dependencies: - '@types/node': 20.19.25 + '@types/node': 24.10.1 '@types/connect-history-api-fallback@1.5.4': dependencies: '@types/express-serve-static-core': 4.19.7 - '@types/node': 20.19.25 + '@types/node': 24.10.1 '@types/connect@3.4.38': dependencies: - '@types/node': 20.19.25 + '@types/node': 24.10.1 - '@types/estree@1.0.5': {} + '@types/cors@2.8.19': + dependencies: + '@types/node': 24.10.1 + + '@types/eslint-scope@3.7.7': + dependencies: + '@types/eslint': 9.6.1 + '@types/estree': 1.0.8 + + '@types/eslint@9.6.1': + dependencies: + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + + '@types/estree@1.0.6': {} '@types/estree@1.0.8': {} '@types/express-serve-static-core@4.19.7': dependencies: - '@types/node': 20.19.25 + '@types/node': 24.10.1 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -5558,27 +6256,21 @@ snapshots: '@types/http-proxy@1.17.17': dependencies: - '@types/node': 20.19.25 + '@types/node': 24.10.1 + + '@types/jasmine@5.1.13': {} '@types/json-schema@7.0.15': {} '@types/mime@1.3.5': {} - '@types/mute-stream@0.0.4': - dependencies: - '@types/node': 20.19.25 - '@types/node-forge@1.3.14': dependencies: - '@types/node': 20.19.25 + '@types/node': 24.10.1 - '@types/node@20.19.25': + '@types/node@24.10.1': dependencies: - undici-types: 6.21.0 - - '@types/node@22.19.1': - dependencies: - undici-types: 6.21.0 + undici-types: 7.16.0 '@types/qs@6.14.0': {} @@ -5589,11 +6281,11 @@ snapshots: '@types/send@0.17.6': dependencies: '@types/mime': 1.3.5 - '@types/node': 20.19.25 + '@types/node': 24.10.1 '@types/send@1.2.1': dependencies: - '@types/node': 20.19.25 + '@types/node': 24.10.1 '@types/serve-index@1.9.4': dependencies: @@ -5602,22 +6294,20 @@ snapshots: '@types/serve-static@1.15.10': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 20.19.25 + '@types/node': 24.10.1 '@types/send': 0.17.6 '@types/sockjs@0.3.36': dependencies: - '@types/node': 20.19.25 - - '@types/wrap-ansi@3.0.0': {} + '@types/node': 24.10.1 '@types/ws@8.18.1': dependencies: - '@types/node': 20.19.25 + '@types/node': 24.10.1 - '@vitejs/plugin-basic-ssl@1.1.0(vite@5.4.21(@types/node@20.19.25)(less@4.2.0)(sass@1.77.6)(terser@5.31.6))': + '@vitejs/plugin-basic-ssl@1.2.0(vite@6.4.1(@types/node@24.10.1)(jiti@1.21.7)(less@4.2.2)(sass@1.85.0)(terser@5.39.0))': dependencies: - vite: 5.4.21(@types/node@20.19.25)(less@4.2.0)(sass@1.77.6)(terser@5.31.6) + vite: 6.4.1(@types/node@24.10.1)(jiti@1.21.7)(less@4.2.2)(sass@1.85.0)(terser@5.39.0) '@webassemblyjs/ast@1.14.1': dependencies: @@ -5701,17 +6391,13 @@ snapshots: '@yarnpkg/lockfile@1.1.0': {} - abbrev@2.0.0: {} + abbrev@3.0.1: {} accepts@1.3.8: dependencies: mime-types: 2.1.35 negotiator: 0.6.3 - acorn-import-attributes@1.9.5(acorn@8.15.0): - dependencies: - acorn: 8.15.0 - acorn@8.15.0: {} adjust-sourcemap-loader@4.0.0: @@ -5721,11 +6407,6 @@ snapshots: agent-base@7.1.4: {} - aggregate-error@3.1.0: - dependencies: - clean-stack: 2.2.0 - indent-string: 4.0.0 - ajv-formats@2.1.1(ajv@8.17.1): optionalDependencies: ajv: 8.17.1 @@ -5734,22 +6415,11 @@ snapshots: optionalDependencies: ajv: 8.17.1 - ajv-keywords@3.5.2(ajv@6.12.6): - dependencies: - ajv: 6.12.6 - ajv-keywords@5.1.0(ajv@8.17.1): dependencies: ajv: 8.17.1 fast-deep-equal: 3.1.3 - ajv@6.12.6: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - ajv@8.17.1: dependencies: fast-deep-equal: 3.1.3 @@ -5759,10 +6429,6 @@ snapshots: ansi-colors@4.1.3: {} - ansi-escapes@4.3.2: - dependencies: - type-fest: 0.21.3 - ansi-escapes@7.2.0: dependencies: environment: 1.1.0 @@ -5792,32 +6458,32 @@ snapshots: array-flatten@1.1.1: {} - autoprefixer@10.4.20(postcss@8.4.41): + autoprefixer@10.4.20(postcss@8.5.2): dependencies: browserslist: 4.28.0 - caniuse-lite: 1.0.30001755 + caniuse-lite: 1.0.30001757 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 - postcss: 8.4.41 + postcss: 8.5.2 postcss-value-parser: 4.2.0 autoprefixer@10.4.22(postcss@8.5.6): dependencies: browserslist: 4.28.0 - caniuse-lite: 1.0.30001755 + caniuse-lite: 1.0.30001757 fraction.js: 5.3.4 normalize-range: 0.1.2 picocolors: 1.1.1 postcss: 8.5.6 postcss-value-parser: 4.2.0 - babel-loader@9.1.3(@babel/core@7.26.10)(webpack@5.94.0(esbuild@0.23.0)): + babel-loader@9.2.1(@babel/core@7.26.10)(webpack@5.98.0): dependencies: '@babel/core': 7.26.10 find-cache-dir: 4.0.0 schema-utils: 4.3.3 - webpack: 5.94.0(esbuild@0.23.0) + webpack: 5.98.0(esbuild@0.25.4) babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.26.10): dependencies: @@ -5832,7 +6498,7 @@ snapshots: dependencies: '@babel/core': 7.26.10 '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.26.10) - core-js-compat: 3.46.0 + core-js-compat: 3.47.0 transitivePeerDependencies: - supports-color @@ -5847,10 +6513,23 @@ snapshots: base64-js@1.5.1: {} - baseline-browser-mapping@2.8.29: {} + base64id@2.0.0: {} + + baseline-browser-mapping@2.8.31: {} batch@0.6.1: {} + beasties@0.3.2: + dependencies: + css-select: 5.2.2 + css-what: 6.2.2 + dom-serializer: 2.0.0 + domhandler: 5.0.3 + htmlparser2: 10.0.0 + picocolors: 1.1.1 + postcss: 8.5.6 + postcss-media-query-parser: 0.2.3 + big.js@5.2.2: {} binary-extensions@2.3.0: {} @@ -5885,6 +6564,11 @@ snapshots: boolbase@1.0.0: {} + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 @@ -5895,9 +6579,9 @@ snapshots: browserslist@4.28.0: dependencies: - baseline-browser-mapping: 2.8.29 - caniuse-lite: 1.0.30001755 - electron-to-chromium: 1.5.254 + baseline-browser-mapping: 2.8.31 + caniuse-lite: 1.0.30001757 + electron-to-chromium: 1.5.259 node-releases: 2.0.27 update-browserslist-db: 1.1.4(browserslist@4.28.0) @@ -5914,20 +6598,20 @@ snapshots: bytes@3.1.2: {} - cacache@18.0.4: + cacache@19.0.1: dependencies: - '@npmcli/fs': 3.1.1 + '@npmcli/fs': 4.0.0 fs-minipass: 3.0.3 - glob: 10.4.5 + glob: 10.5.0 lru-cache: 10.4.3 minipass: 7.1.2 minipass-collect: 2.0.1 minipass-flush: 1.0.5 minipass-pipeline: 1.2.4 - p-map: 4.0.0 - ssri: 10.0.6 - tar: 6.2.1 - unique-filename: 3.0.0 + p-map: 7.0.4 + ssri: 12.0.0 + tar: 7.5.2 + unique-filename: 4.0.0 call-bind-apply-helpers@1.0.2: dependencies: @@ -5943,18 +6627,14 @@ snapshots: camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001755: {} + caniuse-lite@1.0.30001757: {} chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - chardet@0.7.0: {} - - chart.js@4.5.1: - dependencies: - '@kurkle/color': 0.3.4 + chardet@2.1.1: {} chokidar@3.6.0: dependencies: @@ -5974,9 +6654,9 @@ snapshots: chownr@2.0.0: {} - chrome-trace-event@1.0.4: {} + chownr@3.0.0: {} - clean-stack@2.2.0: {} + chrome-trace-event@1.0.4: {} cli-cursor@3.1.0: dependencies: @@ -5995,6 +6675,12 @@ snapshots: cli-width@4.1.0: {} + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -6039,8 +6725,19 @@ snapshots: transitivePeerDependencies: - supports-color + concat-map@0.0.1: {} + connect-history-api-fallback@2.0.0: {} + connect@3.7.0: + dependencies: + debug: 2.6.9 + finalhandler: 1.1.2 + parseurl: 1.3.3 + utils-merge: 1.0.1 + transitivePeerDependencies: + - supports-color + content-disposition@0.5.4: dependencies: safe-buffer: 5.2.1 @@ -6055,44 +6752,41 @@ snapshots: cookie@0.7.1: {} + cookie@0.7.2: {} + copy-anything@2.0.6: dependencies: is-what: 3.14.1 - copy-webpack-plugin@12.0.2(webpack@5.94.0(esbuild@0.23.0)): + copy-webpack-plugin@12.0.2(webpack@5.98.0): dependencies: - fast-glob: 3.3.2 + fast-glob: 3.3.3 glob-parent: 6.0.2 globby: 14.1.0 normalize-path: 3.0.0 schema-utils: 4.3.3 serialize-javascript: 6.0.2 - webpack: 5.94.0(esbuild@0.23.0) + webpack: 5.98.0(esbuild@0.25.4) - core-js-compat@3.46.0: + core-js-compat@3.47.0: dependencies: browserslist: 4.28.0 core-util-is@1.0.3: {} - cosmiconfig@9.0.0(typescript@5.4.5): + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + cosmiconfig@9.0.0(typescript@5.7.3): dependencies: env-paths: 2.2.1 import-fresh: 3.3.1 js-yaml: 4.1.1 parse-json: 5.2.0 optionalDependencies: - typescript: 5.4.5 - - critters@0.0.24: - dependencies: - chalk: 4.1.2 - css-select: 5.2.2 - dom-serializer: 2.0.0 - domhandler: 5.0.3 - htmlparser2: 8.0.2 - postcss: 8.5.6 - postcss-media-query-parser: 0.2.3 + typescript: 5.7.3 cross-spawn@7.0.6: dependencies: @@ -6100,7 +6794,7 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - css-loader@7.1.2(webpack@5.94.0(esbuild@0.23.0)): + css-loader@7.1.2(webpack@5.98.0): dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 @@ -6109,9 +6803,9 @@ snapshots: postcss-modules-scope: 3.2.1(postcss@8.5.6) postcss-modules-values: 4.0.0(postcss@8.5.6) postcss-value-parser: 4.2.0 - semver: 7.6.3 + semver: 7.7.1 optionalDependencies: - webpack: 5.94.0(esbuild@0.23.0) + webpack: 5.98.0(esbuild@0.25.4) css-select@5.2.2: dependencies: @@ -6125,10 +6819,18 @@ snapshots: cssesc@3.0.0: {} + custom-event@1.0.1: {} + + date-format@4.0.14: {} + debug@2.6.9: dependencies: ms: 2.0.0 + debug@4.3.7: + dependencies: + ms: 2.1.3 + debug@4.4.3: dependencies: ms: 2.1.3 @@ -6152,10 +6854,16 @@ snapshots: destroy@1.2.0: {} - detect-libc@2.1.2: {} + detect-libc@1.0.3: + optional: true + + detect-libc@2.1.2: + optional: true detect-node@2.1.0: {} + di@0.0.1: {} + didyoumean@1.2.2: {} dlv@1.1.3: {} @@ -6164,6 +6872,13 @@ snapshots: dependencies: '@leichtgewicht/ip-codec': 2.0.5 + dom-serialize@2.2.1: + dependencies: + custom-event: 1.0.1 + ent: 2.2.2 + extend: 3.0.2 + void-elements: 2.0.1 + dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 @@ -6192,7 +6907,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.254: {} + electron-to-chromium@1.5.259: {} emoji-regex@10.6.0: {} @@ -6211,11 +6926,36 @@ snapshots: iconv-lite: 0.6.3 optional: true + engine.io-parser@5.2.3: {} + + engine.io@6.6.4: + dependencies: + '@types/cors': 2.8.19 + '@types/node': 24.10.1 + accepts: 1.3.8 + base64id: 2.0.0 + cookie: 0.7.2 + cors: 2.8.5 + debug: 4.3.7 + engine.io-parser: 5.2.3 + ws: 8.17.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + enhanced-resolve@5.18.3: dependencies: graceful-fs: 4.2.11 tapable: 2.3.0 + ent@2.2.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + punycode: 1.4.1 + safe-regex-test: 1.1.0 + entities@4.5.0: {} entities@6.0.1: {} @@ -6245,60 +6985,35 @@ snapshots: dependencies: es-errors: 1.3.0 - esbuild-wasm@0.23.0: {} + esbuild-wasm@0.25.4: {} - esbuild@0.21.5: + esbuild@0.25.4: optionalDependencies: - '@esbuild/aix-ppc64': 0.21.5 - '@esbuild/android-arm': 0.21.5 - '@esbuild/android-arm64': 0.21.5 - '@esbuild/android-x64': 0.21.5 - '@esbuild/darwin-arm64': 0.21.5 - '@esbuild/darwin-x64': 0.21.5 - '@esbuild/freebsd-arm64': 0.21.5 - '@esbuild/freebsd-x64': 0.21.5 - '@esbuild/linux-arm': 0.21.5 - '@esbuild/linux-arm64': 0.21.5 - '@esbuild/linux-ia32': 0.21.5 - '@esbuild/linux-loong64': 0.21.5 - '@esbuild/linux-mips64el': 0.21.5 - '@esbuild/linux-ppc64': 0.21.5 - '@esbuild/linux-riscv64': 0.21.5 - '@esbuild/linux-s390x': 0.21.5 - '@esbuild/linux-x64': 0.21.5 - '@esbuild/netbsd-x64': 0.21.5 - '@esbuild/openbsd-x64': 0.21.5 - '@esbuild/sunos-x64': 0.21.5 - '@esbuild/win32-arm64': 0.21.5 - '@esbuild/win32-ia32': 0.21.5 - '@esbuild/win32-x64': 0.21.5 - - esbuild@0.23.0: - optionalDependencies: - '@esbuild/aix-ppc64': 0.23.0 - '@esbuild/android-arm': 0.23.0 - '@esbuild/android-arm64': 0.23.0 - '@esbuild/android-x64': 0.23.0 - '@esbuild/darwin-arm64': 0.23.0 - '@esbuild/darwin-x64': 0.23.0 - '@esbuild/freebsd-arm64': 0.23.0 - '@esbuild/freebsd-x64': 0.23.0 - '@esbuild/linux-arm': 0.23.0 - '@esbuild/linux-arm64': 0.23.0 - '@esbuild/linux-ia32': 0.23.0 - '@esbuild/linux-loong64': 0.23.0 - '@esbuild/linux-mips64el': 0.23.0 - '@esbuild/linux-ppc64': 0.23.0 - '@esbuild/linux-riscv64': 0.23.0 - '@esbuild/linux-s390x': 0.23.0 - '@esbuild/linux-x64': 0.23.0 - '@esbuild/netbsd-x64': 0.23.0 - '@esbuild/openbsd-arm64': 0.23.0 - '@esbuild/openbsd-x64': 0.23.0 - '@esbuild/sunos-x64': 0.23.0 - '@esbuild/win32-arm64': 0.23.0 - '@esbuild/win32-ia32': 0.23.0 - '@esbuild/win32-x64': 0.23.0 + '@esbuild/aix-ppc64': 0.25.4 + '@esbuild/android-arm': 0.25.4 + '@esbuild/android-arm64': 0.25.4 + '@esbuild/android-x64': 0.25.4 + '@esbuild/darwin-arm64': 0.25.4 + '@esbuild/darwin-x64': 0.25.4 + '@esbuild/freebsd-arm64': 0.25.4 + '@esbuild/freebsd-x64': 0.25.4 + '@esbuild/linux-arm': 0.25.4 + '@esbuild/linux-arm64': 0.25.4 + '@esbuild/linux-ia32': 0.25.4 + '@esbuild/linux-loong64': 0.25.4 + '@esbuild/linux-mips64el': 0.25.4 + '@esbuild/linux-ppc64': 0.25.4 + '@esbuild/linux-riscv64': 0.25.4 + '@esbuild/linux-s390x': 0.25.4 + '@esbuild/linux-x64': 0.25.4 + '@esbuild/netbsd-arm64': 0.25.4 + '@esbuild/netbsd-x64': 0.25.4 + '@esbuild/openbsd-arm64': 0.25.4 + '@esbuild/openbsd-x64': 0.25.4 + '@esbuild/sunos-x64': 0.25.4 + '@esbuild/win32-arm64': 0.25.4 + '@esbuild/win32-ia32': 0.25.4 + '@esbuild/win32-x64': 0.25.4 escalade@3.2.0: {} @@ -6365,22 +7080,10 @@ snapshots: transitivePeerDependencies: - supports-color - external-editor@3.1.0: - dependencies: - chardet: 0.7.0 - iconv-lite: 0.4.24 - tmp: 0.0.33 + extend@3.0.2: {} fast-deep-equal@3.1.3: {} - fast-glob@3.3.2: - dependencies: - '@nodelib/fs.stat': 2.0.5 - '@nodelib/fs.walk': 1.2.8 - glob-parent: 5.1.2 - merge2: 1.4.1 - micromatch: 4.0.8 - fast-glob@3.3.3: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -6389,8 +7092,6 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 - fast-json-stable-stringify@2.1.0: {} - fast-uri@3.1.0: {} fastq@1.19.1: @@ -6401,10 +7102,30 @@ snapshots: dependencies: websocket-driver: 0.7.4 + fdir@6.5.0(picomatch@4.0.2): + optionalDependencies: + picomatch: 4.0.2 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 + finalhandler@1.1.2: + dependencies: + debug: 2.6.9 + encodeurl: 1.0.2 + escape-html: 1.0.3 + on-finished: 2.3.0 + parseurl: 1.3.3 + statuses: 1.5.0 + unpipe: 1.0.0 + transitivePeerDependencies: + - supports-color + finalhandler@1.3.1: dependencies: debug: 2.6.9 @@ -6429,6 +7150,8 @@ snapshots: flat@5.0.2: {} + flatted@3.3.3: {} + follow-redirects@1.15.11(debug@4.4.3): optionalDependencies: debug: 4.4.3 @@ -6446,6 +7169,12 @@ snapshots: fresh@0.5.2: {} + fs-extra@8.1.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 4.0.0 + universalify: 0.1.2 + fs-minipass@2.1.0: dependencies: minipass: 3.3.6 @@ -6454,6 +7183,8 @@ snapshots: dependencies: minipass: 7.1.2 + fs.realpath@1.0.0: {} + fsevents@2.3.3: optional: true @@ -6497,7 +7228,7 @@ snapshots: glob-to-regexp@0.4.1: {} - glob@10.4.5: + glob@10.5.0: dependencies: foreground-child: 3.3.1 jackspeak: 3.4.3 @@ -6506,6 +7237,15 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + globby@14.1.0: dependencies: '@sindresorhus/merge-streams': 2.3.0 @@ -6525,11 +7265,15 @@ snapshots: has-symbols@1.1.0: {} + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + hasown@2.0.2: dependencies: function-bind: 1.1.2 - hosted-git-info@7.0.2: + hosted-git-info@8.1.0: dependencies: lru-cache: 10.4.3 @@ -6540,12 +7284,14 @@ snapshots: readable-stream: 2.3.8 wbuf: 1.7.3 - htmlparser2@8.0.2: + html-escaper@2.0.2: {} + + htmlparser2@10.0.0: dependencies: domelementtype: 2.3.0 domhandler: 5.0.3 domutils: 3.2.2 - entities: 4.5.0 + entities: 6.0.1 http-cache-semantics@4.2.0: {} @@ -6606,13 +7352,6 @@ snapshots: transitivePeerDependencies: - debug - https-proxy-agent@7.0.5: - dependencies: - agent-base: 7.1.4 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - https-proxy-agent@7.0.6: dependencies: agent-base: 7.1.4 @@ -6630,13 +7369,17 @@ snapshots: dependencies: safer-buffer: 2.1.2 + iconv-lite@0.7.0: + dependencies: + safer-buffer: 2.1.2 + icss-utils@5.1.0(postcss@8.5.6): dependencies: postcss: 8.5.6 ieee754@1.2.1: {} - ignore-walk@6.0.5: + ignore-walk@7.0.0: dependencies: minimatch: 9.0.5 @@ -6645,7 +7388,7 @@ snapshots: image-size@0.5.5: optional: true - immutable@4.3.7: {} + immutable@5.1.4: {} import-fresh@3.3.1: dependencies: @@ -6654,13 +7397,16 @@ snapshots: imurmurhash@0.1.4: {} - indent-string@4.0.0: {} + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 inherits@2.0.3: {} inherits@2.0.4: {} - ini@4.1.3: {} + ini@5.0.0: {} ip-address@10.1.0: {} @@ -6700,8 +7446,6 @@ snapshots: is-interactive@1.0.0: {} - is-lambda@1.0.1: {} - is-network-error@1.3.0: {} is-number@7.0.0: {} @@ -6714,6 +7458,13 @@ snapshots: is-plain-object@5.0.0: {} + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + is-unicode-supported@0.1.0: {} is-what@3.14.1: {} @@ -6724,6 +7475,8 @@ snapshots: isarray@1.0.0: {} + isbinaryfile@4.0.10: {} + isexe@2.0.0: {} isexe@3.1.1: {} @@ -6732,25 +7485,58 @@ snapshots: istanbul-lib-coverage@3.2.2: {} + istanbul-lib-instrument@5.2.1: + dependencies: + '@babel/core': 7.28.5 + '@babel/parser': 7.28.5 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + istanbul-lib-instrument@6.0.3: dependencies: '@babel/core': 7.26.10 '@babel/parser': 7.28.5 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 - semver: 7.6.3 + semver: 7.7.1 transitivePeerDependencies: - supports-color + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@4.0.1: + dependencies: + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jasmine-core@4.6.1: {} + + jasmine-core@5.6.0: {} + jest-worker@27.5.1: dependencies: - '@types/node': 20.19.25 + '@types/node': 24.10.1 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -6766,9 +7552,7 @@ snapshots: json-parse-even-better-errors@2.3.1: {} - json-parse-even-better-errors@3.0.2: {} - - json-schema-traverse@0.4.1: {} + json-parse-even-better-errors@4.0.0: {} json-schema-traverse@1.0.0: {} @@ -6776,12 +7560,74 @@ snapshots: jsonc-parser@3.3.1: {} + jsonfile@4.0.0: + optionalDependencies: + graceful-fs: 4.2.11 + jsonparse@1.3.1: {} + karma-chrome-launcher@3.2.0: + dependencies: + which: 1.3.1 + + karma-coverage@2.2.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 5.2.1 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.2.0 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + karma-jasmine-html-reporter@2.1.0(jasmine-core@5.6.0)(karma-jasmine@5.1.0(karma@6.4.4))(karma@6.4.4): + dependencies: + jasmine-core: 5.6.0 + karma: 6.4.4 + karma-jasmine: 5.1.0(karma@6.4.4) + + karma-jasmine@5.1.0(karma@6.4.4): + dependencies: + jasmine-core: 4.6.1 + karma: 6.4.4 + karma-source-map-support@1.4.0: dependencies: source-map-support: 0.5.21 + karma@6.4.4: + dependencies: + '@colors/colors': 1.5.0 + body-parser: 1.20.3 + braces: 3.0.3 + chokidar: 3.6.0 + connect: 3.7.0 + di: 0.0.1 + dom-serialize: 2.2.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + http-proxy: 1.18.1(debug@4.4.3) + isbinaryfile: 4.0.10 + lodash: 4.17.21 + log4js: 6.9.1 + mime: 2.6.0 + minimatch: 3.1.2 + mkdirp: 0.5.6 + qjobs: 1.2.0 + range-parser: 1.2.1 + rimraf: 3.0.2 + socket.io: 4.8.1 + source-map: 0.6.1 + tmp: 0.2.5 + ua-parser-js: 0.7.41 + yargs: 16.2.0 + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + kind-of@6.0.3: {} launch-editor@2.12.0: @@ -6789,13 +7635,13 @@ snapshots: picocolors: 1.1.1 shell-quote: 1.8.3 - less-loader@12.2.0(less@4.2.0)(webpack@5.94.0(esbuild@0.23.0)): + less-loader@12.2.0(less@4.2.2)(webpack@5.98.0): dependencies: - less: 4.2.0 + less: 4.2.2 optionalDependencies: - webpack: 5.94.0(esbuild@0.23.0) + webpack: 5.98.0(esbuild@0.25.4) - less@4.2.0: + less@4.2.2: dependencies: copy-anything: 2.0.6 parse-node-version: 1.0.1 @@ -6809,17 +7655,17 @@ snapshots: needle: 3.3.1 source-map: 0.6.1 - license-webpack-plugin@4.0.2(webpack@5.94.0(esbuild@0.23.0)): + license-webpack-plugin@4.0.2(webpack@5.98.0): dependencies: webpack-sources: 3.3.3 optionalDependencies: - webpack: 5.94.0(esbuild@0.23.0) + webpack: 5.98.0(esbuild@0.25.4) lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} - listr2@8.2.4: + listr2@8.2.5: dependencies: cli-truncate: 4.0.0 colorette: 2.0.20 @@ -6828,7 +7674,7 @@ snapshots: rfdc: 1.4.1 wrap-ansi: 9.0.2 - lmdb@3.0.13: + lmdb@3.2.6: dependencies: msgpackr: 1.11.5 node-addon-api: 6.1.0 @@ -6836,12 +7682,13 @@ snapshots: ordered-binary: 1.6.0 weak-lru-cache: 1.2.2 optionalDependencies: - '@lmdb/lmdb-darwin-arm64': 3.0.13 - '@lmdb/lmdb-darwin-x64': 3.0.13 - '@lmdb/lmdb-linux-arm': 3.0.13 - '@lmdb/lmdb-linux-arm64': 3.0.13 - '@lmdb/lmdb-linux-x64': 3.0.13 - '@lmdb/lmdb-win32-x64': 3.0.13 + '@lmdb/lmdb-darwin-arm64': 3.2.6 + '@lmdb/lmdb-darwin-x64': 3.2.6 + '@lmdb/lmdb-linux-arm': 3.2.6 + '@lmdb/lmdb-linux-arm64': 3.2.6 + '@lmdb/lmdb-linux-x64': 3.2.6 + '@lmdb/lmdb-win32-x64': 3.2.6 + optional: true loader-runner@4.3.1: {} @@ -6857,10 +7704,10 @@ snapshots: dependencies: p-locate: 6.0.0 - lodash-es@4.17.21: {} - lodash.debounce@4.0.8: {} + lodash@4.17.21: {} + log-symbols@4.1.0: dependencies: chalk: 4.1.2 @@ -6874,13 +7721,23 @@ snapshots: strip-ansi: 7.1.2 wrap-ansi: 9.0.2 + log4js@6.9.1: + dependencies: + date-format: 4.0.14 + debug: 4.4.3 + flatted: 3.3.3 + rfdc: 1.4.1 + streamroller: 3.1.5 + transitivePeerDependencies: + - supports-color + lru-cache@10.4.3: {} lru-cache@5.1.1: dependencies: yallist: 3.1.1 - magic-string@0.30.11: + magic-string@0.30.17: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -6890,20 +7747,23 @@ snapshots: semver: 5.7.2 optional: true - make-fetch-happen@13.0.1: + make-dir@4.0.0: dependencies: - '@npmcli/agent': 2.2.2 - cacache: 18.0.4 + semver: 7.7.3 + + make-fetch-happen@14.0.3: + dependencies: + '@npmcli/agent': 3.0.0 + cacache: 19.0.1 http-cache-semantics: 4.2.0 - is-lambda: 1.0.1 minipass: 7.1.2 - minipass-fetch: 3.0.5 + minipass-fetch: 4.0.1 minipass-flush: 1.0.5 minipass-pipeline: 1.2.4 - negotiator: 0.6.4 - proc-log: 4.2.0 + negotiator: 1.0.0 + proc-log: 5.0.0 promise-retry: 2.0.1 - ssri: 10.0.6 + ssri: 12.0.0 transitivePeerDependencies: - supports-color @@ -6943,31 +7803,39 @@ snapshots: mime@1.6.0: {} + mime@2.6.0: {} + mimic-fn@2.1.0: {} mimic-function@5.0.1: {} - mini-css-extract-plugin@2.9.0(webpack@5.94.0(esbuild@0.23.0)): + mini-css-extract-plugin@2.9.2(webpack@5.98.0): dependencies: schema-utils: 4.3.3 tapable: 2.3.0 - webpack: 5.94.0(esbuild@0.23.0) + webpack: 5.98.0(esbuild@0.25.4) minimalistic-assert@1.0.1: {} + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + minimatch@9.0.5: dependencies: brace-expansion: 2.0.2 + minimist@1.2.8: {} + minipass-collect@2.0.1: dependencies: minipass: 7.1.2 - minipass-fetch@3.0.5: + minipass-fetch@4.0.1: dependencies: minipass: 7.1.2 minipass-sized: 1.0.3 - minizlib: 2.1.2 + minizlib: 3.1.0 optionalDependencies: encoding: 0.1.13 @@ -6996,9 +7864,17 @@ snapshots: minipass: 3.3.6 yallist: 4.0.0 + minizlib@3.1.0: + dependencies: + minipass: 7.1.2 + + mkdirp@0.5.6: + dependencies: + minimist: 1.2.8 + mkdirp@1.0.4: {} - mrmime@2.0.0: {} + mrmime@2.0.1: {} ms@2.0.0: {} @@ -7019,6 +7895,7 @@ snapshots: msgpackr@1.11.5: optionalDependencies: msgpackr-extract: 3.0.3 + optional: true multicast-dns@7.2.5: dependencies: @@ -7027,6 +7904,8 @@ snapshots: mute-stream@1.0.0: {} + mute-stream@2.0.0: {} + mz@2.7.0: dependencies: any-promise: 1.3.0 @@ -7045,108 +7924,86 @@ snapshots: negotiator@0.6.4: {} + negotiator@1.0.0: {} + neo-async@2.6.2: {} - ng2-charts@6.0.1(@angular/cdk@20.2.13(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(@angular/platform-browser@18.2.14(@angular/animations@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(chart.js@4.5.1)(rxjs@7.8.2): - dependencies: - '@angular/cdk': 20.2.13(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2) - '@angular/common': 18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2) - '@angular/core': 18.2.14(rxjs@7.8.2)(zone.js@0.14.10) - '@angular/platform-browser': 18.2.14(@angular/animations@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)))(@angular/common@18.2.14(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10))(rxjs@7.8.2))(@angular/core@18.2.14(rxjs@7.8.2)(zone.js@0.14.10)) - chart.js: 4.5.1 - lodash-es: 4.17.21 - rxjs: 7.8.2 - tslib: 2.8.1 - - nice-napi@1.0.2: - dependencies: - node-addon-api: 3.2.1 - node-gyp-build: 4.8.4 + node-addon-api@6.1.0: optional: true - node-addon-api@3.2.1: + node-addon-api@7.1.1: optional: true - node-addon-api@6.1.0: {} - node-forge@1.3.1: {} node-gyp-build-optional-packages@5.2.2: dependencies: detect-libc: 2.1.2 - - node-gyp-build@4.8.4: optional: true - node-gyp@10.3.1: + node-gyp@11.5.0: dependencies: env-paths: 2.2.1 exponential-backoff: 3.1.3 - glob: 10.4.5 graceful-fs: 4.2.11 - make-fetch-happen: 13.0.1 - nopt: 7.2.1 - proc-log: 4.2.0 - semver: 7.6.3 - tar: 6.2.1 - which: 4.0.0 + make-fetch-happen: 14.0.3 + nopt: 8.1.0 + proc-log: 5.0.0 + semver: 7.7.1 + tar: 7.5.2 + tinyglobby: 0.2.15 + which: 5.0.0 transitivePeerDependencies: - supports-color node-releases@2.0.27: {} - nopt@7.2.1: + nopt@8.1.0: dependencies: - abbrev: 2.0.0 - - normalize-package-data@6.0.2: - dependencies: - hosted-git-info: 7.0.2 - semver: 7.6.3 - validate-npm-package-license: 3.0.4 + abbrev: 3.0.1 normalize-path@3.0.0: {} normalize-range@0.1.2: {} - npm-bundled@3.0.1: + npm-bundled@4.0.0: dependencies: - npm-normalize-package-bin: 3.0.1 + npm-normalize-package-bin: 4.0.0 - npm-install-checks@6.3.0: + npm-install-checks@7.1.2: dependencies: - semver: 7.6.3 + semver: 7.7.1 - npm-normalize-package-bin@3.0.1: {} + npm-normalize-package-bin@4.0.0: {} - npm-package-arg@11.0.3: + npm-package-arg@12.0.2: dependencies: - hosted-git-info: 7.0.2 - proc-log: 4.2.0 - semver: 7.6.3 - validate-npm-package-name: 5.0.1 + hosted-git-info: 8.1.0 + proc-log: 5.0.0 + semver: 7.7.1 + validate-npm-package-name: 6.0.2 - npm-packlist@8.0.2: + npm-packlist@9.0.0: dependencies: - ignore-walk: 6.0.5 + ignore-walk: 7.0.0 - npm-pick-manifest@9.1.0: + npm-pick-manifest@10.0.0: dependencies: - npm-install-checks: 6.3.0 - npm-normalize-package-bin: 3.0.1 - npm-package-arg: 11.0.3 - semver: 7.6.3 + npm-install-checks: 7.1.2 + npm-normalize-package-bin: 4.0.0 + npm-package-arg: 12.0.2 + semver: 7.7.1 - npm-registry-fetch@17.1.0: + npm-registry-fetch@18.0.2: dependencies: - '@npmcli/redact': 2.0.1 + '@npmcli/redact': 3.2.2 jsonparse: 1.3.1 - make-fetch-happen: 13.0.1 + make-fetch-happen: 14.0.3 minipass: 7.1.2 - minipass-fetch: 3.0.5 - minizlib: 2.1.2 - npm-package-arg: 11.0.3 - proc-log: 4.2.0 + minipass-fetch: 4.0.1 + minizlib: 3.1.0 + npm-package-arg: 12.0.2 + proc-log: 5.0.0 transitivePeerDependencies: - supports-color @@ -7162,12 +8019,20 @@ snapshots: obuf@1.1.2: {} + on-finished@2.3.0: + dependencies: + ee-first: 1.1.1 + on-finished@2.4.1: dependencies: ee-first: 1.1.1 on-headers@1.1.0: {} + once@1.4.0: + dependencies: + wrappy: 1.0.2 + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 @@ -7195,9 +8060,8 @@ snapshots: strip-ansi: 6.0.1 wcwidth: 1.0.1 - ordered-binary@1.6.0: {} - - os-tmpdir@1.0.2: {} + ordered-binary@1.6.0: + optional: true p-limit@4.0.0: dependencies: @@ -7207,9 +8071,7 @@ snapshots: dependencies: p-limit: 4.0.0 - p-map@4.0.0: - dependencies: - aggregate-error: 3.1.0 + p-map@7.0.4: {} p-retry@6.2.1: dependencies: @@ -7219,27 +8081,26 @@ snapshots: package-json-from-dist@1.0.1: {} - pacote@18.0.6: + pacote@20.0.0: dependencies: - '@npmcli/git': 5.0.8 - '@npmcli/installed-package-contents': 2.1.0 - '@npmcli/package-json': 5.2.1 - '@npmcli/promise-spawn': 7.0.2 - '@npmcli/run-script': 8.1.0 - cacache: 18.0.4 + '@npmcli/git': 6.0.3 + '@npmcli/installed-package-contents': 3.0.0 + '@npmcli/package-json': 6.2.0 + '@npmcli/promise-spawn': 8.0.3 + '@npmcli/run-script': 9.1.0 + cacache: 19.0.1 fs-minipass: 3.0.3 minipass: 7.1.2 - npm-package-arg: 11.0.3 - npm-packlist: 8.0.2 - npm-pick-manifest: 9.1.0 - npm-registry-fetch: 17.1.0 - proc-log: 4.2.0 + npm-package-arg: 12.0.2 + npm-packlist: 9.0.0 + npm-pick-manifest: 10.0.0 + npm-registry-fetch: 18.0.2 + proc-log: 5.0.0 promise-retry: 2.0.1 - sigstore: 2.3.1 - ssri: 10.0.6 + sigstore: 3.1.0 + ssri: 12.0.0 tar: 6.2.1 transitivePeerDependencies: - - bluebird - supports-color parent-module@1.0.1: @@ -7269,14 +8130,12 @@ snapshots: dependencies: entities: 6.0.1 - parse5@8.0.0: - dependencies: - entities: 6.0.1 - parseurl@1.3.3: {} path-exists@5.0.0: {} + path-is-absolute@1.0.1: {} + path-key@3.1.1: {} path-parse@1.0.7: {} @@ -7296,6 +8155,8 @@ snapshots: picomatch@4.0.2: {} + picomatch@4.0.3: {} + pify@2.3.0: {} pify@4.0.1: @@ -7303,9 +8164,9 @@ snapshots: pirates@4.0.7: {} - piscina@4.6.1: + piscina@4.8.0: optionalDependencies: - nice-napi: 1.0.2 + '@napi-rs/nice': 1.1.1 pkg-dir@7.0.0: dependencies: @@ -7330,14 +8191,14 @@ snapshots: jiti: 1.21.7 postcss: 8.5.6 - postcss-loader@8.1.1(postcss@8.4.41)(typescript@5.4.5)(webpack@5.94.0(esbuild@0.23.0)): + postcss-loader@8.1.1(postcss@8.5.2)(typescript@5.7.3)(webpack@5.98.0): dependencies: - cosmiconfig: 9.0.0(typescript@5.4.5) + cosmiconfig: 9.0.0(typescript@5.7.3) jiti: 1.21.7 - postcss: 8.4.41 - semver: 7.6.3 + postcss: 8.5.2 + semver: 7.7.1 optionalDependencies: - webpack: 5.94.0(esbuild@0.23.0) + webpack: 5.98.0(esbuild@0.25.4) transitivePeerDependencies: - typescript @@ -7381,7 +8242,7 @@ snapshots: postcss-value-parser@4.2.0: {} - postcss@8.4.41: + postcss@8.5.2: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 @@ -7393,12 +8254,10 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - proc-log@4.2.0: {} + proc-log@5.0.0: {} process-nextick-args@2.0.1: {} - promise-inflight@1.0.1: {} - promise-retry@2.0.1: dependencies: err-code: 2.0.3 @@ -7412,7 +8271,9 @@ snapshots: prr@1.0.1: optional: true - punycode@2.3.1: {} + punycode@1.4.1: {} + + qjobs@1.2.0: {} qs@6.13.0: dependencies: @@ -7502,13 +8363,13 @@ snapshots: postcss: 8.5.6 source-map: 0.6.1 - resolve@1.22.11: + resolve@1.22.10: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - resolve@1.22.8: + resolve@1.22.11: dependencies: is-core-module: 2.16.1 path-parse: 1.0.7 @@ -7532,26 +8393,61 @@ snapshots: rfdc@1.4.1: {} - rollup@4.22.4: + rimraf@3.0.2: dependencies: - '@types/estree': 1.0.5 + glob: 7.2.3 + + rollup@4.34.8: + dependencies: + '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.22.4 - '@rollup/rollup-android-arm64': 4.22.4 - '@rollup/rollup-darwin-arm64': 4.22.4 - '@rollup/rollup-darwin-x64': 4.22.4 - '@rollup/rollup-linux-arm-gnueabihf': 4.22.4 - '@rollup/rollup-linux-arm-musleabihf': 4.22.4 - '@rollup/rollup-linux-arm64-gnu': 4.22.4 - '@rollup/rollup-linux-arm64-musl': 4.22.4 - '@rollup/rollup-linux-powerpc64le-gnu': 4.22.4 - '@rollup/rollup-linux-riscv64-gnu': 4.22.4 - '@rollup/rollup-linux-s390x-gnu': 4.22.4 - '@rollup/rollup-linux-x64-gnu': 4.22.4 - '@rollup/rollup-linux-x64-musl': 4.22.4 - '@rollup/rollup-win32-arm64-msvc': 4.22.4 - '@rollup/rollup-win32-ia32-msvc': 4.22.4 - '@rollup/rollup-win32-x64-msvc': 4.22.4 + '@rollup/rollup-android-arm-eabi': 4.34.8 + '@rollup/rollup-android-arm64': 4.34.8 + '@rollup/rollup-darwin-arm64': 4.34.8 + '@rollup/rollup-darwin-x64': 4.34.8 + '@rollup/rollup-freebsd-arm64': 4.34.8 + '@rollup/rollup-freebsd-x64': 4.34.8 + '@rollup/rollup-linux-arm-gnueabihf': 4.34.8 + '@rollup/rollup-linux-arm-musleabihf': 4.34.8 + '@rollup/rollup-linux-arm64-gnu': 4.34.8 + '@rollup/rollup-linux-arm64-musl': 4.34.8 + '@rollup/rollup-linux-loongarch64-gnu': 4.34.8 + '@rollup/rollup-linux-powerpc64le-gnu': 4.34.8 + '@rollup/rollup-linux-riscv64-gnu': 4.34.8 + '@rollup/rollup-linux-s390x-gnu': 4.34.8 + '@rollup/rollup-linux-x64-gnu': 4.34.8 + '@rollup/rollup-linux-x64-musl': 4.34.8 + '@rollup/rollup-win32-arm64-msvc': 4.34.8 + '@rollup/rollup-win32-ia32-msvc': 4.34.8 + '@rollup/rollup-win32-x64-msvc': 4.34.8 + fsevents: 2.3.3 + + rollup@4.53.3: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.53.3 + '@rollup/rollup-android-arm64': 4.53.3 + '@rollup/rollup-darwin-arm64': 4.53.3 + '@rollup/rollup-darwin-x64': 4.53.3 + '@rollup/rollup-freebsd-arm64': 4.53.3 + '@rollup/rollup-freebsd-x64': 4.53.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 + '@rollup/rollup-linux-arm-musleabihf': 4.53.3 + '@rollup/rollup-linux-arm64-gnu': 4.53.3 + '@rollup/rollup-linux-arm64-musl': 4.53.3 + '@rollup/rollup-linux-loong64-gnu': 4.53.3 + '@rollup/rollup-linux-ppc64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-musl': 4.53.3 + '@rollup/rollup-linux-s390x-gnu': 4.53.3 + '@rollup/rollup-linux-x64-gnu': 4.53.3 + '@rollup/rollup-linux-x64-musl': 4.53.3 + '@rollup/rollup-openharmony-arm64': 4.53.3 + '@rollup/rollup-win32-arm64-msvc': 4.53.3 + '@rollup/rollup-win32-ia32-msvc': 4.53.3 + '@rollup/rollup-win32-x64-gnu': 4.53.3 + '@rollup/rollup-win32-x64-msvc': 4.53.3 fsevents: 2.3.3 run-applescript@7.1.0: {} @@ -7572,30 +8468,32 @@ snapshots: safe-buffer@5.2.1: {} + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + safer-buffer@2.1.2: {} - sass-loader@16.0.0(sass@1.77.6)(webpack@5.94.0(esbuild@0.23.0)): + sass-loader@16.0.5(sass@1.85.0)(webpack@5.98.0): dependencies: neo-async: 2.6.2 optionalDependencies: - sass: 1.77.6 - webpack: 5.94.0(esbuild@0.23.0) + sass: 1.85.0 + webpack: 5.98.0(esbuild@0.25.4) - sass@1.77.6: + sass@1.85.0: dependencies: - chokidar: 3.6.0 - immutable: 4.3.7 + chokidar: 4.0.3 + immutable: 5.1.4 source-map-js: 1.2.1 + optionalDependencies: + '@parcel/watcher': 2.5.1 sax@1.4.3: optional: true - schema-utils@3.3.0: - dependencies: - '@types/json-schema': 7.0.15 - ajv: 6.12.6 - ajv-keywords: 3.5.2(ajv@6.12.6) - schema-utils@4.3.3: dependencies: '@types/json-schema': 7.0.15 @@ -7615,7 +8513,7 @@ snapshots: semver@6.3.1: {} - semver@7.6.3: {} + semver@7.7.1: {} semver@7.7.3: {} @@ -7710,14 +8608,14 @@ snapshots: signal-exit@4.1.0: {} - sigstore@2.3.1: + sigstore@3.1.0: dependencies: - '@sigstore/bundle': 2.3.2 - '@sigstore/core': 1.1.0 - '@sigstore/protobuf-specs': 0.3.3 - '@sigstore/sign': 2.3.2 - '@sigstore/tuf': 2.3.4 - '@sigstore/verify': 1.2.1 + '@sigstore/bundle': 3.1.0 + '@sigstore/core': 2.0.0 + '@sigstore/protobuf-specs': 0.4.3 + '@sigstore/sign': 3.1.0 + '@sigstore/tuf': 3.1.1 + '@sigstore/verify': 2.1.1 transitivePeerDependencies: - supports-color @@ -7735,6 +8633,36 @@ snapshots: smart-buffer@4.2.0: {} + socket.io-adapter@2.5.5: + dependencies: + debug: 4.3.7 + ws: 8.17.1 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.4: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + + socket.io@4.8.1: + dependencies: + accepts: 1.3.8 + base64id: 2.0.0 + cors: 2.8.5 + debug: 4.3.7 + engine.io: 6.6.4 + socket.io-adapter: 2.5.5 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + sockjs@0.3.24: dependencies: faye-websocket: 0.11.4 @@ -7756,11 +8684,11 @@ snapshots: source-map-js@1.2.1: {} - source-map-loader@5.0.0(webpack@5.94.0(esbuild@0.23.0)): + source-map-loader@5.0.0(webpack@5.98.0): dependencies: iconv-lite: 0.6.3 source-map-js: 1.2.1 - webpack: 5.94.0(esbuild@0.23.0) + webpack: 5.98.0(esbuild@0.25.4) source-map-support@0.5.21: dependencies: @@ -7806,7 +8734,7 @@ snapshots: transitivePeerDependencies: - supports-color - ssri@10.0.6: + ssri@12.0.0: dependencies: minipass: 7.1.2 @@ -7814,6 +8742,14 @@ snapshots: statuses@2.0.1: {} + streamroller@3.1.5: + dependencies: + date-format: 4.0.14 + debug: 4.4.3 + fs-extra: 8.1.0 + transitivePeerDependencies: + - supports-color + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -7848,14 +8784,14 @@ snapshots: dependencies: ansi-regex: 6.2.2 - sucrase@3.35.0: + sucrase@3.35.1: dependencies: '@jridgewell/gen-mapping': 0.3.13 commander: 4.1.1 - glob: 10.4.5 lines-and-columns: 1.2.4 mz: 2.7.0 pirates: 4.0.7 + tinyglobby: 0.2.15 ts-interface-checker: 0.1.13 supports-color@7.2.0: @@ -7893,7 +8829,7 @@ snapshots: postcss-nested: 6.2.0(postcss@8.5.6) postcss-selector-parser: 6.1.2 resolve: 1.22.11 - sucrase: 3.35.0 + sucrase: 3.35.1 transitivePeerDependencies: - tsx - yaml @@ -7909,18 +8845,26 @@ snapshots: mkdirp: 1.0.4 yallist: 4.0.0 - terser-webpack-plugin@5.3.14(esbuild@0.23.0)(webpack@5.94.0): + tar@7.5.2: + dependencies: + '@isaacs/fs-minipass': 4.0.1 + chownr: 3.0.0 + minipass: 7.1.2 + minizlib: 3.1.0 + yallist: 5.0.0 + + terser-webpack-plugin@5.3.14(esbuild@0.25.4)(webpack@5.98.0): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 serialize-javascript: 6.0.2 - terser: 5.31.6 - webpack: 5.94.0(esbuild@0.23.0) + terser: 5.39.0 + webpack: 5.98.0(esbuild@0.25.4) optionalDependencies: - esbuild: 0.23.0 + esbuild: 0.25.4 - terser@5.31.6: + terser@5.39.0: dependencies: '@jridgewell/source-map': 0.3.11 acorn: 8.15.0 @@ -7941,9 +8885,12 @@ snapshots: thunky@1.1.0: {} - tmp@0.0.33: + tinyglobby@0.2.15: dependencies: - os-tmpdir: 1.0.2 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tmp@0.2.5: {} to-regex-range@5.0.1: dependencies: @@ -7959,20 +8906,16 @@ snapshots: ts-interface-checker@0.1.13: {} - tslib@2.6.3: {} - tslib@2.8.1: {} - tuf-js@2.2.1: + tuf-js@3.1.0: dependencies: - '@tufjs/models': 2.0.1 + '@tufjs/models': 3.0.1 debug: 4.4.3 - make-fetch-happen: 13.0.1 + make-fetch-happen: 14.0.3 transitivePeerDependencies: - supports-color - type-fest@0.21.3: {} - type-is@1.6.18: dependencies: media-typer: 0.3.0 @@ -7980,9 +8923,11 @@ snapshots: typed-assert@1.0.9: {} - typescript@5.4.5: {} + typescript@5.7.3: {} - undici-types@6.21.0: {} + ua-parser-js@0.7.41: {} + + undici-types@7.16.0: {} unicode-canonical-property-names-ecmascript@2.0.1: {} @@ -7997,14 +8942,16 @@ snapshots: unicorn-magic@0.3.0: {} - unique-filename@3.0.0: + unique-filename@4.0.0: dependencies: - unique-slug: 4.0.0 + unique-slug: 5.0.0 - unique-slug@4.0.0: + unique-slug@5.0.0: dependencies: imurmurhash: 0.1.4 + universalify@0.1.2: {} + unpipe@1.0.0: {} update-browserslist-db@1.1.4(browserslist@4.28.0): @@ -8013,10 +8960,6 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 - util-deprecate@1.0.2: {} utils-merge@1.0.1: {} @@ -8028,23 +8971,34 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - validate-npm-package-name@5.0.1: {} + validate-npm-package-name@6.0.2: {} vary@1.1.2: {} - vite@5.4.21(@types/node@20.19.25)(less@4.2.0)(sass@1.77.6)(terser@5.31.6): + vite@6.4.1(@types/node@24.10.1)(jiti@1.21.7)(less@4.2.2)(sass@1.85.0)(terser@5.39.0): dependencies: - esbuild: 0.21.5 + esbuild: 0.25.4 + fdir: 6.5.0(picomatch@4.0.2) + picomatch: 4.0.2 postcss: 8.5.6 - rollup: 4.22.4 + rollup: 4.53.3 + tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 20.19.25 + '@types/node': 24.10.1 fsevents: 2.3.3 - less: 4.2.0 - sass: 1.77.6 - terser: 5.31.6 + jiti: 1.21.7 + less: 4.2.2 + sass: 1.85.0 + terser: 5.39.0 - watchpack@2.4.1: + void-elements@2.0.1: {} + + watchpack@2.4.2: + dependencies: + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + + watchpack@2.4.4: dependencies: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 @@ -8057,9 +9011,10 @@ snapshots: dependencies: defaults: 1.0.4 - weak-lru-cache@1.2.2: {} + weak-lru-cache@1.2.2: + optional: true - webpack-dev-middleware@7.4.2(webpack@5.94.0): + webpack-dev-middleware@7.4.2(webpack@5.98.0): dependencies: colorette: 2.0.20 memfs: 4.51.0 @@ -8068,9 +9023,9 @@ snapshots: range-parser: 1.2.1 schema-utils: 4.3.3 optionalDependencies: - webpack: 5.94.0(esbuild@0.23.0) + webpack: 5.98.0(esbuild@0.25.4) - webpack-dev-server@5.2.2(webpack@5.94.0): + webpack-dev-server@5.2.2(webpack@5.98.0): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -8098,10 +9053,10 @@ snapshots: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 7.4.2(webpack@5.94.0) + webpack-dev-middleware: 7.4.2(webpack@5.98.0) ws: 8.18.3 optionalDependencies: - webpack: 5.94.0(esbuild@0.23.0) + webpack: 5.98.0(esbuild@0.25.4) transitivePeerDependencies: - bufferutil - debug @@ -8116,19 +9071,19 @@ snapshots: webpack-sources@3.3.3: {} - webpack-subresource-integrity@5.1.0(webpack@5.94.0(esbuild@0.23.0)): + webpack-subresource-integrity@5.1.0(webpack@5.98.0): dependencies: typed-assert: 1.0.9 - webpack: 5.94.0(esbuild@0.23.0) + webpack: 5.98.0(esbuild@0.25.4) - webpack@5.94.0(esbuild@0.23.0): + webpack@5.98.0(esbuild@0.25.4): dependencies: + '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 '@webassemblyjs/ast': 1.14.1 '@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.15.0 - acorn-import-attributes: 1.9.5(acorn@8.15.0) browserslist: 4.28.0 chrome-trace-event: 1.0.4 enhanced-resolve: 5.18.3 @@ -8141,10 +9096,10 @@ snapshots: loader-runner: 4.3.1 mime-types: 2.1.35 neo-async: 2.6.2 - schema-utils: 3.3.0 + schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.14(esbuild@0.23.0)(webpack@5.94.0) - watchpack: 2.4.1 + terser-webpack-plugin: 5.3.14(esbuild@0.25.4)(webpack@5.98.0) + watchpack: 2.4.4 webpack-sources: 3.3.3 transitivePeerDependencies: - '@swc/core' @@ -8159,11 +9114,15 @@ snapshots: websocket-extensions@0.1.4: {} + which@1.3.1: + dependencies: + isexe: 2.0.0 + which@2.0.2: dependencies: isexe: 2.0.0 - which@4.0.0: + which@5.0.0: dependencies: isexe: 3.1.1 @@ -8193,6 +9152,10 @@ snapshots: string-width: 7.2.0 strip-ansi: 7.1.2 + wrappy@1.0.2: {} + + ws@8.17.1: {} + ws@8.18.3: {} y18n@5.0.8: {} @@ -8201,8 +9164,22 @@ snapshots: yallist@4.0.0: {} + yallist@5.0.0: {} + + yargs-parser@20.2.9: {} + yargs-parser@21.1.1: {} + yargs@16.2.0: + dependencies: + cliui: 7.0.4 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + yargs@17.7.2: dependencies: cliui: 8.0.1 @@ -8217,4 +9194,4 @@ snapshots: yoctocolors-cjs@2.1.3: {} - zone.js@0.14.10: {} + zone.js@0.15.1: {} diff --git a/ui/postcss.config.js b/ui/postcss.config.js new file mode 100644 index 0000000..33ad091 --- /dev/null +++ b/ui/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/ui/proxy.conf.json b/ui/proxy.conf.json index 527d410..c62c3f8 100644 --- a/ui/proxy.conf.json +++ b/ui/proxy.conf.json @@ -2,6 +2,7 @@ "/api": { "target": "http://localhost:3000", "secure": false, + "ws": true, "changeOrigin": true } } diff --git a/ui/public/favicon.ico b/ui/public/favicon.ico new file mode 100644 index 0000000..57614f9 Binary files /dev/null and b/ui/public/favicon.ico differ diff --git a/ui/src/app/app.component.css b/ui/src/app/app.component.css new file mode 100644 index 0000000..e69de29 diff --git a/ui/src/app/app.component.html b/ui/src/app/app.component.html new file mode 100644 index 0000000..36093e1 --- /dev/null +++ b/ui/src/app/app.component.html @@ -0,0 +1,336 @@ + + + + + + + + + + + +
+
+
+ +

Hello, {{ title }}

+

Congratulations! Your app is running. 🎉

+
+ +
+
+ @for (item of [ + { title: 'Explore the Docs', link: 'https://angular.dev' }, + { title: 'Learn with Tutorials', link: 'https://angular.dev/tutorials' }, + { title: 'CLI Docs', link: 'https://angular.dev/tools/cli' }, + { title: 'Angular Language Service', link: 'https://angular.dev/tools/language-service' }, + { title: 'Angular DevTools', link: 'https://angular.dev/tools/devtools' }, + ]; track item.title) { + + {{ item.title }} + + + + + } +
+ +
+
+
+ + + + + + + + + + + diff --git a/ui/src/app/app.component.spec.ts b/ui/src/app/app.component.spec.ts new file mode 100644 index 0000000..7f740ce --- /dev/null +++ b/ui/src/app/app.component.spec.ts @@ -0,0 +1,29 @@ +import { TestBed } from '@angular/core/testing'; +import { AppComponent } from './app.component'; + +describe('AppComponent', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AppComponent], + }).compileComponents(); + }); + + it('should create the app', () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + + it(`should have the 'ui' title`, () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app.title).toEqual('ui'); + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('h1')?.textContent).toContain('Hello, ui'); + }); +}); diff --git a/ui/src/app/app.component.ts b/ui/src/app/app.component.ts index 35301a3..cd20dcf 100644 --- a/ui/src/app/app.component.ts +++ b/ui/src/app/app.component.ts @@ -1,23 +1,14 @@ -import { Component, OnInit, OnDestroy, inject } from '@angular/core'; +import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; -import { WebSocketService } from './core/services/websocket.service'; +import { ToasterComponent } from './ui/toast/toaster.component'; @Component({ selector: 'app-root', standalone: true, - imports: [RouterOutlet], - template: ``, + imports: [RouterOutlet, ToasterComponent], + template: ` + + + `, }) -export class AppComponent implements OnInit, OnDestroy { - private wsService = inject(WebSocketService); - - ngOnInit(): void { - // Connect to WebSocket when app starts - this.wsService.connect(); - } - - ngOnDestroy(): void { - // Disconnect when app is destroyed - this.wsService.disconnect(); - } -} +export class AppComponent {} diff --git a/ui/src/app/app.config.ts b/ui/src/app/app.config.ts new file mode 100644 index 0000000..7e553b6 --- /dev/null +++ b/ui/src/app/app.config.ts @@ -0,0 +1,13 @@ +import { ApplicationConfig, provideExperimentalZonelessChangeDetection } from '@angular/core'; +import { provideRouter } from '@angular/router'; +import { provideHttpClient, withInterceptors } from '@angular/common/http'; +import { routes } from './app.routes'; +import { authInterceptor } from './core/interceptors/auth.interceptor'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideExperimentalZonelessChangeDetection(), + provideRouter(routes), + provideHttpClient(withInterceptors([authInterceptor])), + ], +}; diff --git a/ui/src/app/app.routes.ts b/ui/src/app/app.routes.ts index 4942650..8671f82 100644 --- a/ui/src/app/app.routes.ts +++ b/ui/src/app/app.routes.ts @@ -9,9 +9,11 @@ export const routes: Routes = [ }, { path: '', - canActivate: [authGuard], loadComponent: () => - import('./shared/components/layout.component').then((m) => m.LayoutComponent), + import('./shared/components/layout/layout.component').then( + (m) => m.LayoutComponent + ), + canActivate: [authGuard], children: [ { path: '', @@ -21,28 +23,59 @@ export const routes: Routes = [ { path: 'dashboard', loadComponent: () => - import('./features/dashboard/dashboard.component').then((m) => m.DashboardComponent), + import('./features/dashboard/dashboard.component').then( + (m) => m.DashboardComponent + ), }, { path: 'services', - loadComponent: () => - import('./features/services/services-list.component').then( - (m) => m.ServicesListComponent - ), + children: [ + { + path: '', + loadComponent: () => + import('./features/services/services-list.component').then( + (m) => m.ServicesListComponent + ), + }, + { + path: 'create', + loadComponent: () => + import('./features/services/service-create.component').then( + (m) => m.ServiceCreateComponent + ), + }, + { + path: ':name', + loadComponent: () => + import('./features/services/service-detail.component').then( + (m) => m.ServiceDetailComponent + ), + }, + ], }, { - path: 'services/new', - loadComponent: () => - import('./features/services/service-create.component').then( - (m) => m.ServiceCreateComponent - ), + path: 'domains', + children: [ + { + path: '', + loadComponent: () => + import('./features/domains/domains.component').then( + (m) => m.DomainsComponent + ), + }, + { + path: ':domain', + loadComponent: () => + import('./features/domains/domain-detail.component').then( + (m) => m.DomainDetailComponent + ), + }, + ], }, { - path: 'services/:name', + path: 'dns', loadComponent: () => - import('./features/services/service-detail.component').then( - (m) => m.ServiceDetailComponent - ), + import('./features/dns/dns.component').then((m) => m.DnsComponent), }, { path: 'registries', @@ -51,28 +84,17 @@ export const routes: Routes = [ (m) => m.RegistriesComponent ), }, - { - path: 'dns', - loadComponent: () => - import('./features/dns/dns.component').then((m) => m.DnsComponent), - }, - { - path: 'domains', - loadComponent: () => - import('./features/domains/domains.component').then((m) => m.DomainsComponent), - }, - { - path: 'domains/:domain', - loadComponent: () => - import('./features/domains/domain-detail.component').then( - (m) => m.DomainDetailComponent - ), - }, { path: 'settings', loadComponent: () => - import('./features/settings/settings.component').then((m) => m.SettingsComponent), + import('./features/settings/settings.component').then( + (m) => m.SettingsComponent + ), }, ], }, + { + path: '**', + redirectTo: 'dashboard', + }, ]; diff --git a/ui/src/app/core/guards/auth.guard.ts b/ui/src/app/core/guards/auth.guard.ts index 3af3491..585f90a 100644 --- a/ui/src/app/core/guards/auth.guard.ts +++ b/ui/src/app/core/guards/auth.guard.ts @@ -3,10 +3,10 @@ import { Router, CanActivateFn } from '@angular/router'; import { AuthService } from '../services/auth.service'; export const authGuard: CanActivateFn = () => { - const authService = inject(AuthService); + const auth = inject(AuthService); const router = inject(Router); - if (authService.isAuthenticated()) { + if (auth.isAuthenticated()) { return true; } diff --git a/ui/src/app/core/interceptors/auth.interceptor.ts b/ui/src/app/core/interceptors/auth.interceptor.ts index ad72c3e..b491dbc 100644 --- a/ui/src/app/core/interceptors/auth.interceptor.ts +++ b/ui/src/app/core/interceptors/auth.interceptor.ts @@ -1,17 +1,26 @@ -import { HttpInterceptorFn } from '@angular/common/http'; import { inject } from '@angular/core'; +import { HttpInterceptorFn, HttpRequest, HttpHandlerFn } from '@angular/common/http'; import { AuthService } from '../services/auth.service'; -export const authInterceptor: HttpInterceptorFn = (req, next) => { - const authService = inject(AuthService); - const token = authService.getToken(); +export const authInterceptor: HttpInterceptorFn = ( + req: HttpRequest, + next: HttpHandlerFn +) => { + const auth = inject(AuthService); + const token = auth.getToken(); - if (token && !req.url.includes('/api/auth/login')) { - req = req.clone({ + // Skip auth header for login request + if (req.url.includes('/api/auth/login')) { + return next(req); + } + + if (token) { + const authReq = req.clone({ setHeaders: { Authorization: `Bearer ${token}`, }, }); + return next(authReq); } return next(req); diff --git a/ui/src/app/core/services/api.service.ts b/ui/src/app/core/services/api.service.ts index f74cb40..a8ccb23 100644 --- a/ui/src/app/core/services/api.service.ts +++ b/ui/src/app/core/services/api.service.ts @@ -1,186 +1,141 @@ import { Injectable, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import { Observable } from 'rxjs'; +import { firstValueFrom } from 'rxjs'; +import { + IApiResponse, + IService, + IServiceCreate, + IServiceUpdate, + ISystemStatus, + IDomain, + IDomainDetail, + IDnsRecord, + IRegistry, + IRegistryCreate, + ISetting, + ISettings, +} from '../types/api.types'; -export interface ApiResponse { - success: boolean; - data?: T; - error?: string; - message?: string; -} - -export interface Service { - id: number; - name: string; - image: string; - registry?: string; - envVars: Record; - port: number; - domain?: string; - containerID?: string; - status: 'stopped' | 'starting' | 'running' | 'stopping' | 'failed'; - createdAt: number; - updatedAt: number; - // Onebox Registry fields - useOneboxRegistry?: boolean; - registryRepository?: string; - registryToken?: string; - registryImageTag?: string; - autoUpdateOnPush?: boolean; - imageDigest?: string; -} - -export interface Registry { - id: number; - url: string; - username: string; - createdAt: number; -} - -export interface SystemStatus { - docker: { - running: boolean; - version: any; - }; - reverseProxy: { - http: { - running: boolean; - port: number; - }; - https: { - running: boolean; - port: number; - certificates: number; - }; - routes: number; - }; - dns: { - configured: boolean; - }; - ssl: { - configured: boolean; - certbotInstalled: boolean; - }; - services: { - total: number; - running: number; - stopped: number; - }; -} - -@Injectable({ - providedIn: 'root', -}) +@Injectable({ providedIn: 'root' }) export class ApiService { private http = inject(HttpClient); - private baseUrl = '/api'; - // System - getStatus(): Observable> { - return this.http.get>(`${this.baseUrl}/status`); + // System Status + async getStatus(): Promise> { + return firstValueFrom(this.http.get>('/api/status')); } // Services - getServices(): Observable> { - return this.http.get>(`${this.baseUrl}/services`); + async getServices(): Promise> { + return firstValueFrom(this.http.get>('/api/services')); } - getService(name: string): Observable> { - return this.http.get>(`${this.baseUrl}/services/${name}`); + async getService(name: string): Promise> { + return firstValueFrom(this.http.get>(`/api/services/${name}`)); } - createService(data: any): Observable> { - return this.http.post>(`${this.baseUrl}/services`, data); + async createService(data: IServiceCreate): Promise> { + return firstValueFrom(this.http.post>('/api/services', data)); } - deleteService(name: string): Observable { - return this.http.delete(`${this.baseUrl}/services/${name}`); + async updateService(name: string, data: IServiceUpdate): Promise> { + return firstValueFrom(this.http.put>(`/api/services/${name}`, data)); } - startService(name: string): Observable { - return this.http.post(`${this.baseUrl}/services/${name}/start`, {}); + async deleteService(name: string): Promise> { + return firstValueFrom(this.http.delete>(`/api/services/${name}`)); } - stopService(name: string): Observable { - return this.http.post(`${this.baseUrl}/services/${name}/stop`, {}); + async startService(name: string): Promise> { + return firstValueFrom(this.http.post>(`/api/services/${name}/start`, {})); } - restartService(name: string): Observable { - return this.http.post(`${this.baseUrl}/services/${name}/restart`, {}); + async stopService(name: string): Promise> { + return firstValueFrom(this.http.post>(`/api/services/${name}/stop`, {})); } - getServiceLogs(name: string): Observable> { - return this.http.get>(`${this.baseUrl}/services/${name}/logs`); + async restartService(name: string): Promise> { + return firstValueFrom(this.http.post>(`/api/services/${name}/restart`, {})); } - updateService(name: string, updates: { - image?: string; - registry?: string; - port?: number; - domain?: string; - envVars?: Record; - }): Observable> { - return this.http.put>(`${this.baseUrl}/services/${name}`, updates); + async getServiceLogs(name: string): Promise> { + return firstValueFrom(this.http.get>(`/api/services/${name}/logs`)); } // Registries - getRegistries(): Observable> { - return this.http.get>(`${this.baseUrl}/registries`); + async getRegistries(): Promise> { + return firstValueFrom(this.http.get>('/api/registries')); } - createRegistry(data: any): Observable> { - return this.http.post>(`${this.baseUrl}/registries`, data); + async createRegistry(data: IRegistryCreate): Promise> { + return firstValueFrom(this.http.post>('/api/registries', data)); } - deleteRegistry(url: string): Observable { - return this.http.delete(`${this.baseUrl}/registries/${encodeURIComponent(url)}`); + async deleteRegistry(id: number): Promise> { + return firstValueFrom(this.http.delete>(`/api/registries/${id}`)); } - // DNS - getDnsRecords(): Observable> { - return this.http.get>(`${this.baseUrl}/dns`); + // DNS Records + async getDnsRecords(): Promise> { + return firstValueFrom(this.http.get>('/api/dns')); } - createDnsRecord(data: any): Observable { - return this.http.post(`${this.baseUrl}/dns`, data); + async createDnsRecord(domain: string, ip?: string): Promise> { + return firstValueFrom(this.http.post>('/api/dns', { domain, ip })); } - deleteDnsRecord(domain: string): Observable { - return this.http.delete(`${this.baseUrl}/dns/${domain}`); + async deleteDnsRecord(domain: string): Promise> { + return firstValueFrom(this.http.delete>(`/api/dns/${domain}`)); } - syncDnsRecords(): Observable { - return this.http.post(`${this.baseUrl}/dns/sync`, {}); - } - - // SSL - getSslCertificates(): Observable> { - return this.http.get>(`${this.baseUrl}/ssl`); - } - - renewSslCertificate(domain: string): Observable { - return this.http.post(`${this.baseUrl}/ssl/${domain}/renew`, {}); + async syncDnsRecords(): Promise> { + return firstValueFrom(this.http.post>('/api/dns/sync', {})); } // Domains - getDomains(): Observable> { - return this.http.get>(`${this.baseUrl}/domains`); + async getDomains(): Promise> { + return firstValueFrom(this.http.get>('/api/domains')); } - getDomainDetail(domain: string): Observable> { - return this.http.get>(`${this.baseUrl}/domains/${domain}`); + async getDomainDetail(domain: string): Promise> { + return firstValueFrom(this.http.get>(`/api/domains/${domain}`)); } - syncCloudflareDomains(): Observable { - return this.http.post(`${this.baseUrl}/domains/sync`, {}); + async syncCloudflareDomains(): Promise> { + return firstValueFrom(this.http.post>('/api/domains/sync', {})); + } + + // SSL Certificates + async obtainCertificate(domain: string, includeWildcard?: boolean): Promise> { + return firstValueFrom( + this.http.post>('/api/ssl/obtain', { domain, includeWildcard }) + ); + } + + async renewCertificate(domain: string): Promise> { + return firstValueFrom(this.http.post>(`/api/ssl/${domain}/renew`, {})); } // Settings - getSettings(): Observable>> { - return this.http.get>>(`${this.baseUrl}/settings`); + async getSettings(): Promise> { + return firstValueFrom(this.http.get>('/api/settings')); } - updateSetting(key: string, value: string): Observable { - return this.http.post(`${this.baseUrl}/settings`, { key, value }); + async updateSettings(settings: Record | ISettings): Promise> { + return firstValueFrom(this.http.put>('/api/settings', settings)); + } + + async updateSetting(key: string, value: string): Promise> { + return firstValueFrom(this.http.put>('/api/settings', { key, value })); + } + + // Auth + async changePassword(currentPassword: string, newPassword: string): Promise> { + return firstValueFrom( + this.http.post>('/api/auth/change-password', { + currentPassword, + newPassword, + }) + ); } } diff --git a/ui/src/app/core/services/auth.service.ts b/ui/src/app/core/services/auth.service.ts index 52ffff7..222887d 100644 --- a/ui/src/app/core/services/auth.service.ts +++ b/ui/src/app/core/services/auth.service.ts @@ -1,69 +1,54 @@ -import { Injectable, inject, signal } from '@angular/core'; +import { Injectable, inject, signal, computed } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Router } from '@angular/router'; -import { Observable, tap } from 'rxjs'; +import { firstValueFrom } from 'rxjs'; +import { IApiResponse, ILoginResponse, IUser } from '../types/api.types'; -export interface LoginRequest { - username: string; - password: string; -} - -export interface LoginResponse { - success: boolean; - data?: { - token: string; - user: { - username: string; - role: string; - }; - }; - error?: string; -} - -@Injectable({ - providedIn: 'root', -}) +@Injectable({ providedIn: 'root' }) export class AuthService { private http = inject(HttpClient); private router = inject(Router); - isAuthenticated = signal(false); - currentUser = signal<{ username: string; role: string } | null>(null); + private token = signal(this.loadToken()); + currentUser = signal(null); + isAuthenticated = computed(() => !!this.token()); - constructor() { - // Check if already authenticated - const token = this.getToken(); - if (token) { - this.isAuthenticated.set(true); - // TODO: Decode JWT to get user info - this.currentUser.set({ username: 'admin', role: 'admin' }); + private loadToken(): string | null { + if (typeof localStorage === 'undefined') return null; + return localStorage.getItem('onebox_token'); + } + + async login(username: string, password: string): Promise<{ success: boolean; error?: string }> { + try { + const response = await firstValueFrom( + this.http.post>('/api/auth/login', { username, password }) + ); + + if (response?.success && response.data) { + this.token.set(response.data.token); + this.currentUser.set(response.data.user); + if (typeof localStorage !== 'undefined') { + localStorage.setItem('onebox_token', response.data.token); + } + return { success: true }; + } + return { success: false, error: response?.error || 'Login failed' }; + } catch (err: any) { + const errorMessage = err?.error?.error || err?.message || 'Login failed'; + return { success: false, error: errorMessage }; } } - login(credentials: LoginRequest): Observable { - return this.http.post('/api/auth/login', credentials).pipe( - tap((response) => { - if (response.success && response.data) { - this.setToken(response.data.token); - this.currentUser.set(response.data.user); - this.isAuthenticated.set(true); - } - }) - ); - } - logout(): void { - localStorage.removeItem('onebox_token'); - this.isAuthenticated.set(false); + this.token.set(null); this.currentUser.set(null); + if (typeof localStorage !== 'undefined') { + localStorage.removeItem('onebox_token'); + } this.router.navigate(['/login']); } getToken(): string | null { - return localStorage.getItem('onebox_token'); - } - - private setToken(token: string): void { - localStorage.setItem('onebox_token', token); + return this.token(); } } diff --git a/ui/src/app/core/services/log-stream.service.ts b/ui/src/app/core/services/log-stream.service.ts new file mode 100644 index 0000000..e31153f --- /dev/null +++ b/ui/src/app/core/services/log-stream.service.ts @@ -0,0 +1,140 @@ +import { Injectable, signal } from '@angular/core'; + +export interface ILogStreamState { + connected: boolean; + error: string | null; + serviceName: string | null; +} + +@Injectable({ providedIn: 'root' }) +export class LogStreamService { + private ws: WebSocket | null = null; + private currentService: string | null = null; + + // Signals for reactive state + state = signal({ + connected: false, + error: null, + serviceName: null, + }); + + logs = signal([]); + isStreaming = signal(false); + + /** + * Connect to log stream for a service + */ + connect(serviceName: string): void { + // Disconnect any existing stream + this.disconnect(); + + this.currentService = serviceName; + this.isStreaming.set(true); + this.logs.set([]); + this.state.set({ + connected: false, + error: null, + serviceName, + }); + + if (typeof window === 'undefined') return; + + const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; + const host = window.location.host; + const url = `${protocol}//${host}/api/services/${serviceName}/logs/stream`; + + try { + this.ws = new WebSocket(url); + + this.ws.onopen = () => { + // Connection established, waiting for 'connected' message from server + }; + + this.ws.onmessage = (event) => { + const data = event.data; + + // Try to parse as JSON (for control messages) + try { + const json = JSON.parse(data); + + if (json.type === 'connected') { + this.state.set({ + connected: true, + error: null, + serviceName: json.serviceName, + }); + return; + } + + if (json.error) { + this.state.update((s) => ({ ...s, error: json.error })); + return; + } + } catch { + // Not JSON - it's a log line + this.logs.update((lines) => { + const newLines = [...lines, data]; + // Keep last 1000 lines to prevent memory issues + if (newLines.length > 1000) { + return newLines.slice(-1000); + } + return newLines; + }); + } + }; + + this.ws.onclose = () => { + this.state.update((s) => ({ ...s, connected: false })); + this.isStreaming.set(false); + this.ws = null; + }; + + this.ws.onerror = () => { + this.state.update((s) => ({ + ...s, + connected: false, + error: 'WebSocket connection failed', + })); + this.isStreaming.set(false); + }; + } catch (error) { + this.state.set({ + connected: false, + error: 'Failed to connect to log stream', + serviceName, + }); + this.isStreaming.set(false); + } + } + + /** + * Disconnect from log stream + */ + disconnect(): void { + if (this.ws) { + this.ws.close(); + this.ws = null; + } + this.currentService = null; + this.isStreaming.set(false); + this.state.set({ + connected: false, + error: null, + serviceName: null, + }); + } + + /** + * Clear logs buffer + */ + clearLogs(): void { + this.logs.set([]); + } + + /** + * Get current service name being streamed + */ + getCurrentService(): string | null { + return this.currentService; + } +} diff --git a/ui/src/app/core/services/theme.service.ts b/ui/src/app/core/services/theme.service.ts new file mode 100644 index 0000000..7507626 --- /dev/null +++ b/ui/src/app/core/services/theme.service.ts @@ -0,0 +1,64 @@ +import { Injectable, signal, effect } from '@angular/core'; + +export type Theme = 'light' | 'dark' | 'system'; + +@Injectable({ providedIn: 'root' }) +export class ThemeService { + theme = signal(this.loadTheme()); + + constructor() { + effect(() => { + this.applyTheme(this.theme()); + }); + + // Listen for system preference changes + if (typeof window !== 'undefined') { + window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { + if (this.theme() === 'system') { + this.applyTheme('system'); + } + }); + } + } + + private loadTheme(): Theme { + if (typeof localStorage === 'undefined') return 'system'; + const stored = localStorage.getItem('onebox-theme'); + if (stored === 'light' || stored === 'dark' || stored === 'system') { + return stored; + } + return 'system'; + } + + setTheme(theme: Theme): void { + this.theme.set(theme); + if (typeof localStorage !== 'undefined') { + localStorage.setItem('onebox-theme', theme); + } + } + + toggle(): void { + const resolved = this.resolvedTheme(); + this.setTheme(resolved === 'dark' ? 'light' : 'dark'); + } + + isDark(): boolean { + return this.resolvedTheme() === 'dark'; + } + + resolvedTheme(): 'light' | 'dark' { + if (this.theme() === 'system') { + if (typeof window === 'undefined') return 'light'; + return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; + } + return this.theme() as 'light' | 'dark'; + } + + private applyTheme(theme: Theme): void { + if (typeof document === 'undefined') return; + const resolved = theme === 'system' + ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light') + : theme; + document.documentElement.classList.toggle('dark', resolved === 'dark'); + } +} diff --git a/ui/src/app/core/services/toast.service.ts b/ui/src/app/core/services/toast.service.ts index 62bf0e5..db6a13c 100644 --- a/ui/src/app/core/services/toast.service.ts +++ b/ui/src/app/core/services/toast.service.ts @@ -1,53 +1,48 @@ import { Injectable, signal } from '@angular/core'; +import { IToast, ToastType } from '../types/api.types'; -export type ToastType = 'success' | 'error' | 'info' | 'warning'; - -export interface Toast { - id: string; - type: ToastType; - message: string; - duration?: number; -} - -@Injectable({ - providedIn: 'root' -}) +@Injectable({ providedIn: 'root' }) export class ToastService { - toasts = signal([]); - private nextId = 0; + toasts = signal([]); - show(type: ToastType, message: string, duration: number = 5000) { - const id = `toast-${this.nextId++}`; - const toast: Toast = { id, type, message, duration }; + private generateId(): string { + return Math.random().toString(36).substring(2, 9); + } + + show(type: ToastType, message: string, duration = 5000): string { + const id = this.generateId(); + const toast: IToast = { id, type, message, duration }; this.toasts.update(toasts => [...toasts, toast]); if (duration > 0) { - setTimeout(() => this.remove(id), duration); + setTimeout(() => this.dismiss(id), duration); } + + return id; } - success(message: string, duration?: number) { - this.show('success', message, duration); + success(message: string, duration?: number): string { + return this.show('success', message, duration); } - error(message: string, duration?: number) { - this.show('error', message, duration); + error(message: string, duration?: number): string { + return this.show('error', message, duration); } - info(message: string, duration?: number) { - this.show('info', message, duration); + info(message: string, duration?: number): string { + return this.show('info', message, duration); } - warning(message: string, duration?: number) { - this.show('warning', message, duration); + warning(message: string, duration?: number): string { + return this.show('warning', message, duration); } - remove(id: string) { + dismiss(id: string): void { this.toasts.update(toasts => toasts.filter(t => t.id !== id)); } - clear() { + dismissAll(): void { this.toasts.set([]); } } diff --git a/ui/src/app/core/services/websocket.service.ts b/ui/src/app/core/services/websocket.service.ts index 0c3e045..39ab4eb 100644 --- a/ui/src/app/core/services/websocket.service.ts +++ b/ui/src/app/core/services/websocket.service.ts @@ -1,101 +1,109 @@ -import { Injectable } from '@angular/core'; -import { Subject, Observable } from 'rxjs'; +import { Injectable, signal, computed, effect, inject } from '@angular/core'; +import { IWebSocketMessage } from '../types/api.types'; +import { AuthService } from './auth.service'; -export interface WebSocketMessage { - type: string; - action?: string; - serviceName?: string; - data?: any; - status?: string; - timestamp: number; - message?: string; -} - -@Injectable({ - providedIn: 'root' -}) +@Injectable({ providedIn: 'root' }) export class WebSocketService { + private auth = inject(AuthService); private ws: WebSocket | null = null; - private messageSubject = new Subject(); private reconnectAttempts = 0; private maxReconnectAttempts = 5; - private reconnectDelay = 3000; - private reconnectTimer: any = null; + private reconnectDelay = 1000; - constructor() {} + isConnected = signal(false); + lastMessage = signal(null); + + // Computed signals for specific message types + serviceUpdates = computed(() => { + const msg = this.lastMessage(); + return msg?.type === 'service_update' ? msg : null; + }); + + serviceStatus = computed(() => { + const msg = this.lastMessage(); + return msg?.type === 'service_status' ? msg : null; + }); + + systemStatus = computed(() => { + const msg = this.lastMessage(); + return msg?.type === 'system_status' ? msg : null; + }); + + constructor() { + // Auto-connect when authenticated + effect(() => { + if (this.auth.isAuthenticated()) { + this.connect(); + } else { + this.disconnect(); + } + }); + } connect(): void { - if (this.ws?.readyState === WebSocket.OPEN) { - console.log('WebSocket already connected'); - return; - } + if (this.ws?.readyState === WebSocket.OPEN) return; + if (typeof window === 'undefined') return; const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; - const wsUrl = `${protocol}//${window.location.host}/api/ws`; + const host = window.location.host; + const url = `${protocol}//${host}/api/ws`; - console.log('Connecting to WebSocket:', wsUrl); + try { + this.ws = new WebSocket(url); - this.ws = new WebSocket(wsUrl); + this.ws.onopen = () => { + this.isConnected.set(true); + this.reconnectAttempts = 0; + this.reconnectDelay = 1000; + }; - this.ws.onopen = () => { - console.log('✓ WebSocket connected'); - this.reconnectAttempts = 0; - }; + this.ws.onmessage = (event) => { + try { + const message: IWebSocketMessage = JSON.parse(event.data); + this.lastMessage.set(message); + } catch { + console.error('Failed to parse WebSocket message'); + } + }; - this.ws.onmessage = (event) => { - try { - const message: WebSocketMessage = JSON.parse(event.data); - console.log('📨 WebSocket message:', message); - this.messageSubject.next(message); - } catch (error) { - console.error('Failed to parse WebSocket message:', error); - } - }; + this.ws.onclose = () => { + this.isConnected.set(false); + this.ws = null; + this.attemptReconnect(); + }; - this.ws.onerror = (error) => { - console.error('✖ WebSocket error:', error); - }; - - this.ws.onclose = () => { - console.log('⚠ WebSocket closed'); - this.ws = null; - this.attemptReconnect(); - }; + this.ws.onerror = () => { + this.isConnected.set(false); + }; + } catch { + this.isConnected.set(false); + } } private attemptReconnect(): void { - if (this.reconnectAttempts >= this.maxReconnectAttempts) { - console.error('Max WebSocket reconnect attempts reached'); - return; - } + if (!this.auth.isAuthenticated()) return; + if (this.reconnectAttempts >= this.maxReconnectAttempts) return; this.reconnectAttempts++; - const delay = this.reconnectDelay * this.reconnectAttempts; - - console.log(`Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts}/${this.maxReconnectAttempts})`); - - this.reconnectTimer = setTimeout(() => { + setTimeout(() => { this.connect(); - }, delay); + }, this.reconnectDelay); + + // Exponential backoff + this.reconnectDelay = Math.min(this.reconnectDelay * 2, 30000); } disconnect(): void { - if (this.reconnectTimer) { - clearTimeout(this.reconnectTimer); - this.reconnectTimer = null; - } - if (this.ws) { this.ws.close(); this.ws = null; } + this.isConnected.set(false); } - getMessages(): Observable { - return this.messageSubject.asObservable(); - } - - isConnected(): boolean { - return this.ws?.readyState === WebSocket.OPEN; + send(message: any): void { + if (this.ws?.readyState === WebSocket.OPEN) { + this.ws.send(JSON.stringify(message)); + } } } diff --git a/ui/src/app/core/types/api.types.ts b/ui/src/app/core/types/api.types.ts new file mode 100644 index 0000000..d8fcb97 --- /dev/null +++ b/ui/src/app/core/types/api.types.ts @@ -0,0 +1,175 @@ +export interface IApiResponse { + success: boolean; + data?: T; + error?: string; + message?: string; +} + +export interface IUser { + username: string; + role: 'admin' | 'user'; +} + +export interface ILoginResponse { + token: string; + user: IUser; +} + +export interface IService { + id?: number; + name: string; + image: string; + registry?: string; + envVars: Record; + port: number; + domain?: string; + containerID?: string; + status: 'stopped' | 'starting' | 'running' | 'stopping' | 'failed'; + createdAt: number; + updatedAt: number; + useOneboxRegistry?: boolean; + registryRepository?: string; + registryToken?: string; + registryImageTag?: string; + autoUpdateOnPush?: boolean; + imageDigest?: string; +} + +export interface IServiceCreate { + name: string; + image: string; + port: number; + domain?: string; + envVars?: Record; + useOneboxRegistry?: boolean; + registryImageTag?: string; + autoUpdateOnPush?: boolean; +} + +export interface IServiceUpdate { + image?: string; + registry?: string; + port?: number; + domain?: string; + envVars?: Record; +} + +export interface ISystemStatus { + docker: { + running: boolean; + version: any; + }; + reverseProxy: { + http: { running: boolean; port: number }; + https: { running: boolean; port: number; certificates: number }; + routes: number; + }; + dns: { configured: boolean }; + ssl: { configured: boolean; certbotInstalled: boolean }; + services: { total: number; running: number; stopped: number }; +} + +export interface IDomain { + id?: number; + domain: string; + dnsProvider: 'cloudflare' | 'manual' | null; + cloudflareZoneId?: string; + isObsolete: boolean; + defaultWildcard: boolean; + createdAt: number; + updatedAt: number; +} + +export interface ICertificate { + id?: number; + domainId: number; + certDomain: string; + isWildcard: boolean; + certPath: string; + keyPath: string; + fullChainPath: string; + expiryDate: number; + issuer: string; + isValid: boolean; + createdAt: number; + updatedAt: number; +} + +export interface ICertRequirement { + id?: number; + domainId: number; + serviceId: number; + subdomain: string; + status: 'pending' | 'active' | 'renewing' | 'failed'; + certificateId?: number; + createdAt: number; + updatedAt: number; +} + +export interface IDomainDetail { + domain: IDomain; + certificates: ICertificate[]; + requirements: ICertRequirement[]; + serviceCount: number; + certificateStatus: 'valid' | 'expiring-soon' | 'expired' | 'pending' | 'none'; + daysRemaining: number | null; +} + +export interface IDnsRecord { + id?: number; + domain: string; + type: 'A' | 'AAAA' | 'CNAME'; + value: string; + cloudflareID?: string; + createdAt: number; + updatedAt: number; +} + +export interface IRegistry { + id?: number; + url: string; + username: string; + createdAt: number; +} + +export interface IRegistryCreate { + url: string; + username: string; + password: string; +} + +export interface ISetting { + key: string; + value: string; + updatedAt: number; +} + +export interface ISettings { + cloudflareToken: string; + cloudflareZoneId: string; + autoRenewCerts: boolean; + renewalThreshold: number; + acmeEmail: string; + httpPort: number; + httpsPort: number; + forceHttps: boolean; +} + +export interface IWebSocketMessage { + type: 'connected' | 'service_update' | 'service_status' | 'system_status'; + action?: 'created' | 'updated' | 'deleted' | 'started' | 'stopped'; + serviceName?: string; + status?: string; + data?: any; + message?: string; + timestamp: number; +} + +export type ToastType = 'success' | 'error' | 'info' | 'warning'; + +export interface IToast { + id: string; + type: ToastType; + message: string; + duration?: number; +} diff --git a/ui/src/app/features/dashboard/dashboard.component.ts b/ui/src/app/features/dashboard/dashboard.component.ts index 656ef51..05f6f1a 100644 --- a/ui/src/app/features/dashboard/dashboard.component.ts +++ b/ui/src/app/features/dashboard/dashboard.component.ts @@ -1,242 +1,262 @@ -import { Component, OnInit, OnDestroy, inject, signal } from '@angular/core'; -import { CommonModule } from '@angular/common'; +import { Component, inject, signal, effect, OnInit, OnDestroy } from '@angular/core'; import { RouterLink } from '@angular/router'; -import { ApiService, SystemStatus } from '../../core/services/api.service'; +import { ApiService } from '../../core/services/api.service'; import { WebSocketService } from '../../core/services/websocket.service'; -import { Subscription } from 'rxjs'; +import { ToastService } from '../../core/services/toast.service'; +import { ISystemStatus } from '../../core/types/api.types'; +import { + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, +} from '../../ui/card/card.component'; +import { ButtonComponent } from '../../ui/button/button.component'; +import { BadgeComponent } from '../../ui/badge/badge.component'; +import { SkeletonComponent } from '../../ui/skeleton/skeleton.component'; @Component({ selector: 'app-dashboard', standalone: true, - imports: [CommonModule, RouterLink], + imports: [ + RouterLink, + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, + ButtonComponent, + BadgeComponent, + SkeletonComponent, + ], template: ` -
-
-

Dashboard

-
- @if (lastUpdated()) { - - Last updated: {{ lastUpdated()!.toLocaleTimeString() }} - - } - +
+ +
+
+

Dashboard

+

System overview and quick actions

+
- @if (loading()) { -
-
+ @if (loading() && !status()) { + +
+ @for (_ of [1,2,3,4]; track $index) { + + + + + + + + + }
} @else if (status()) { -
- -
-
-
- - - -
-
-
-
Total Services
-
{{ status()!.services.total }}
-
-
-
-
+
+ + + Total Services + + + + + +
{{ status()!.services.total }}
+
+
- -
-
-
- - - -
-
-
-
Running
-
{{ status()!.services.running }}
-
-
-
-
+ + + Running + + + + + +
{{ status()!.services.running }}
+
+
- -
-
-
- - - -
-
-
-
Stopped
-
{{ status()!.services.stopped }}
-
-
-
-
+ + + Stopped + + + + + + +
{{ status()!.services.stopped }}
+
+
- -
-
-
- - - -
-
-
-
Docker
-
- {{ status()!.docker.running ? 'Running' : 'Stopped' }} -
-
-
-
-
+ + + Docker + + + + + + + {{ status()!.docker.running ? 'Running' : 'Stopped' }} + + +
-
- -
-

Docker

-
-
- Status - - {{ status()!.docker.running ? 'Running' : 'Stopped' }} - -
- @if (status()!.docker.version) { -
- Version - {{ status()!.docker.version.Version }} -
- } -
-
- +
-
-

Reverse Proxy

-
-
- HTTP (Port {{ status()!.reverseProxy.http.port }}) - - {{ status()!.reverseProxy.http.running ? 'Running' : 'Stopped' }} - + + + Reverse Proxy + HTTP/HTTPS proxy status + + +
+ HTTP ({{ status()!.reverseProxy.http.port }}) + + {{ status()!.reverseProxy.http.running ? 'Active' : 'Inactive' }} +
-
- HTTPS (Port {{ status()!.reverseProxy.https.port }}) - - {{ status()!.reverseProxy.https.running ? 'Running' : 'Stopped' }} - +
+ HTTPS ({{ status()!.reverseProxy.https.port }}) + + {{ status()!.reverseProxy.https.running ? 'Active' : 'Inactive' }} +
-
- SSL Certificates - {{ status()!.reverseProxy.https.certificates }} +
+ Certificates + {{ status()!.reverseProxy.https.certificates }}
-
-
+
+
- -
-

DNS & SSL

-
-
- DNS Configured - - {{ status()!.dns.configured ? 'Yes' : 'No' }} - + + + + DNS + DNS configuration status + + +
+ Cloudflare + + {{ status()!.dns.configured ? 'Configured' : 'Not configured' }} +
-
- SSL Configured - - {{ status()!.ssl.configured ? 'Yes' : 'No' }} - + + + + + + + SSL/TLS + Certificate management + + +
+ ACME + + {{ status()!.ssl.configured ? 'Configured' : 'Not configured' }} +
-
-
+
+ Certbot + + {{ status()!.ssl.certbotInstalled ? 'Installed' : 'Not installed' }} + +
+ +
- + + + + + }
`, }) export class DashboardComponent implements OnInit, OnDestroy { - private apiService = inject(ApiService); - private wsService = inject(WebSocketService); + private api = inject(ApiService); + private ws = inject(WebSocketService); + private toast = inject(ToastService); - status = signal(null); - loading = signal(true); - lastUpdated = signal(null); - private wsSubscription?: Subscription; - private refreshInterval?: number; + status = signal(null); + loading = signal(false); - ngOnInit(): void { - this.loadStatus(); + private refreshInterval: any; - // Subscribe to WebSocket updates - this.wsSubscription = this.wsService.getMessages().subscribe((message: any) => { - // Reload status on any service or system update - if (message.type === 'service_update' || message.type === 'service_status' || message.type === 'system_status') { + constructor() { + // React to WebSocket updates + effect(() => { + const update = this.ws.serviceUpdates(); + const systemStatus = this.ws.systemStatus(); + if (update || systemStatus) { this.loadStatus(); } }); + } + ngOnInit(): void { + this.loadStatus(); // Auto-refresh every 30 seconds - this.refreshInterval = window.setInterval(() => { - this.loadStatus(); - }, 30000); + this.refreshInterval = setInterval(() => this.loadStatus(), 30000); } ngOnDestroy(): void { - this.wsSubscription?.unsubscribe(); if (this.refreshInterval) { clearInterval(this.refreshInterval); } } - loadStatus(): void { + async loadStatus(): Promise { this.loading.set(true); - this.apiService.getStatus().subscribe({ - next: (response) => { - if (response.success && response.data) { - this.status.set(response.data); - this.lastUpdated.set(new Date()); - } - this.loading.set(false); - }, - error: () => { - this.loading.set(false); - }, - }); - } - - refresh(): void { - this.loadStatus(); + try { + const response = await this.api.getStatus(); + if (response.success && response.data) { + this.status.set(response.data); + } else { + this.toast.error(response.error || 'Failed to load status'); + } + } catch (err) { + this.toast.error('Failed to load status'); + } finally { + this.loading.set(false); + } } } diff --git a/ui/src/app/features/dns/dns.component.ts b/ui/src/app/features/dns/dns.component.ts index f201238..477e87c 100644 --- a/ui/src/app/features/dns/dns.component.ts +++ b/ui/src/app/features/dns/dns.component.ts @@ -1,105 +1,207 @@ -import { Component, OnInit, inject, signal } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; +import { Component, inject, signal, OnInit } from '@angular/core'; import { ApiService } from '../../core/services/api.service'; import { ToastService } from '../../core/services/toast.service'; +import { IDnsRecord } from '../../core/types/api.types'; +import { + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, +} from '../../ui/card/card.component'; +import { ButtonComponent } from '../../ui/button/button.component'; +import { BadgeComponent } from '../../ui/badge/badge.component'; +import { + TableComponent, + TableHeaderComponent, + TableBodyComponent, + TableRowComponent, + TableHeadComponent, + TableCellComponent, +} from '../../ui/table/table.component'; +import { SkeletonComponent } from '../../ui/skeleton/skeleton.component'; +import { + DialogComponent, + DialogHeaderComponent, + DialogTitleComponent, + DialogDescriptionComponent, + DialogFooterComponent, +} from '../../ui/dialog/dialog.component'; @Component({ selector: 'app-dns', standalone: true, - imports: [CommonModule, FormsModule], + imports: [ + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, + ButtonComponent, + BadgeComponent, + TableComponent, + TableHeaderComponent, + TableBodyComponent, + TableRowComponent, + TableHeadComponent, + TableCellComponent, + SkeletonComponent, + DialogComponent, + DialogHeaderComponent, + DialogTitleComponent, + DialogDescriptionComponent, + DialogFooterComponent, + ], template: ` -
-
-

DNS Records

-
- @if (records().length > 0) { -
- - - - - - - - - - - @for (record of records(); track record.domain) { - - - - - - + + + @if (loading() && records().length === 0) { +
+ @for (_ of [1,2,3]; track $index) { + } -
-
DomainTypeValueActions
{{ record.domain }}{{ record.type }}{{ record.value }} - -
-
- } @else { -
-

No DNS records configured

-

DNS records are created automatically when deploying services with domains

-

Or click "Sync Cloudflare" to import existing DNS records from Cloudflare

-
- } +
+ } @else if (records().length === 0) { +
+ + + +

No DNS records

+

DNS records are created automatically when you deploy services with domains.

+ +
+ } @else { + + + + Domain + Type + Value + Actions + + + + @for (record of records(); track record.id) { + + {{ record.domain }} + + {{ record.type }} + + {{ record.value }} + + + + + } + + + } + +
+ + + + Delete DNS Record + + Are you sure you want to delete the record for "{{ recordToDelete()?.domain }}"? + + + + + + + `, }) export class DnsComponent implements OnInit { - private apiService = inject(ApiService); - private toastService = inject(ToastService); - records = signal([]); + private api = inject(ApiService); + private toast = inject(ToastService); + + records = signal([]); + loading = signal(false); syncing = signal(false); + deleteDialogOpen = signal(false); + recordToDelete = signal(null); ngOnInit(): void { this.loadRecords(); } - loadRecords(): void { - this.apiService.getDnsRecords().subscribe({ - next: (response) => { - if (response.success && response.data) { - this.records.set(response.data); - } - }, - }); + async loadRecords(): Promise { + this.loading.set(true); + try { + const response = await this.api.getDnsRecords(); + if (response.success && response.data) { + this.records.set(response.data); + } + } catch { + this.toast.error('Failed to load DNS records'); + } finally { + this.loading.set(false); + } } - syncRecords(): void { + async syncRecords(): Promise { this.syncing.set(true); - this.apiService.syncDnsRecords().subscribe({ - next: (response) => { - if (response.success) { - this.toastService.success('Cloudflare DNS records synced successfully'); - this.loadRecords(); - } else { - this.toastService.error(response.error || 'Failed to sync DNS records'); - } - this.syncing.set(false); - }, - error: () => { - this.toastService.error('Failed to sync DNS records'); - this.syncing.set(false); - }, - }); + try { + const response = await this.api.syncDnsRecords(); + if (response.success) { + this.toast.success('DNS records synced'); + this.loadRecords(); + } else { + this.toast.error(response.error || 'Failed to sync DNS records'); + } + } catch { + this.toast.error('Failed to sync DNS records'); + } finally { + this.syncing.set(false); + } } - deleteRecord(record: any): void { - if (confirm(`Delete DNS record for ${record.domain}?`)) { - this.apiService.deleteDnsRecord(record.domain).subscribe({ - next: () => this.loadRecords(), - }); + confirmDelete(record: IDnsRecord): void { + this.recordToDelete.set(record); + this.deleteDialogOpen.set(true); + } + + async deleteRecord(): Promise { + const record = this.recordToDelete(); + if (!record) return; + + try { + const response = await this.api.deleteDnsRecord(record.domain); + if (response.success) { + this.toast.success('DNS record deleted'); + this.loadRecords(); + } else { + this.toast.error(response.error || 'Failed to delete record'); + } + } catch { + this.toast.error('Failed to delete record'); + } finally { + this.deleteDialogOpen.set(false); + this.recordToDelete.set(null); } } } diff --git a/ui/src/app/features/domains/domain-detail.component.ts b/ui/src/app/features/domains/domain-detail.component.ts index db698a6..6824234 100644 --- a/ui/src/app/features/domains/domain-detail.component.ts +++ b/ui/src/app/features/domains/domain-detail.component.ts @@ -1,356 +1,289 @@ -import { Component, OnInit, inject, signal } from '@angular/core'; -import { CommonModule } from '@angular/common'; +import { Component, inject, signal, OnInit } from '@angular/core'; import { ActivatedRoute, RouterLink } from '@angular/router'; import { ApiService } from '../../core/services/api.service'; - -interface DomainDetail { - domain: { - id: number; - domain: string; - dnsProvider: 'cloudflare' | 'manual' | null; - cloudflareZoneId?: string; - isObsolete: boolean; - defaultWildcard: boolean; - createdAt: number; - updatedAt: number; - }; - certificates: Array<{ - id: number; - certDomain: string; - isWildcard: boolean; - expiryDate: number; - issuer: string; - isValid: boolean; - createdAt: number; - }>; - requirements: Array<{ - id: number; - serviceId: number; - subdomain: string; - status: 'pending' | 'active' | 'renewing'; - certificateId?: number; - }>; - services: Array<{ - id: number; - name: string; - domain: string; - status: string; - }>; -} +import { ToastService } from '../../core/services/toast.service'; +import { IDomainDetail, IService } from '../../core/types/api.types'; +import { + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, +} from '../../ui/card/card.component'; +import { ButtonComponent } from '../../ui/button/button.component'; +import { BadgeComponent } from '../../ui/badge/badge.component'; +import { + TableComponent, + TableHeaderComponent, + TableBodyComponent, + TableRowComponent, + TableHeadComponent, + TableCellComponent, +} from '../../ui/table/table.component'; +import { SkeletonComponent } from '../../ui/skeleton/skeleton.component'; @Component({ selector: 'app-domain-detail', standalone: true, - imports: [CommonModule, RouterLink], + imports: [ + RouterLink, + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, + ButtonComponent, + BadgeComponent, + TableComponent, + TableHeaderComponent, + TableBodyComponent, + TableRowComponent, + TableHeadComponent, + TableCellComponent, + SkeletonComponent, + ], template: ` -
- -
- +
+
+ + + + + Back to Domains + - @if (loading()) { -
-

Loading domain details...

-
- } @else if (domainDetail()) { -
-
-
-

- {{ domainDetail()!.domain.domain }} -

-
- @if (domainDetail()!.domain.dnsProvider === 'cloudflare') { - - Cloudflare - - } @else if (domainDetail()!.domain.dnsProvider === 'manual') { - - Manual DNS - - } - @if (domainDetail()!.domain.defaultWildcard) { - - Wildcard Enabled - - } - @if (domainDetail()!.domain.isObsolete) { - - Obsolete - - } -
-
-
- - -
-
-
Certificates
-
- {{ domainDetail()!.certificates.length }} -
-
-
-
Requirements
-
- {{ domainDetail()!.requirements.length }} -
-
-
-
Services
-
- {{ domainDetail()!.services.length }} -
-
-
- - -
-

SSL Certificates

- @if (domainDetail()!.certificates.length > 0) { -
- - - - - - - - - - - - @for (cert of domainDetail()!.certificates; track cert.id) { - - - - - - - - } - -
DomainTypeStatusExpiresIssuer
- {{ cert.certDomain }} - - @if (cert.isWildcard) { - - Wildcard - - } @else { - - Standard - - } - - @if (getCertStatus(cert) === 'valid') { - - Valid - - } @else if (getCertStatus(cert) === 'expiring') { - - Expiring Soon - - } @else { - - Expired/Invalid - - } - - {{ formatDate(cert.expiryDate) }} - ({{ getDaysRemaining(cert.expiryDate) }} days) - - {{ cert.issuer }} -
-
- } @else { -
-

No certificates for this domain

-
- } -
- - -
-

Certificate Requirements

- @if (domainDetail()!.requirements.length > 0) { -
- - - - - - - - - - - @for (req of domainDetail()!.requirements; track req.id) { - - - - - - - } - -
ServiceSubdomainStatusCertificate ID
- {{ getServiceName(req.serviceId) }} - - {{ req.subdomain || '(root)' }} - - @if (req.status === 'active') { - - Active - - } @else if (req.status === 'pending') { - - Pending - - } @else if (req.status === 'renewing') { - - Renewing - - } - - {{ req.certificateId || '—' }} -
-
- } @else { -
-

No certificate requirements

-
- } -
- - -
-

Services Using This Domain

- @if (domainDetail()!.services.length > 0) { -
- - - - - - - - - - - @for (service of domainDetail()!.services; track service.id) { - - - - - - - } - -
NameDomainStatusActions
- {{ service.name }} - - {{ service.domain }} - - @if (service.status === 'running') { - - Running - - } @else if (service.status === 'stopped') { - - Stopped - - } @else { - - {{ service.status }} - - } - - - View Service - -
-
- } @else { -
-

No services using this domain

-
- } -
-
- } @else { -
-

Domain not found

+ @if (loading() && !domain()) { + + } @else if (domain()) { +
+

{{ domain()!.domain.domain }}

+ + {{ domain()!.domain.dnsProvider || 'Manual' }} + + @if (domain()!.domain.defaultWildcard) { + Wildcard + } + @if (domain()!.domain.isObsolete) { + Obsolete + }
}
+ + @if (domain()) { + +
+ + + Certificates + + + + + +
{{ domain()!.certificates.length }}
+
+
+ + + Requirements + + + + + +
{{ domain()!.requirements.length }}
+
+
+ + + Services + + + + + +
{{ domain()!.serviceCount }}
+
+
+
+ + + + + SSL Certificates + Active certificates for this domain + + + @if (domain()!.certificates.length === 0) { +
No certificates
+ } @else { + + + + Domain + Type + Status + Expires + Issuer + Actions + + + + @for (cert of domain()!.certificates; track cert.id) { + + {{ cert.certDomain }} + + {{ cert.isWildcard ? 'Wildcard' : 'Standard' }} + + + + {{ getCertStatus(cert) }} + + + + {{ formatDate(cert.expiryDate) }} + + ({{ getDaysRemaining(cert.expiryDate) }} days) + + + {{ cert.issuer }} + + + + + } + + + } +
+
+ + + + + Services + Services using this domain + + + @if (services().length === 0) { +
No services using this domain
+ } @else { + + + + Service + Domain + Status + Actions + + + + @for (svc of services(); track svc.name) { + + {{ svc.name }} + {{ svc.domain }} + + + {{ svc.status }} + + + + + + + + + } + + + } +
+
+ }
`, }) export class DomainDetailComponent implements OnInit { private route = inject(ActivatedRoute); - private apiService = inject(ApiService); + private api = inject(ApiService); + private toast = inject(ToastService); - domainDetail = signal(null); - loading = signal(true); + domain = signal(null); + services = signal([]); + loading = signal(false); ngOnInit(): void { - const domain = this.route.snapshot.paramMap.get('domain'); - if (domain) { - this.loadDomainDetail(domain); + const domainName = this.route.snapshot.paramMap.get('domain'); + if (domainName) { + this.loadDomain(domainName); + this.loadServices(domainName); } } - loadDomainDetail(domain: string): void { + async loadDomain(name: string): Promise { this.loading.set(true); - this.apiService.getDomainDetail(domain).subscribe({ - next: (response) => { - if (response.success && response.data) { - this.domainDetail.set(response.data); - } - this.loading.set(false); - }, - error: () => { - this.loading.set(false); - }, - }); + try { + const response = await this.api.getDomainDetail(name); + if (response.success && response.data) { + this.domain.set(response.data); + } + } catch { + this.toast.error('Failed to load domain'); + } finally { + this.loading.set(false); + } + } + + async loadServices(domainName: string): Promise { + try { + const response = await this.api.getServices(); + if (response.success && response.data) { + this.services.set(response.data.filter(s => s.domain?.includes(domainName))); + } + } catch { + // Silent fail + } } formatDate(timestamp: number): string { - return new Date(timestamp).toLocaleDateString('en-US', { - year: 'numeric', - month: 'short', - day: 'numeric', - }); + return new Date(timestamp).toLocaleDateString(); } - getDaysRemaining(expiryDate: number): number { + getDaysRemaining(timestamp: number): number { const now = Date.now(); - const diff = expiryDate - now; - return Math.floor(diff / (24 * 60 * 60 * 1000)); + return Math.floor((timestamp - now) / (1000 * 60 * 60 * 24)); } - getCertStatus(cert: any): 'valid' | 'expiring' | 'invalid' { - if (!cert.isValid) return 'invalid'; - const daysRemaining = this.getDaysRemaining(cert.expiryDate); - if (daysRemaining < 0) return 'invalid'; - if (daysRemaining <= 30) return 'expiring'; - return 'valid'; + getCertStatus(cert: any): string { + if (!cert.isValid) return 'Invalid'; + const days = this.getDaysRemaining(cert.expiryDate); + if (days < 0) return 'Expired'; + if (days <= 30) return 'Expiring'; + return 'Valid'; } - getServiceName(serviceId: number): string { - const service = this.domainDetail()?.services.find((s) => s.id === serviceId); - return service?.name || `Service #${serviceId}`; + getCertStatusVariant(cert: any): 'success' | 'warning' | 'destructive' { + const status = this.getCertStatus(cert); + switch (status) { + case 'Valid': return 'success'; + case 'Expiring': return 'warning'; + default: return 'destructive'; + } + } + + async renewCertificate(domain: string): Promise { + try { + const response = await this.api.renewCertificate(domain); + if (response.success) { + this.toast.success('Certificate renewal initiated'); + } else { + this.toast.error(response.error || 'Failed to renew certificate'); + } + } catch { + this.toast.error('Failed to renew certificate'); + } } } diff --git a/ui/src/app/features/domains/domains.component.ts b/ui/src/app/features/domains/domains.component.ts index a47f55b..e980323 100644 --- a/ui/src/app/features/domains/domains.component.ts +++ b/ui/src/app/features/domains/domains.component.ts @@ -1,216 +1,242 @@ -import { Component, OnInit, inject, signal } from '@angular/core'; -import { CommonModule } from '@angular/common'; +import { Component, inject, signal, OnInit } from '@angular/core'; import { RouterLink } from '@angular/router'; import { ApiService } from '../../core/services/api.service'; import { ToastService } from '../../core/services/toast.service'; - -interface DomainView { - domain: { - id: number; - domain: string; - dnsProvider: 'cloudflare' | 'manual' | null; - isObsolete: boolean; - defaultWildcard: boolean; - }; - serviceCount: number; - certificateStatus: 'valid' | 'expiring-soon' | 'expired' | 'pending' | 'none'; - daysRemaining: number | null; - certificates: any[]; - requirements: any[]; -} +import { IDomainDetail } from '../../core/types/api.types'; +import { + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, +} from '../../ui/card/card.component'; +import { ButtonComponent } from '../../ui/button/button.component'; +import { BadgeComponent } from '../../ui/badge/badge.component'; +import { + TableComponent, + TableHeaderComponent, + TableBodyComponent, + TableRowComponent, + TableHeadComponent, + TableCellComponent, +} from '../../ui/table/table.component'; +import { SkeletonComponent } from '../../ui/skeleton/skeleton.component'; @Component({ selector: 'app-domains', standalone: true, - imports: [CommonModule, RouterLink], + imports: [ + RouterLink, + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, + ButtonComponent, + BadgeComponent, + TableComponent, + TableHeaderComponent, + TableBodyComponent, + TableRowComponent, + TableHeadComponent, + TableCellComponent, + SkeletonComponent, + ], template: ` -
-
-

Domains

-
- @if (loading()) { -
-

Loading domains...

-
- } @else if (domains().length > 0) { -
- - - - - - - - - - - - - @for (domainView of domains(); track domainView.domain.id) { - - - - - - - - - } - -
DomainProviderServicesCertificateExpiryActions
-
{{ domainView.domain.domain }}
- @if (domainView.domain.isObsolete) { - Obsolete - } -
- @if (domainView.domain.dnsProvider === 'cloudflare') { - - Cloudflare - - } @else if (domainView.domain.dnsProvider === 'manual') { - - Manual - - } @else { - None - } - - {{ domainView.serviceCount }} - - @switch (domainView.certificateStatus) { - @case ('valid') { - - Valid - - } - @case ('expiring-soon') { - - Expiring Soon - - } - @case ('expired') { - - Expired - - } - @case ('pending') { - - Pending - - } - @default { - - None - - } - } - - @if (domainView.daysRemaining !== null) { - - {{ domainView.daysRemaining }} days - - } @else { - - } - - - View Details - -
-
+ +
+ + + Total Domains + + + + + +
{{ domains().length }}
+
+
+ + + Valid Certificates + + + + + +
{{ countByStatus('valid') }}
+
+
+ + + Expiring Soon + + + + + +
{{ countByStatus('expiring-soon') }}
+
+
+ + + Expired/Pending + + + + + +
{{ countByStatus('expired') + countByStatus('pending') }}
+
+
+
- -
-
-
Total Domains
-
{{ domains().length }}
-
-
-
Valid Certificates
-
{{ getStatusCount('valid') }}
-
-
-
Expiring Soon
-
{{ getStatusCount('expiring-soon') }}
-
-
-
Expired/Pending
-
{{ getStatusCount('expired') + getStatusCount('pending') }}
-
-
- } @else { -
-

No domains found

-

- Sync your Cloudflare zones or manually add domains to get started -

- -
- } + + + + @if (loading() && domains().length === 0) { +
+ @for (_ of [1,2,3]; track $index) { + + } +
+ } @else if (domains().length === 0) { +
+

No domains found

+

Sync domains from Cloudflare to get started.

+ +
+ } @else { + + + + Domain + Provider + Services + Certificate + Expires + Actions + + + + @for (d of domains(); track d.domain.id) { + + +
+ {{ d.domain.domain }} + @if (d.domain.isObsolete) { + Obsolete + } +
+
+ + + {{ d.domain.dnsProvider || 'None' }} + + + {{ d.serviceCount }} + + + {{ d.certificateStatus }} + + + + @if (d.daysRemaining !== null) { + + {{ d.daysRemaining }} days + + } @else { + - + } + + + + + + +
+ } +
+
+ } +
+
`, }) export class DomainsComponent implements OnInit { - private apiService = inject(ApiService); - private toastService = inject(ToastService); + private api = inject(ApiService); + private toast = inject(ToastService); - domains = signal([]); - loading = signal(true); + domains = signal([]); + loading = signal(false); syncing = signal(false); ngOnInit(): void { this.loadDomains(); } - loadDomains(): void { + async loadDomains(): Promise { this.loading.set(true); - this.apiService.getDomains().subscribe({ - next: (response) => { - if (response.success && response.data) { - this.domains.set(response.data); - } - this.loading.set(false); - }, - error: () => { - this.loading.set(false); - }, - }); + try { + const response = await this.api.getDomains(); + if (response.success && response.data) { + this.domains.set(response.data); + } + } catch { + this.toast.error('Failed to load domains'); + } finally { + this.loading.set(false); + } } - syncDomains(): void { + async syncDomains(): Promise { this.syncing.set(true); - this.apiService.syncCloudflareDomains().subscribe({ - next: (response) => { - if (response.success) { - this.toastService.success('Cloudflare domains synced successfully'); - this.loadDomains(); - } - this.syncing.set(false); - }, - error: (error) => { - this.toastService.error('Failed to sync Cloudflare domains: ' + (error.error?.error || error.message)); - this.syncing.set(false); - }, - }); + try { + const response = await this.api.syncCloudflareDomains(); + if (response.success) { + this.toast.success('Domains synced'); + this.loadDomains(); + } else { + this.toast.error(response.error || 'Failed to sync domains'); + } + } catch { + this.toast.error('Failed to sync domains'); + } finally { + this.syncing.set(false); + } } - getStatusCount(status: string): number { + countByStatus(status: string): number { return this.domains().filter(d => d.certificateStatus === status).length; } + + getCertStatusVariant(status: string): 'success' | 'warning' | 'destructive' | 'secondary' { + switch (status) { + case 'valid': return 'success'; + case 'expiring-soon': return 'warning'; + case 'expired': return 'destructive'; + case 'pending': return 'secondary'; + default: return 'secondary'; + } + } } diff --git a/ui/src/app/features/login/login.component.ts b/ui/src/app/features/login/login.component.ts index 0827ab4..dc21022 100644 --- a/ui/src/app/features/login/login.component.ts +++ b/ui/src/app/features/login/login.component.ts @@ -1,103 +1,163 @@ -import { Component, inject } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; +import { Component, inject, signal } from '@angular/core'; import { Router } from '@angular/router'; +import { FormsModule } from '@angular/forms'; import { AuthService } from '../../core/services/auth.service'; +import { ThemeService } from '../../core/services/theme.service'; +import { + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, + CardFooterComponent, +} from '../../ui/card/card.component'; +import { ButtonComponent } from '../../ui/button/button.component'; +import { InputComponent } from '../../ui/input/input.component'; +import { LabelComponent } from '../../ui/label/label.component'; +import { AlertComponent, AlertDescriptionComponent } from '../../ui/alert/alert.component'; @Component({ selector: 'app-login', standalone: true, - imports: [CommonModule, FormsModule], + imports: [ + FormsModule, + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, + CardFooterComponent, + ButtonComponent, + InputComponent, + LabelComponent, + AlertComponent, + AlertDescriptionComponent, + ], template: ` -
-
-
-

- Onebox -

-

- Sign in to your account -

-
-
-
-
- +
+
+ +
+ + + +
+ + + +
+ Welcome to Onebox + Enter your credentials to sign in +
+ + + + @if (error()) { + + {{ error() }} + + } + +
+
-
- + +
+
-
+
- @if (error) { -
-

{{ error }}

-
- } - -
+ -
- -
-

Default credentials: admin / admin

-

Please change after first login

-
+ -
+ +
+

+ Default credentials: admin / admin +

+
+
`, }) export class LoginComponent { - private authService = inject(AuthService); + private auth = inject(AuthService); private router = inject(Router); + theme = inject(ThemeService); username = ''; password = ''; - loading = false; - error = ''; + loading = signal(false); + error = signal(null); - onSubmit(): void { - this.error = ''; - this.loading = true; + async onSubmit(): Promise { + if (!this.username || !this.password) { + this.error.set('Please enter username and password'); + return; + } - this.authService.login({ username: this.username, password: this.password }).subscribe({ - next: (response) => { - this.loading = false; - if (response.success) { - this.router.navigate(['/dashboard']); - } else { - this.error = response.error || 'Login failed'; - } - }, - error: (err) => { - this.loading = false; - this.error = err.error?.error || 'An error occurred during login'; - }, - }); + this.loading.set(true); + this.error.set(null); + + const result = await this.auth.login(this.username, this.password); + + this.loading.set(false); + + if (result.success) { + this.router.navigate(['/dashboard']); + } else { + this.error.set(result.error || 'Invalid credentials'); + } } } diff --git a/ui/src/app/features/registries/registries.component.ts b/ui/src/app/features/registries/registries.component.ts index 68607ce..b10935e 100644 --- a/ui/src/app/features/registries/registries.component.ts +++ b/ui/src/app/features/registries/registries.component.ts @@ -1,99 +1,229 @@ -import { Component, OnInit, inject, signal } from '@angular/core'; -import { CommonModule } from '@angular/common'; +import { Component, inject, signal, OnInit } from '@angular/core'; import { FormsModule } from '@angular/forms'; -import { ApiService, Registry } from '../../core/services/api.service'; +import { ApiService } from '../../core/services/api.service'; +import { ToastService } from '../../core/services/toast.service'; +import { IRegistry, IRegistryCreate } from '../../core/types/api.types'; +import { + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, +} from '../../ui/card/card.component'; +import { ButtonComponent } from '../../ui/button/button.component'; +import { InputComponent } from '../../ui/input/input.component'; +import { LabelComponent } from '../../ui/label/label.component'; +import { + TableComponent, + TableHeaderComponent, + TableBodyComponent, + TableRowComponent, + TableHeadComponent, + TableCellComponent, +} from '../../ui/table/table.component'; +import { SkeletonComponent } from '../../ui/skeleton/skeleton.component'; +import { + DialogComponent, + DialogHeaderComponent, + DialogTitleComponent, + DialogDescriptionComponent, + DialogFooterComponent, +} from '../../ui/dialog/dialog.component'; @Component({ selector: 'app-registries', standalone: true, - imports: [CommonModule, FormsModule], + imports: [ + FormsModule, + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, + ButtonComponent, + InputComponent, + LabelComponent, + TableComponent, + TableHeaderComponent, + TableBodyComponent, + TableRowComponent, + TableHeadComponent, + TableCellComponent, + SkeletonComponent, + DialogComponent, + DialogHeaderComponent, + DialogTitleComponent, + DialogDescriptionComponent, + DialogFooterComponent, + ], template: ` -
-

Docker Registries

- - -
-

Add Registry

-
-
- - -
-
- - -
-
- - -
- -
+
+
+

Docker Registries

+

Manage Docker registry credentials

+ + + + Add Registry + Add credentials for a private Docker registry + + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+ - @if (registries().length > 0) { -
- - - - - - - - - - - @for (registry of registries(); track registry.id) { - - - - - - + + + Registered Registries + + + @if (loading() && registries().length === 0) { +
+ @for (_ of [1,2]; track $index) { + } -
-
URLUsernameCreatedActions
{{ registry.url }}{{ registry.username }}{{ formatDate(registry.createdAt) }} - -
-
- } +
+ } @else if (registries().length === 0) { +
+

No registries configured

+
+ } @else { + + + + URL + Username + Created + Actions + + + + @for (registry of registries(); track registry.id) { + + {{ registry.url }} + {{ registry.username }} + {{ formatDate(registry.createdAt) }} + + + + + } + + + } + +
+ + + + Delete Registry + + Are you sure you want to delete "{{ registryToDelete()?.url }}"? + + + + + + + `, }) export class RegistriesComponent implements OnInit { - private apiService = inject(ApiService); - registries = signal([]); - newRegistry = { url: '', username: '', password: '' }; + private api = inject(ApiService); + private toast = inject(ToastService); + + registries = signal([]); + loading = signal(false); + deleteDialogOpen = signal(false); + registryToDelete = signal(null); + + form: IRegistryCreate = { url: '', username: '', password: '' }; ngOnInit(): void { this.loadRegistries(); } - loadRegistries(): void { - this.apiService.getRegistries().subscribe({ - next: (response) => { - if (response.success && response.data) { - this.registries.set(response.data); - } - }, - }); + async loadRegistries(): Promise { + this.loading.set(true); + try { + const response = await this.api.getRegistries(); + if (response.success && response.data) { + this.registries.set(response.data); + } + } catch { + this.toast.error('Failed to load registries'); + } finally { + this.loading.set(false); + } } - addRegistry(): void { - this.apiService.createRegistry(this.newRegistry).subscribe({ - next: () => { - this.newRegistry = { url: '', username: '', password: '' }; + async addRegistry(): Promise { + if (!this.form.url || !this.form.username || !this.form.password) { + this.toast.error('Please fill in all fields'); + return; + } + + this.loading.set(true); + try { + const response = await this.api.createRegistry(this.form); + if (response.success) { + this.toast.success('Registry added'); + this.form = { url: '', username: '', password: '' }; this.loadRegistries(); - }, - }); + } else { + this.toast.error(response.error || 'Failed to add registry'); + } + } catch { + this.toast.error('Failed to add registry'); + } finally { + this.loading.set(false); + } } - deleteRegistry(registry: Registry): void { - if (confirm(`Delete registry ${registry.url}?`)) { - this.apiService.deleteRegistry(registry.url).subscribe({ - next: () => this.loadRegistries(), - }); + confirmDelete(registry: IRegistry): void { + this.registryToDelete.set(registry); + this.deleteDialogOpen.set(true); + } + + async deleteRegistry(): Promise { + const registry = this.registryToDelete(); + if (!registry?.id) return; + + try { + const response = await this.api.deleteRegistry(registry.id); + if (response.success) { + this.toast.success('Registry deleted'); + this.loadRegistries(); + } else { + this.toast.error(response.error || 'Failed to delete registry'); + } + } catch { + this.toast.error('Failed to delete registry'); + } finally { + this.deleteDialogOpen.set(false); + this.registryToDelete.set(null); } } diff --git a/ui/src/app/features/services/service-create.component.ts b/ui/src/app/features/services/service-create.component.ts index d6b6990..26460d5 100644 --- a/ui/src/app/features/services/service-create.component.ts +++ b/ui/src/app/features/services/service-create.component.ts @@ -1,354 +1,317 @@ -import { Component, OnInit, inject, signal } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; +import { Component, inject, signal, OnInit } from '@angular/core'; import { Router, RouterLink } from '@angular/router'; +import { FormsModule } from '@angular/forms'; import { ApiService } from '../../core/services/api.service'; +import { ToastService } from '../../core/services/toast.service'; +import { IServiceCreate, IDomainDetail } from '../../core/types/api.types'; +import { + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, + CardFooterComponent, +} from '../../ui/card/card.component'; +import { ButtonComponent } from '../../ui/button/button.component'; +import { InputComponent } from '../../ui/input/input.component'; +import { LabelComponent } from '../../ui/label/label.component'; +import { CheckboxComponent } from '../../ui/checkbox/checkbox.component'; +import { AlertComponent, AlertDescriptionComponent } from '../../ui/alert/alert.component'; +import { SeparatorComponent } from '../../ui/separator/separator.component'; interface EnvVar { key: string; value: string; } -interface Domain { - domain: string; - dnsProvider: 'cloudflare' | 'manual' | null; - isObsolete: boolean; -} - @Component({ selector: 'app-service-create', standalone: true, - imports: [CommonModule, FormsModule, RouterLink], + imports: [ + FormsModule, + RouterLink, + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, + CardFooterComponent, + ButtonComponent, + InputComponent, + LabelComponent, + CheckboxComponent, + AlertComponent, + AlertDescriptionComponent, + SeparatorComponent, + ], template: ` -
-

Deploy New Service

+
+ +
+ + + + + Back to Services + +

Deploy Service

+

Deploy a new Docker service

+
-
-
- -
- - -

Lowercase letters, numbers, and hyphens only

-
- - -
- - -

Format: image:tag or registry/image:tag

-
- - -
-
- - -
-

- Store your container image in the local Onebox registry instead of using an external image. -

- - @if (useOneboxRegistry) { -
-
- - -

Tag to use (e.g., latest, v1.0, develop)

-
- -
- - -
-

- Automatically pull and restart the service when a new image is pushed to the registry -

+ + + + Service Configuration + Configure your service settings + + + +
+
+ + +

Lowercase letters, numbers, and hyphens only

- } -
- -
- - -

Port that your application listens on

-
- - -
- - - - @for (domain of availableDomains(); track domain.domain) { - - } - - - @if (domainWarning()) { -
-
-
- - - -
-
-

- {{ domainWarningTitle() }} -

-

{{ domainWarningMessage() }}

- -
-
+
+ + +

e.g., nginx:latest, registry.example.com/image:tag

- } @else { -

- Leave empty to skip automatic DNS & SSL. - @if (availableDomains().length > 0) { - Or select from {{ availableDomains().length }} available domain(s). + +

+ + +
+ +
+ + + + @for (d of domains(); track d.domain.domain) { + + } + + @if (domainWarning()) { + + {{ domainWarning() }} + } -

- } -
+
+
- -
- - @for (env of envVars(); track $index) { -
- - -
- } - -
- -
-
- - + @if (envVars().length > 0) { +
+ @for (env of envVars(); track $index; let i = $index) { +
+ + + +
+ } +
+ }
-
- - -
-
- @if (error()) { -
-

{{ error() }}

-
- } + - -
- + + - -
- -
+ + +
`, }) export class ServiceCreateComponent implements OnInit { - private apiService = inject(ApiService); + private api = inject(ApiService); private router = inject(Router); + private toast = inject(ToastService); + + form: IServiceCreate = { + name: '', + image: '', + port: 80, + domain: '', + useOneboxRegistry: false, + registryImageTag: 'latest', + autoUpdateOnPush: false, + }; - name = ''; - image = ''; - port = 80; - domain = ''; - autoDNS = true; - autoSSL = true; envVars = signal([]); + domains = signal([]); loading = signal(false); - error = signal(''); - - // Onebox Registry - useOneboxRegistry = false; - registryImageTag = 'latest'; - autoUpdateOnPush = false; - - // Domain validation - availableDomains = signal([]); - domainWarning = signal(false); - domainWarningTitle = signal(''); - domainWarningMessage = signal(''); + domainWarning = signal(null); ngOnInit(): void { this.loadDomains(); } - loadDomains(): void { - this.apiService.getDomains().subscribe({ - next: (response) => { - if (response.success && response.data) { - const domains: Domain[] = response.data.map((d: any) => ({ - domain: d.domain.domain, - dnsProvider: d.domain.dnsProvider, - isObsolete: d.domain.isObsolete, - })); - this.availableDomains.set(domains); - } - }, - error: () => { - // Silently fail - domains list not critical - }, - }); - } - - onDomainChange(): void { - if (!this.domain) { - this.domainWarning.set(false); - return; - } - - // Extract base domain from entered domain - const parts = this.domain.split('.'); - if (parts.length < 2) { - // Not a valid domain format - this.domainWarning.set(false); - return; - } - - const baseDomain = parts.slice(-2).join('.'); - - // Check if base domain exists in available domains - const matchingDomain = this.availableDomains().find( - (d) => d.domain === baseDomain - ); - - if (!matchingDomain) { - this.domainWarning.set(true); - this.domainWarningTitle.set('Domain not found'); - this.domainWarningMessage.set( - `The base domain "${baseDomain}" is not in the Domain table. The service will deploy, but certificate management may not work. Sync your Cloudflare domains or manually add the domain first.` - ); - } else if (matchingDomain.isObsolete) { - this.domainWarning.set(true); - this.domainWarningTitle.set('Domain is obsolete'); - this.domainWarningMessage.set( - `The domain "${baseDomain}" is marked as obsolete (likely removed from Cloudflare). Certificate management may not work properly.` - ); - } else { - this.domainWarning.set(false); + async loadDomains(): Promise { + try { + const response = await this.api.getDomains(); + if (response.success && response.data) { + this.domains.set(response.data); + } + } catch { + // Silent fail - domain autocomplete is optional } } addEnvVar(): void { - this.envVars.update((vars) => [...vars, { key: '', value: '' }]); + this.envVars.update(vars => [...vars, { key: '', value: '' }]); } removeEnvVar(index: number): void { - this.envVars.update((vars) => vars.filter((_, i) => i !== index)); + this.envVars.update(vars => vars.filter((_, i) => i !== index)); } - onSubmit(): void { - this.error.set(''); + validateDomain(): void { + if (!this.form.domain) { + this.domainWarning.set(null); + return; + } + + const domain = this.domains().find(d => d.domain.domain === this.form.domain); + if (!domain) { + this.domainWarning.set('This domain is not in your domain list. DNS and SSL may not be configured automatically.'); + } else if (domain.domain.isObsolete) { + this.domainWarning.set('This domain is marked as obsolete.'); + } else { + this.domainWarning.set(null); + } + } + + async onSubmit(): Promise { + if (!this.form.name || !this.form.image || !this.form.port) { + this.toast.error('Please fill in all required fields'); + return; + } + this.loading.set(true); - // Convert env vars to object + // Build env vars object const envVarsObj: Record = {}; for (const env of this.envVars()) { if (env.key && env.value) { @@ -356,36 +319,23 @@ export class ServiceCreateComponent implements OnInit { } } - const data = { - name: this.name, - image: this.image, - port: this.port, - domain: this.domain || undefined, - envVars: envVarsObj, - autoDNS: this.autoDNS, - autoSSL: this.autoSSL, - useOneboxRegistry: this.useOneboxRegistry, - registryImageTag: this.useOneboxRegistry ? this.registryImageTag : undefined, - autoUpdateOnPush: this.useOneboxRegistry ? this.autoUpdateOnPush : undefined, + const data: IServiceCreate = { + ...this.form, + envVars: Object.keys(envVarsObj).length > 0 ? envVarsObj : undefined, }; - this.apiService.createService(data).subscribe({ - next: (response) => { - this.loading.set(false); - if (response.success) { - this.router.navigate(['/services']); - } else { - this.error.set(response.error || 'Failed to deploy service'); - } - }, - error: (err) => { - this.loading.set(false); - this.error.set(err.error?.error || 'An error occurred'); - }, - }); - } - - cancel(): void { - this.router.navigate(['/services']); + try { + const response = await this.api.createService(data); + if (response.success) { + this.toast.success(`Service "${this.form.name}" deployed successfully`); + this.router.navigate(['/services']); + } else { + this.toast.error(response.error || 'Failed to deploy service'); + } + } catch { + this.toast.error('Failed to deploy service'); + } finally { + this.loading.set(false); + } } } diff --git a/ui/src/app/features/services/service-detail.component.ts b/ui/src/app/features/services/service-detail.component.ts index 74de708..bf69c4e 100644 --- a/ui/src/app/features/services/service-detail.component.ts +++ b/ui/src/app/features/services/service-detail.component.ts @@ -1,663 +1,428 @@ -import { Component, OnInit, OnDestroy, inject, signal } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { FormsModule } from '@angular/forms'; +import { Component, inject, signal, OnInit, OnDestroy, ViewChild, ElementRef, effect } from '@angular/core'; import { ActivatedRoute, Router, RouterLink } from '@angular/router'; -import { ApiService, Service } from '../../core/services/api.service'; +import { FormsModule } from '@angular/forms'; +import { ApiService } from '../../core/services/api.service'; import { ToastService } from '../../core/services/toast.service'; - -interface EnvVar { - key: string; - value: string; -} - -interface Domain { - domain: string; - dnsProvider: 'cloudflare' | 'manual' | null; - isObsolete: boolean; -} +import { LogStreamService } from '../../core/services/log-stream.service'; +import { IService, IServiceUpdate } from '../../core/types/api.types'; +import { + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, + CardFooterComponent, +} from '../../ui/card/card.component'; +import { ButtonComponent } from '../../ui/button/button.component'; +import { BadgeComponent } from '../../ui/badge/badge.component'; +import { InputComponent } from '../../ui/input/input.component'; +import { LabelComponent } from '../../ui/label/label.component'; +import { SkeletonComponent } from '../../ui/skeleton/skeleton.component'; +import { SeparatorComponent } from '../../ui/separator/separator.component'; +import { + DialogComponent, + DialogHeaderComponent, + DialogTitleComponent, + DialogDescriptionComponent, + DialogFooterComponent, +} from '../../ui/dialog/dialog.component'; @Component({ selector: 'app-service-detail', standalone: true, - imports: [CommonModule, FormsModule, RouterLink], + imports: [ + FormsModule, + RouterLink, + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, + CardFooterComponent, + ButtonComponent, + BadgeComponent, + InputComponent, + LabelComponent, + SkeletonComponent, + SeparatorComponent, + DialogComponent, + DialogHeaderComponent, + DialogTitleComponent, + DialogDescriptionComponent, + DialogFooterComponent, + ], template: ` -
- @if (loading()) { -
-
+
+ +
+ + + + + Back to Services + + + @if (loading() && !service()) { + + } @else if (service()) { +
+

{{ service()!.name }}

+ {{ service()!.status }} +
+ } +
+ + @if (loading() && !service()) { +
+ + + + + + @for (_ of [1,2,3,4]; track $index) { + + } + +
} @else if (service()) { -
-
-

{{ service()!.name }}

- - {{ service()!.status }} - -
-
- - -
-
-

Service Details

- @if (!isEditing()) { - - } -
- - @if (!isEditing()) { -
-
-
Image
-
{{ service()!.image }}
-
-
-
Port
-
{{ service()!.port }}
-
- @if (service()!.domain) { - +
+ + + + Service Details + @if (!editMode()) { + } - @if (service()!.containerID) { -
-
Container ID
-
{{ service()!.containerID?.substring(0, 12) }}
-
- } -
-
Created
-
{{ formatDate(service()!.createdAt) }}
-
-
-
Updated
-
{{ formatDate(service()!.updatedAt) }}
-
-
- - - @if (service()!.useOneboxRegistry) { -
-

Onebox Registry

-
+ + + @if (editMode()) { +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ } @else { +
-
Repository
-
{{ service()!.registryRepository }}
+
Image
+
{{ service()!.image }}
-
Tag
-
{{ service()!.registryImageTag || 'latest' }}
+
Port
+
{{ service()!.port }}
- @if (service()!.registryToken) { -
-
Push/Pull Token
-
-
- - -
-

- Use this token to push images: docker login -u unused -p [token] {{ registryBaseUrl() }} -

-
-
- }
-
Auto-update
-
- {{ service()!.autoUpdateOnPush ? 'Enabled' : 'Disabled' }} +
Domain
+
+ @if (service()!.domain) { + + {{ service()!.domain }} + + } @else { + Not configured + }
- @if (service()!.imageDigest) { -
-
Current Digest
-
{{ service()!.imageDigest }}
+ @if (service()!.containerID) { +
+
Container ID
+
{{ service()!.containerID?.slice(0, 12) }}
} +
+
Created
+
{{ formatDate(service()!.createdAt) }}
+
+
+
Updated
+
{{ formatDate(service()!.updatedAt) }}
+
-
- } - - - @if (Object.keys(service()!.envVars).length > 0) { -
-

Environment Variables

-
- @for (entry of Object.entries(service()!.envVars); track entry[0]) { -
- {{ entry[0] }} - {{ entry[1] }} -
- } -
-
- } - } @else { - -
- -
- - -

Format: image:tag or registry/image:tag

-
- - -
- - -

Port that your application listens on

-
- - -
- - - - @for (domain of availableDomains(); track domain.domain) { - - } - - - @if (domainWarning()) { -
-
-
- - - -
-
-

- {{ domainWarningTitle() }} -

-

{{ domainWarningMessage() }}

- -
-
-
- } @else { -

- Leave empty to skip automatic DNS & SSL. - @if (availableDomains().length > 0) { - Or select from {{ availableDomains().length }} available domain(s). - } -

- } -
- - -
- - @for (env of editEnvVars(); track $index) { -
- - - -
- } - -
- - @if (error()) { -
-

{{ error() }}

-
} + + - -
- - + } + @if (service()!.status === 'running') { + + + } +
-
+ + + + + @if (service()!.envVars && getEnvKeys(service()!.envVars).length > 0) { + + + Environment Variables + + +
+ @for (key of getEnvKeys(service()!.envVars); track key) { +
+ {{ key }}: + {{ service()!.envVars[key] }} +
+ } +
+
+
+ } + + + @if (service()!.useOneboxRegistry) { + + + Onebox Registry + Push images directly to this service + + +
+
Repository
+
{{ service()!.registryRepository }}
+
+
+
Tag
+
{{ service()!.registryImageTag || 'latest' }}
+
+ @if (service()!.registryToken) { +
+
Push Token
+
+ + +
+
+ } +
+
Auto-update on push
+
{{ service()!.autoUpdateOnPush ? 'Enabled' : 'Disabled' }}
+
+
+
}
- - @if (!isEditing()) { -
-

Actions

-
- @if (service()!.status === 'stopped') { - - } - @if (service()!.status === 'running') { - - - } - -
-
- } - - - @if (!isEditing()) { -
-
-

Logs

-
- - - - - - - - - - -
-
- @if (loadingLogs()) { -
-
-
- } @else { -
- @if (filteredLogs().length === 0) { -

No logs available

+ + + +
+ Logs + + @if (logStream.isStreaming()) { + + + + + + Live streaming + } @else { - @for (line of filteredLogs(); track $index) { -
{{ line }}
- } + Container logs } -
- } - @if (filteredLogs().length > 0 && filteredLogs().length !== logLines().length) { -
- Showing {{ filteredLogs().length }} of {{ logLines().length }} lines -
- } -
- } + +
+
+ @if (logStream.isStreaming()) { + + } @else { + + } + + +
+ + +
+ @if (logStream.state().error) { +

Error: {{ logStream.state().error }}

+ } @else if (logStream.logs().length > 0) { + @for (line of logStream.logs(); track $index) { +
{{ line }}
+ } + } @else if (logStream.isStreaming()) { +

Waiting for logs...

+ } @else { +

Click "Stream" to start live log streaming

+ } +
+
+ }
+ + + + + Delete Service + + Are you sure you want to delete "{{ service()?.name }}"? This action cannot be undone. + + + + + + + `, }) export class ServiceDetailComponent implements OnInit, OnDestroy { - private apiService = inject(ApiService); private route = inject(ActivatedRoute); private router = inject(Router); + private api = inject(ApiService); + private toast = inject(ToastService); + logStream = inject(LogStreamService); - service = signal(null); - logs = signal(''); - logLines = signal([]); - filteredLogs = signal([]); - logSearch = ''; - logLevelFilter = 'all'; - logsAutoRefresh = false; - private logsRefreshInterval?: number; - loading = signal(true); - loadingLogs = signal(false); + @ViewChild('logContainer') logContainer!: ElementRef; - // Edit mode - isEditing = signal(false); - saving = signal(false); - error = signal(''); - editForm = { - image: '', - port: 80, - domain: '', - }; - editEnvVars = signal([]); + service = signal(null); + loading = signal(false); + actionLoading = signal(false); + editMode = signal(false); + deleteDialogOpen = signal(false); + autoScroll = true; - // Domain validation - availableDomains = signal([]); - domainWarning = signal(false); - domainWarningTitle = signal(''); - domainWarningMessage = signal(''); + editForm: IServiceUpdate = {}; - Object = Object; + constructor() { + // Auto-scroll when new logs arrive + effect(() => { + const logs = this.logStream.logs(); + if (logs.length > 0 && this.autoScroll && this.logContainer?.nativeElement) { + setTimeout(() => { + const container = this.logContainer.nativeElement; + container.scrollTop = container.scrollHeight; + }, 0); + } + }); + } ngOnInit(): void { - const name = this.route.snapshot.paramMap.get('name')!; - this.loadService(name); - this.loadLogs(name); - this.loadDomains(); + const name = this.route.snapshot.paramMap.get('name'); + if (name) { + this.loadService(name); + } } - loadService(name: string): void { + ngOnDestroy(): void { + this.logStream.disconnect(); + } + + async loadService(name: string): Promise { this.loading.set(true); - this.apiService.getService(name).subscribe({ - next: (response) => { - if (response.success && response.data) { - this.service.set(response.data); - } - this.loading.set(false); - }, - error: () => { - this.loading.set(false); + try { + const response = await this.api.getService(name); + if (response.success && response.data) { + this.service.set(response.data); + this.editForm = { + image: response.data.image, + port: response.data.port, + domain: response.data.domain, + }; + } else { + this.toast.error(response.error || 'Service not found'); this.router.navigate(['/services']); - }, - }); - } - - loadLogs(name: string): void { - this.loadingLogs.set(true); - this.apiService.getServiceLogs(name).subscribe({ - next: (response) => { - if (response.success && response.data) { - this.logs.set(response.data); - const lines = response.data.split('\n').filter((line: string) => line.trim()); - this.logLines.set(lines); - this.filterLogs(); - } - this.loadingLogs.set(false); - }, - error: () => { - this.loadingLogs.set(false); - }, - }); - } - - filterLogs(): void { - let lines = this.logLines(); - - // Apply level filter - if (this.logLevelFilter !== 'all') { - lines = lines.filter(line => this.isLogLevel(line, this.logLevelFilter)); - } - - // Apply search filter - if (this.logSearch.trim()) { - const searchLower = this.logSearch.toLowerCase(); - lines = lines.filter(line => line.toLowerCase().includes(searchLower)); - } - - this.filteredLogs.set(lines); - } - - isLogLevel(line: string, level: string): boolean { - const lineLower = line.toLowerCase(); - if (level === 'error') return lineLower.includes('error') || lineLower.includes('✖'); - if (level === 'warn') return lineLower.includes('warn') || lineLower.includes('warning'); - if (level === 'info') return lineLower.includes('info') || lineLower.includes('ℹ'); - if (level === 'debug') return lineLower.includes('debug'); - return false; - } - - hasLogLevel(line: string): boolean { - return this.isLogLevel(line, 'error') || - this.isLogLevel(line, 'warn') || - this.isLogLevel(line, 'info') || - this.isLogLevel(line, 'debug'); - } - - toggleLogsAutoRefresh(): void { - if (this.logsAutoRefresh) { - this.logsRefreshInterval = window.setInterval(() => { - this.refreshLogs(); - }, 5000); // Refresh every 5 seconds - } else { - if (this.logsRefreshInterval) { - clearInterval(this.logsRefreshInterval); - this.logsRefreshInterval = undefined; } + } catch { + this.toast.error('Failed to load service'); + } finally { + this.loading.set(false); } } - loadDomains(): void { - this.apiService.getDomains().subscribe({ - next: (response) => { - if (response.success && response.data) { - const domains: Domain[] = response.data.map((d: any) => ({ - domain: d.domain.domain, - dnsProvider: d.domain.dnsProvider, - isObsolete: d.domain.isObsolete, - })); - this.availableDomains.set(domains); - } - }, - error: () => { - // Silently fail - domains list not critical - }, - }); - } - - startEditing(): void { - const svc = this.service()!; - this.editForm.image = svc.image; - this.editForm.port = svc.port; - this.editForm.domain = svc.domain || ''; - - // Convert env vars to array - const envVars: EnvVar[] = []; - for (const [key, value] of Object.entries(svc.envVars || {})) { - envVars.push({ key, value }); - } - this.editEnvVars.set(envVars); - - this.isEditing.set(true); - this.error.set(''); - } - - cancelEditing(): void { - this.isEditing.set(false); - this.error.set(''); - this.domainWarning.set(false); - } - - saveService(): void { - this.error.set(''); - this.saving.set(true); - - // Convert env vars to object - const envVarsObj: Record = {}; - for (const env of this.editEnvVars()) { - if (env.key && env.value) { - envVarsObj[env.key] = env.value; - } - } - - const updates = { - image: this.editForm.image, - port: this.editForm.port, - domain: this.editForm.domain || undefined, - envVars: envVarsObj, - }; - - this.apiService.updateService(this.service()!.name, updates).subscribe({ - next: (response) => { - this.saving.set(false); - if (response.success) { - this.service.set(response.data!); - this.isEditing.set(false); - } else { - this.error.set(response.error || 'Failed to update service'); - } - }, - error: (err) => { - this.saving.set(false); - this.error.set(err.error?.error || 'An error occurred'); - }, - }); - } - - addEnvVar(): void { - this.editEnvVars.update((vars) => [...vars, { key: '', value: '' }]); - } - - removeEnvVar(index: number): void { - this.editEnvVars.update((vars) => vars.filter((_, i) => i !== index)); - } - - onDomainChange(): void { - if (!this.editForm.domain) { - this.domainWarning.set(false); - return; - } - - // Extract base domain from entered domain - const parts = this.editForm.domain.split('.'); - if (parts.length < 2) { - this.domainWarning.set(false); - return; - } - - const baseDomain = parts.slice(-2).join('.'); - - // Check if base domain exists in available domains - const matchingDomain = this.availableDomains().find( - (d) => d.domain === baseDomain - ); - - if (!matchingDomain) { - this.domainWarning.set(true); - this.domainWarningTitle.set('Domain not found'); - this.domainWarningMessage.set( - `The base domain "${baseDomain}" is not in the Domain table. The service will update, but certificate management may not work. Sync your Cloudflare domains or manually add the domain first.` - ); - } else if (matchingDomain.isObsolete) { - this.domainWarning.set(true); - this.domainWarningTitle.set('Domain is obsolete'); - this.domainWarningMessage.set( - `The domain "${baseDomain}" is marked as obsolete (likely removed from Cloudflare). Certificate management may not work properly.` - ); - } else { - this.domainWarning.set(false); + startLogStream(): void { + const name = this.service()?.name; + if (name) { + this.logStream.connect(name); } } - refreshLogs(): void { - this.loadLogs(this.service()!.name); + stopLogStream(): void { + this.logStream.disconnect(); } - startService(): void { - this.apiService.startService(this.service()!.name).subscribe({ - next: () => { - this.loadService(this.service()!.name); - }, - }); + clearLogs(): void { + this.logStream.clearLogs(); } - stopService(): void { - this.apiService.stopService(this.service()!.name).subscribe({ - next: () => { - this.loadService(this.service()!.name); - }, - }); - } - - restartService(): void { - this.apiService.restartService(this.service()!.name).subscribe({ - next: () => { - this.loadService(this.service()!.name); - }, - }); - } - - deleteService(): void { - if (confirm(`Are you sure you want to delete ${this.service()!.name}?`)) { - this.apiService.deleteService(this.service()!.name).subscribe({ - next: () => { - this.router.navigate(['/services']); - }, - }); + getStatusVariant(status: string): 'success' | 'destructive' | 'warning' | 'secondary' { + switch (status) { + case 'running': return 'success'; + case 'stopped': return 'secondary'; + case 'failed': return 'destructive'; + case 'starting': + case 'stopping': return 'warning'; + default: return 'secondary'; } } @@ -665,21 +430,128 @@ export class ServiceDetailComponent implements OnInit, OnDestroy { return new Date(timestamp).toLocaleString(); } - private toastService = inject(ToastService); - - copyToken(token: string): void { - navigator.clipboard.writeText(token).then(() => { - this.toastService.success('Token copied to clipboard!'); - }).catch(() => { - this.toastService.error('Failed to copy token'); - }); + getEnvKeys(envVars: Record): string[] { + return Object.keys(envVars); } - registryBaseUrl = signal('localhost:5000'); + cancelEdit(): void { + this.editMode.set(false); + if (this.service()) { + this.editForm = { + image: this.service()!.image, + port: this.service()!.port, + domain: this.service()!.domain, + }; + } + } - ngOnDestroy(): void { - if (this.logsRefreshInterval) { - clearInterval(this.logsRefreshInterval); + async saveChanges(): Promise { + const name = this.service()?.name; + if (!name) return; + + this.actionLoading.set(true); + try { + const response = await this.api.updateService(name, this.editForm); + if (response.success) { + this.toast.success('Service updated'); + this.editMode.set(false); + this.loadService(name); + } else { + this.toast.error(response.error || 'Failed to update service'); + } + } catch { + this.toast.error('Failed to update service'); + } finally { + this.actionLoading.set(false); + } + } + + async startService(): Promise { + const name = this.service()?.name; + if (!name) return; + + this.actionLoading.set(true); + try { + const response = await this.api.startService(name); + if (response.success) { + this.toast.success('Service started'); + this.loadService(name); + } else { + this.toast.error(response.error || 'Failed to start service'); + } + } catch { + this.toast.error('Failed to start service'); + } finally { + this.actionLoading.set(false); + } + } + + async stopService(): Promise { + const name = this.service()?.name; + if (!name) return; + + this.actionLoading.set(true); + try { + const response = await this.api.stopService(name); + if (response.success) { + this.toast.success('Service stopped'); + this.loadService(name); + } else { + this.toast.error(response.error || 'Failed to stop service'); + } + } catch { + this.toast.error('Failed to stop service'); + } finally { + this.actionLoading.set(false); + } + } + + async restartService(): Promise { + const name = this.service()?.name; + if (!name) return; + + this.actionLoading.set(true); + try { + const response = await this.api.restartService(name); + if (response.success) { + this.toast.success('Service restarted'); + this.loadService(name); + } else { + this.toast.error(response.error || 'Failed to restart service'); + } + } catch { + this.toast.error('Failed to restart service'); + } finally { + this.actionLoading.set(false); + } + } + + async deleteService(): Promise { + const name = this.service()?.name; + if (!name) return; + + this.actionLoading.set(true); + try { + const response = await this.api.deleteService(name); + if (response.success) { + this.toast.success('Service deleted'); + this.router.navigate(['/services']); + } else { + this.toast.error(response.error || 'Failed to delete service'); + } + } catch { + this.toast.error('Failed to delete service'); + } finally { + this.actionLoading.set(false); + this.deleteDialogOpen.set(false); + } + } + + copyToken(): void { + const token = this.service()?.registryToken; + if (token) { + navigator.clipboard.writeText(token); + this.toast.success('Token copied to clipboard'); } } } diff --git a/ui/src/app/features/services/services-list.component.ts b/ui/src/app/features/services/services-list.component.ts index 0c7e84a..98d93fb 100644 --- a/ui/src/app/features/services/services-list.component.ts +++ b/ui/src/app/features/services/services-list.component.ts @@ -1,180 +1,332 @@ -import { Component, OnInit, OnDestroy, inject, signal } from '@angular/core'; -import { CommonModule } from '@angular/common'; +import { Component, inject, signal, effect, OnInit } from '@angular/core'; import { RouterLink } from '@angular/router'; -import { ApiService, Service } from '../../core/services/api.service'; +import { ApiService } from '../../core/services/api.service'; import { WebSocketService } from '../../core/services/websocket.service'; -import { Subscription } from 'rxjs'; +import { ToastService } from '../../core/services/toast.service'; +import { IService } from '../../core/types/api.types'; +import { + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, +} from '../../ui/card/card.component'; +import { ButtonComponent } from '../../ui/button/button.component'; +import { BadgeComponent } from '../../ui/badge/badge.component'; +import { + TableComponent, + TableHeaderComponent, + TableBodyComponent, + TableRowComponent, + TableHeadComponent, + TableCellComponent, +} from '../../ui/table/table.component'; +import { SkeletonComponent } from '../../ui/skeleton/skeleton.component'; +import { + DialogComponent, + DialogHeaderComponent, + DialogTitleComponent, + DialogDescriptionComponent, + DialogFooterComponent, +} from '../../ui/dialog/dialog.component'; @Component({ selector: 'app-services-list', standalone: true, - imports: [CommonModule, RouterLink], + imports: [ + RouterLink, + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, + ButtonComponent, + BadgeComponent, + TableComponent, + TableHeaderComponent, + TableBodyComponent, + TableRowComponent, + TableHeadComponent, + TableCellComponent, + SkeletonComponent, + DialogComponent, + DialogHeaderComponent, + DialogTitleComponent, + DialogDescriptionComponent, + DialogFooterComponent, + ], template: ` -
-
-

Services

-
- - Deploy New Service - +
+ +
+
+

Services

+

Manage your deployed services

+ + +
- @if (loading()) { -
-
-
- } @else if (services().length === 0) { -
- - - -

No services

-

Get started by deploying a new service.

- -
- } @else { -
- - - - - - - - - - - - @for (service of services(); track service.id) { - - - - - - - + + + + @if (loading() && services().length === 0) { +
+ @for (_ of [1,2,3]; track $index) { + } -
-
NameImageDomainStatusActions
- - {{ service.name }} - - - {{ service.image }} - - {{ service.domain || '-' }} - - - {{ service.status }} - - - @if (service.status === 'stopped') { - - } - @if (service.status === 'running') { - - - } - -
-
- } +
+ } @else if (services().length === 0) { +
+ + + +

No services

+

Get started by deploying your first service.

+ + + +
+ } @else { + + + + Name + Image + Domain + Status + Actions + + + + @for (service of services(); track service.name) { + + + + {{ service.name }} + + + {{ service.image }} + + @if (service.domain) { + + {{ service.domain }} + + } @else { + - + } + + + + {{ service.status }} + + + +
+ @if (service.status === 'stopped' || service.status === 'failed') { + + } + @if (service.status === 'running') { + + + } + +
+
+
+ } +
+
+ } + +
+ + + + + Delete Service + + Are you sure you want to delete "{{ serviceToDelete()?.name }}"? This action cannot be undone. + + + + + + + `, }) -export class ServicesListComponent implements OnInit, OnDestroy { - private apiService = inject(ApiService); - private wsService = inject(WebSocketService); - private wsSubscription?: Subscription; +export class ServicesListComponent implements OnInit { + private api = inject(ApiService); + private ws = inject(WebSocketService); + private toast = inject(ToastService); - services = signal([]); - loading = signal(true); + services = signal([]); + loading = signal(false); + actionLoading = signal(null); + deleteDialogOpen = signal(false); + serviceToDelete = signal(null); - ngOnInit(): void { - // Initial load - this.loadServices(); - - // Subscribe to WebSocket updates - this.wsSubscription = this.wsService.getMessages().subscribe((message) => { - this.handleWebSocketMessage(message); + constructor() { + // React to WebSocket updates + effect(() => { + const update = this.ws.serviceUpdates(); + const status = this.ws.serviceStatus(); + if (update || status) { + this.loadServices(); + } }); } - ngOnDestroy(): void { - this.wsSubscription?.unsubscribe(); + ngOnInit(): void { + this.loadServices(); } - private handleWebSocketMessage(message: any): void { - if (message.type === 'service_update') { - // Reload the full service list on any service update - this.loadServices(); - } else if (message.type === 'service_status') { - // Update individual service status - const currentServices = this.services(); - const updatedServices = currentServices.map(s => - s.name === message.serviceName - ? { ...s, status: message.status } - : s - ); - this.services.set(updatedServices); + async loadServices(): Promise { + this.loading.set(true); + try { + const response = await this.api.getServices(); + if (response.success && response.data) { + this.services.set(response.data); + } + } catch { + this.toast.error('Failed to load services'); + } finally { + this.loading.set(false); } } - loadServices(): void { - this.loading.set(true); - this.apiService.getServices().subscribe({ - next: (response) => { - if (response.success && response.data) { - this.services.set(response.data); - } - this.loading.set(false); - }, - error: () => { - this.loading.set(false); - }, - }); + getStatusVariant(status: string): 'success' | 'destructive' | 'warning' | 'secondary' { + switch (status) { + case 'running': + return 'success'; + case 'stopped': + return 'secondary'; + case 'failed': + return 'destructive'; + case 'starting': + case 'stopping': + return 'warning'; + default: + return 'secondary'; + } } - startService(service: Service): void { - this.apiService.startService(service.name).subscribe({ - next: () => { - // WebSocket will handle the update - }, - }); + async startService(name: string): Promise { + this.actionLoading.set(name); + try { + const response = await this.api.startService(name); + if (response.success) { + this.toast.success(`Service "${name}" started`); + this.loadServices(); + } else { + this.toast.error(response.error || 'Failed to start service'); + } + } catch { + this.toast.error('Failed to start service'); + } finally { + this.actionLoading.set(null); + } } - stopService(service: Service): void { - this.apiService.stopService(service.name).subscribe({ - next: () => { - // WebSocket will handle the update - }, - }); + async stopService(name: string): Promise { + this.actionLoading.set(name); + try { + const response = await this.api.stopService(name); + if (response.success) { + this.toast.success(`Service "${name}" stopped`); + this.loadServices(); + } else { + this.toast.error(response.error || 'Failed to stop service'); + } + } catch { + this.toast.error('Failed to stop service'); + } finally { + this.actionLoading.set(null); + } } - restartService(service: Service): void { - this.apiService.restartService(service.name).subscribe({ - next: () => { - // WebSocket will handle the update - }, - }); + async restartService(name: string): Promise { + this.actionLoading.set(name); + try { + const response = await this.api.restartService(name); + if (response.success) { + this.toast.success(`Service "${name}" restarted`); + this.loadServices(); + } else { + this.toast.error(response.error || 'Failed to restart service'); + } + } catch { + this.toast.error('Failed to restart service'); + } finally { + this.actionLoading.set(null); + } } - deleteService(service: Service): void { - if (confirm(`Are you sure you want to delete ${service.name}?`)) { - this.apiService.deleteService(service.name).subscribe({ - next: () => { - // WebSocket will handle the update - }, - }); + confirmDelete(service: IService): void { + this.serviceToDelete.set(service); + this.deleteDialogOpen.set(true); + } + + async deleteService(): Promise { + const service = this.serviceToDelete(); + if (!service) return; + + this.actionLoading.set(service.name); + try { + const response = await this.api.deleteService(service.name); + if (response.success) { + this.toast.success(`Service "${service.name}" deleted`); + this.deleteDialogOpen.set(false); + this.loadServices(); + } else { + this.toast.error(response.error || 'Failed to delete service'); + } + } catch { + this.toast.error('Failed to delete service'); + } finally { + this.actionLoading.set(null); + this.serviceToDelete.set(null); } } } diff --git a/ui/src/app/features/settings/settings.component.ts b/ui/src/app/features/settings/settings.component.ts index 20e3549..9252ccc 100644 --- a/ui/src/app/features/settings/settings.component.ts +++ b/ui/src/app/features/settings/settings.component.ts @@ -1,100 +1,337 @@ -import { Component, OnInit, inject, signal } from '@angular/core'; -import { CommonModule } from '@angular/common'; +import { Component, inject, signal, OnInit } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { ApiService } from '../../core/services/api.service'; import { ToastService } from '../../core/services/toast.service'; +import { ThemeService } from '../../core/services/theme.service'; +import { AuthService } from '../../core/services/auth.service'; +import { ISettings } from '../../core/types/api.types'; +import { + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, +} from '../../ui/card/card.component'; +import { ButtonComponent } from '../../ui/button/button.component'; +import { InputComponent } from '../../ui/input/input.component'; +import { LabelComponent } from '../../ui/label/label.component'; +import { SwitchComponent } from '../../ui/switch/switch.component'; +import { SeparatorComponent } from '../../ui/separator/separator.component'; +import { SkeletonComponent } from '../../ui/skeleton/skeleton.component'; @Component({ selector: 'app-settings', standalone: true, - imports: [CommonModule, FormsModule], + imports: [ + FormsModule, + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, + ButtonComponent, + InputComponent, + LabelComponent, + SwitchComponent, + SeparatorComponent, + SkeletonComponent, + ], template: ` -
-

Settings

+
+
+

Settings

+

Manage system configuration

+
-
- -
-

Cloudflare DNS

-
-
- - -
-
- - -
-
- - -
-
+ @if (loading()) { +
+ @for (_ of [1,2,3]; track $index) { + + + + + + + + + }
+ } @else { + + + + Appearance + Customize the look and feel + + +
+
+ +

Toggle dark mode on or off

+
+ +
+
+
- -
-

Server

-
-
- - + + + + Cloudflare Integration + Configure Cloudflare API for DNS management + + +
+
+ + +
+
+ + +
-
- - -
-
-
+

+ Get your API token from the Cloudflare dashboard with DNS edit permissions. +

+ + - -
-

SSL / ACME

-
- - -
-
+ + + + SSL/TLS Settings + Configure certificate management + + +
+
+ +

Automatically renew certificates before expiry

+
+ +
+ +
+ + +

+ Renew certificates when they have fewer than this many days remaining. +

+
+
+ + +

+ Email address for Let's Encrypt notifications. +

+
+
+
+ + + + + Network Settings + Configure network and proxy settings + + +
+
+ + +
+
+ + +
+
+
+
+ +

Redirect all HTTP traffic to HTTPS

+
+ +
+
+
+ + + + + Account + Manage your account settings + + +
+ +

{{ auth.currentUser()?.username || 'Unknown' }}

+
+ +
+

Change Password

+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
-
- +
-
+ }
`, }) export class SettingsComponent implements OnInit { - private apiService = inject(ApiService); - private toastService = inject(ToastService); - settings: any = {}; + private api = inject(ApiService); + private toast = inject(ToastService); + protected theme = inject(ThemeService); + protected auth = inject(AuthService); + + loading = signal(false); + saving = signal(false); + + settings: ISettings = { + cloudflareToken: '', + cloudflareZoneId: '', + autoRenewCerts: true, + renewalThreshold: 30, + acmeEmail: '', + httpPort: 80, + httpsPort: 443, + forceHttps: true, + }; + + passwordForm = { + current: '', + new: '', + confirm: '', + }; ngOnInit(): void { this.loadSettings(); } - loadSettings(): void { - this.apiService.getSettings().subscribe({ - next: (response) => { - if (response.success && response.data) { - this.settings = response.data; - } - }, - }); + async loadSettings(): Promise { + this.loading.set(true); + try { + const response = await this.api.getSettings(); + if (response.success && response.data) { + this.settings = { ...this.settings, ...response.data }; + } + } catch { + this.toast.error('Failed to load settings'); + } finally { + this.loading.set(false); + } } - saveSettings(): void { - // Save each setting individually - const promises = Object.entries(this.settings).map(([key, value]) => - this.apiService.updateSetting(key, value as string).toPromise() - ); + async saveSettings(): Promise { + this.saving.set(true); + try { + const response = await this.api.updateSettings(this.settings); + if (response.success) { + this.toast.success('Settings saved'); + } else { + this.toast.error(response.error || 'Failed to save settings'); + } + } catch { + this.toast.error('Failed to save settings'); + } finally { + this.saving.set(false); + } + } - Promise.all(promises).then(() => { - this.toastService.success('Settings saved successfully'); - }).catch((error) => { - this.toastService.error('Failed to save settings: ' + (error.message || 'Unknown error')); - }); + async changePassword(): Promise { + if (!this.passwordForm.current || !this.passwordForm.new) { + this.toast.error('Please fill in all password fields'); + return; + } + + if (this.passwordForm.new !== this.passwordForm.confirm) { + this.toast.error('New passwords do not match'); + return; + } + + if (this.passwordForm.new.length < 6) { + this.toast.error('Password must be at least 6 characters'); + return; + } + + try { + const response = await this.api.changePassword( + this.passwordForm.current, + this.passwordForm.new + ); + if (response.success) { + this.toast.success('Password changed'); + this.passwordForm = { current: '', new: '', confirm: '' }; + } else { + this.toast.error(response.error || 'Failed to change password'); + } + } catch { + this.toast.error('Failed to change password'); + } } } diff --git a/ui/src/app/shared/components/layout.component.ts b/ui/src/app/shared/components/layout.component.ts deleted file mode 100644 index 3c632de..0000000 --- a/ui/src/app/shared/components/layout.component.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { Component, inject } from '@angular/core'; -import { RouterLink, RouterLinkActive, RouterOutlet } from '@angular/router'; -import { CommonModule } from '@angular/common'; -import { AuthService } from '../../core/services/auth.service'; -import { ToastComponent } from './toast.component'; - -@Component({ - selector: 'app-layout', - standalone: true, - imports: [CommonModule, RouterOutlet, RouterLink, RouterLinkActive, ToastComponent], - template: ` -
- - - - -
- -
- - - -
- `, -}) -export class LayoutComponent { - authService = inject(AuthService); - - logout(): void { - this.authService.logout(); - } -} diff --git a/ui/src/app/shared/components/layout/layout.component.ts b/ui/src/app/shared/components/layout/layout.component.ts new file mode 100644 index 0000000..26d6eee --- /dev/null +++ b/ui/src/app/shared/components/layout/layout.component.ts @@ -0,0 +1,126 @@ +import { Component, inject } from '@angular/core'; +import { RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router'; +import { AuthService } from '../../../core/services/auth.service'; +import { ThemeService } from '../../../core/services/theme.service'; +import { ToasterComponent } from '../../../ui/toast/toaster.component'; +import { ButtonComponent } from '../../../ui/button/button.component'; +import { SeparatorComponent } from '../../../ui/separator/separator.component'; + +interface NavItem { + label: string; + path: string; + icon: string; +} + +@Component({ + selector: 'app-layout', + standalone: true, + imports: [ + RouterOutlet, + RouterLink, + RouterLinkActive, + ToasterComponent, + ButtonComponent, + SeparatorComponent, + ], + template: ` +
+ +
+
+ + + + + + Onebox + + + + + + +
+ + + + + + +
+ + {{ auth.currentUser()?.username || 'User' }} + + +
+
+
+
+ + +
+ +
+ + + +
+ `, + styles: [` + .container { + width: 100%; + max-width: 1400px; + margin-left: auto; + margin-right: auto; + padding-left: 1rem; + padding-right: 1rem; + } + `], +}) +export class LayoutComponent { + auth = inject(AuthService); + theme = inject(ThemeService); + + navItems: NavItem[] = [ + { label: 'Dashboard', path: '/dashboard', icon: 'home' }, + { label: 'Services', path: '/services', icon: 'server' }, + { label: 'Registries', path: '/registries', icon: 'database' }, + { label: 'DNS', path: '/dns', icon: 'globe' }, + { label: 'Domains', path: '/domains', icon: 'link' }, + { label: 'Settings', path: '/settings', icon: 'settings' }, + ]; +} diff --git a/ui/src/app/shared/components/loading-spinner.component.ts b/ui/src/app/shared/components/loading-spinner.component.ts deleted file mode 100644 index 35d31ca..0000000 --- a/ui/src/app/shared/components/loading-spinner.component.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Component, Input } from '@angular/core'; -import { CommonModule } from '@angular/common'; - -@Component({ - selector: 'app-loading-spinner', - standalone: true, - imports: [CommonModule], - template: ` -
-
- @if (text) { - {{ text }} - } -
- `, - styles: [` - @keyframes spin { - to { - transform: rotate(360deg); - } - } - - .animate-spin { - animation: spin 1s linear infinite; - } - - .border-3 { - border-width: 3px; - } - `] -}) -export class LoadingSpinnerComponent { - @Input() size: 'sm' | 'md' | 'lg' | 'xl' = 'md'; - @Input() color: 'primary' | 'white' | 'gray' = 'primary'; - @Input() text?: string; - @Input() containerClass?: string; -} diff --git a/ui/src/app/shared/components/toast.component.ts b/ui/src/app/shared/components/toast.component.ts deleted file mode 100644 index 97760c0..0000000 --- a/ui/src/app/shared/components/toast.component.ts +++ /dev/null @@ -1,91 +0,0 @@ -import { Component, inject } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { ToastService } from '../../core/services/toast.service'; - -@Component({ - selector: 'app-toast', - standalone: true, - imports: [CommonModule], - template: ` -
- @for (toast of toastService.toasts(); track toast.id) { -
- -
- @if (toast.type === 'success') { - - - - } - @if (toast.type === 'error') { - - - - } - @if (toast.type === 'info') { - - - - } - @if (toast.type === 'warning') { - - - - } -
- - -
- {{ toast.message }} -
- - - -
- } -
- `, - styles: [` - @keyframes slide-in-right { - from { - transform: translateX(100%); - opacity: 0; - } - to { - transform: translateX(0); - opacity: 1; - } - } - - .animate-slide-in-right { - animation: slide-in-right 0.3s ease-out; - } - - .toast-item { - transition: all 0.3s ease-out; - } - - .toast-item:hover { - transform: translateY(-2px); - } - `] -}) -export class ToastComponent { - toastService = inject(ToastService); -} diff --git a/ui/src/app/ui/alert/alert.component.ts b/ui/src/app/ui/alert/alert.component.ts new file mode 100644 index 0000000..30eebfe --- /dev/null +++ b/ui/src/app/ui/alert/alert.component.ts @@ -0,0 +1,62 @@ +import { Component, Input } from '@angular/core'; + +export type AlertVariant = 'default' | 'destructive' | 'success' | 'warning'; + +@Component({ + selector: 'ui-alert', + standalone: true, + template: ``, + host: { + '[class]': 'computedClasses', + role: 'alert', + }, +}) +export class AlertComponent { + @Input() variant: AlertVariant = 'default'; + @Input() class = ''; + + private baseClasses = + 'relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground'; + + private variantClasses: Record = { + default: 'bg-background text-foreground', + destructive: + 'border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive', + success: 'border-success/50 text-success dark:border-success [&>svg]:text-success', + warning: 'border-warning/50 text-warning dark:border-warning [&>svg]:text-warning', + }; + + get computedClasses(): string { + return `${this.baseClasses} ${this.variantClasses[this.variant]} ${this.class}`.trim(); + } +} + +@Component({ + selector: 'ui-alert-title', + standalone: true, + template: `
`, +}) +export class AlertTitleComponent { + @Input() class = ''; + + private baseClasses = 'mb-1 font-medium leading-none tracking-tight'; + + get computedClasses(): string { + return `${this.baseClasses} ${this.class}`.trim(); + } +} + +@Component({ + selector: 'ui-alert-description', + standalone: true, + template: `
`, +}) +export class AlertDescriptionComponent { + @Input() class = ''; + + private baseClasses = 'text-sm [&_p]:leading-relaxed'; + + get computedClasses(): string { + return `${this.baseClasses} ${this.class}`.trim(); + } +} diff --git a/ui/src/app/ui/badge/badge.component.ts b/ui/src/app/ui/badge/badge.component.ts new file mode 100644 index 0000000..cc0d9e2 --- /dev/null +++ b/ui/src/app/ui/badge/badge.component.ts @@ -0,0 +1,32 @@ +import { Component, Input } from '@angular/core'; + +export type BadgeVariant = 'default' | 'secondary' | 'destructive' | 'success' | 'warning' | 'outline'; + +@Component({ + selector: 'ui-badge', + standalone: true, + template: ``, + host: { + '[class]': 'computedClasses', + }, +}) +export class BadgeComponent { + @Input() variant: BadgeVariant = 'default'; + @Input() class = ''; + + private baseClasses = + 'inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2'; + + private variantClasses: Record = { + default: 'border-transparent bg-primary text-primary-foreground hover:bg-primary/80', + secondary: 'border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80', + destructive: 'border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80', + success: 'border-transparent bg-success text-success-foreground hover:bg-success/80', + warning: 'border-transparent bg-warning text-warning-foreground hover:bg-warning/80', + outline: 'text-foreground', + }; + + get computedClasses(): string { + return `${this.baseClasses} ${this.variantClasses[this.variant]} ${this.class}`.trim(); + } +} diff --git a/ui/src/app/ui/button/button.component.ts b/ui/src/app/ui/button/button.component.ts new file mode 100644 index 0000000..486d57a --- /dev/null +++ b/ui/src/app/ui/button/button.component.ts @@ -0,0 +1,45 @@ +import { Component, Input } from '@angular/core'; + +export type ButtonVariant = 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link'; +export type ButtonSize = 'default' | 'sm' | 'lg' | 'icon'; + +@Component({ + selector: 'ui-button, button[uiButton]', + standalone: true, + template: ``, + host: { + '[class]': 'computedClasses', + '[disabled]': 'disabled', + '[type]': 'type', + }, +}) +export class ButtonComponent { + @Input() variant: ButtonVariant = 'default'; + @Input() size: ButtonSize = 'default'; + @Input() disabled = false; + @Input() type: 'button' | 'submit' | 'reset' = 'button'; + @Input() class = ''; + + private baseClasses = + 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0'; + + private variantClasses: Record = { + default: 'bg-primary text-primary-foreground hover:bg-primary/90', + destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90', + outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground', + secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80', + ghost: 'hover:bg-accent hover:text-accent-foreground', + link: 'text-primary underline-offset-4 hover:underline', + }; + + private sizeClasses: Record = { + default: 'h-10 px-4 py-2', + sm: 'h-9 rounded-md px-3', + lg: 'h-11 rounded-md px-8', + icon: 'h-10 w-10', + }; + + get computedClasses(): string { + return `${this.baseClasses} ${this.variantClasses[this.variant]} ${this.sizeClasses[this.size]} ${this.class}`.trim(); + } +} diff --git a/ui/src/app/ui/card/card.component.ts b/ui/src/app/ui/card/card.component.ts new file mode 100644 index 0000000..754d3b2 --- /dev/null +++ b/ui/src/app/ui/card/card.component.ts @@ -0,0 +1,112 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'ui-card', + standalone: true, + template: ``, + host: { + '[class]': 'computedClasses', + }, + styles: [':host { display: block; }'], +}) +export class CardComponent { + @Input() class = ''; + + private baseClasses = 'rounded-lg border bg-card text-card-foreground shadow-sm'; + + get computedClasses(): string { + return `${this.baseClasses} ${this.class}`.trim(); + } +} + +@Component({ + selector: 'ui-card-header', + standalone: true, + template: ``, + host: { + '[class]': 'computedClasses', + }, +}) +export class CardHeaderComponent { + @Input() class = ''; + + private baseClasses = 'p-6'; + + get computedClasses(): string { + return `${this.baseClasses} ${this.class}`.trim(); + } +} + +@Component({ + selector: 'ui-card-title', + standalone: true, + template: ``, + host: { + '[class]': 'computedClasses', + }, +}) +export class CardTitleComponent { + @Input() class = ''; + + private baseClasses = 'text-lg font-semibold leading-none tracking-tight'; + + get computedClasses(): string { + return `${this.baseClasses} ${this.class}`.trim(); + } +} + +@Component({ + selector: 'ui-card-description', + standalone: true, + template: ``, + host: { + '[class]': 'computedClasses', + }, + styles: [':host { display: block; }'], +}) +export class CardDescriptionComponent { + @Input() class = ''; + + private baseClasses = 'text-sm text-muted-foreground'; + + get computedClasses(): string { + return `${this.baseClasses} ${this.class}`.trim(); + } +} + +@Component({ + selector: 'ui-card-content', + standalone: true, + template: ``, + host: { + '[class]': 'computedClasses', + }, +}) +export class CardContentComponent { + @Input() class = ''; + + private baseClasses = 'block p-6 pt-0'; + + get computedClasses(): string { + return `${this.baseClasses} ${this.class}`.trim(); + } +} + +@Component({ + selector: 'ui-card-footer', + standalone: true, + template: ``, + host: { + '[class]': 'computedClasses', + }, + styles: [':host { display: block; }'], +}) +export class CardFooterComponent { + @Input() class = ''; + + private baseClasses = 'flex items-center p-6 pt-0'; + + get computedClasses(): string { + return `${this.baseClasses} ${this.class}`.trim(); + } +} diff --git a/ui/src/app/ui/checkbox/checkbox.component.ts b/ui/src/app/ui/checkbox/checkbox.component.ts new file mode 100644 index 0000000..075877a --- /dev/null +++ b/ui/src/app/ui/checkbox/checkbox.component.ts @@ -0,0 +1,79 @@ +import { Component, Input, Output, EventEmitter, forwardRef } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; + +@Component({ + selector: 'ui-checkbox', + standalone: true, + template: ` + + `, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => CheckboxComponent), + multi: true, + }, + ], +}) +export class CheckboxComponent implements ControlValueAccessor { + @Input() checked = false; + @Input() disabled = false; + @Input() class = ''; + @Output() checkedChange = new EventEmitter(); + + private onChange: (value: boolean) => void = () => {}; + private onTouched: () => void = () => {}; + + private baseClasses = + 'peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground flex items-center justify-center'; + + get computedClasses(): string { + const stateClass = this.checked ? 'bg-primary text-primary-foreground' : 'bg-background'; + return `${this.baseClasses} ${stateClass} ${this.class}`.trim(); + } + + toggle(): void { + if (this.disabled) return; + this.checked = !this.checked; + this.checkedChange.emit(this.checked); + this.onChange(this.checked); + this.onTouched(); + } + + writeValue(value: boolean): void { + this.checked = value; + } + + registerOnChange(fn: (value: boolean) => void): void { + this.onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + } +} diff --git a/ui/src/app/ui/dialog/dialog.component.ts b/ui/src/app/ui/dialog/dialog.component.ts new file mode 100644 index 0000000..90bc1fc --- /dev/null +++ b/ui/src/app/ui/dialog/dialog.component.ts @@ -0,0 +1,67 @@ +import { Component, Input, Output, EventEmitter } from '@angular/core'; + +@Component({ + selector: 'ui-dialog', + standalone: true, + template: ` + @if (open) { +
+ +
+ + +
+ +
+
+ } + `, +}) +export class DialogComponent { + @Input() open = false; + @Input() closeOnOverlay = true; + @Output() openChange = new EventEmitter(); + + onOverlayClick(): void { + if (this.closeOnOverlay) { + this.open = false; + this.openChange.emit(false); + } + } + + close(): void { + this.open = false; + this.openChange.emit(false); + } +} + +@Component({ + selector: 'ui-dialog-header', + standalone: true, + template: `
`, +}) +export class DialogHeaderComponent {} + +@Component({ + selector: 'ui-dialog-title', + standalone: true, + template: `

`, +}) +export class DialogTitleComponent {} + +@Component({ + selector: 'ui-dialog-description', + standalone: true, + template: `

`, +}) +export class DialogDescriptionComponent {} + +@Component({ + selector: 'ui-dialog-footer', + standalone: true, + template: `
`, +}) +export class DialogFooterComponent {} diff --git a/ui/src/app/ui/index.ts b/ui/src/app/ui/index.ts new file mode 100644 index 0000000..40ecdc1 --- /dev/null +++ b/ui/src/app/ui/index.ts @@ -0,0 +1,62 @@ +// Button +export { ButtonComponent, ButtonVariant, ButtonSize } from './button/button.component'; + +// Input +export { InputComponent } from './input/input.component'; + +// Label +export { LabelComponent } from './label/label.component'; + +// Card +export { + CardComponent, + CardHeaderComponent, + CardTitleComponent, + CardDescriptionComponent, + CardContentComponent, + CardFooterComponent, +} from './card/card.component'; + +// Badge +export { BadgeComponent, BadgeVariant } from './badge/badge.component'; + +// Table +export { + TableComponent, + TableHeaderComponent, + TableBodyComponent, + TableRowComponent, + TableHeadComponent, + TableCellComponent, +} from './table/table.component'; + +// Skeleton +export { SkeletonComponent } from './skeleton/skeleton.component'; + +// Separator +export { SeparatorComponent } from './separator/separator.component'; + +// Alert +export { AlertComponent, AlertTitleComponent, AlertDescriptionComponent, AlertVariant } from './alert/alert.component'; + +// Checkbox +export { CheckboxComponent } from './checkbox/checkbox.component'; + +// Switch +export { SwitchComponent } from './switch/switch.component'; + +// Toast +export { ToastComponent } from './toast/toast.component'; +export { ToasterComponent } from './toast/toaster.component'; + +// Dialog +export { + DialogComponent, + DialogHeaderComponent, + DialogTitleComponent, + DialogDescriptionComponent, + DialogFooterComponent, +} from './dialog/dialog.component'; + +// Select +export { SelectComponent, SelectOption } from './select/select.component'; diff --git a/ui/src/app/ui/input/input.component.ts b/ui/src/app/ui/input/input.component.ts new file mode 100644 index 0000000..86b8b1a --- /dev/null +++ b/ui/src/app/ui/input/input.component.ts @@ -0,0 +1,26 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'ui-input, input[uiInput]', + standalone: true, + template: ``, + host: { + '[class]': 'computedClasses', + '[disabled]': 'disabled', + '[type]': 'type', + '[placeholder]': 'placeholder', + }, +}) +export class InputComponent { + @Input() type: string = 'text'; + @Input() placeholder: string = ''; + @Input() disabled = false; + @Input() class = ''; + + private baseClasses = + 'flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50'; + + get computedClasses(): string { + return `${this.baseClasses} ${this.class}`.trim(); + } +} diff --git a/ui/src/app/ui/label/label.component.ts b/ui/src/app/ui/label/label.component.ts new file mode 100644 index 0000000..20b7300 --- /dev/null +++ b/ui/src/app/ui/label/label.component.ts @@ -0,0 +1,20 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'ui-label, label[uiLabel]', + standalone: true, + template: ``, + host: { + '[class]': 'computedClasses', + }, +}) +export class LabelComponent { + @Input() class = ''; + + private baseClasses = + 'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'; + + get computedClasses(): string { + return `${this.baseClasses} ${this.class}`.trim(); + } +} diff --git a/ui/src/app/ui/select/select.component.ts b/ui/src/app/ui/select/select.component.ts new file mode 100644 index 0000000..581f5d1 --- /dev/null +++ b/ui/src/app/ui/select/select.component.ts @@ -0,0 +1,121 @@ +import { Component, Input, Output, EventEmitter, forwardRef, signal } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; + +export interface SelectOption { + label: string; + value: string; +} + +@Component({ + selector: 'ui-select', + standalone: true, + template: ` +
+ + + @if (isOpen()) { +
+
+ @for (option of options; track option.value) { +
+ {{ option.label }} +
+ } +
+
+ } +
+ `, + host: { + '(document:click)': 'onDocumentClick($event)', + }, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => SelectComponent), + multi: true, + }, + ], +}) +export class SelectComponent implements ControlValueAccessor { + @Input() options: SelectOption[] = []; + @Input() placeholder = 'Select...'; + @Input() value: string = ''; + @Input() disabled = false; + @Input() class = ''; + @Output() valueChange = new EventEmitter(); + + isOpen = signal(false); + + private onChange: (value: string) => void = () => {}; + private onTouched: () => void = () => {}; + + get selectedLabel(): string { + const selected = this.options.find((o) => o.value === this.value); + return selected?.label || ''; + } + + get buttonClasses(): string { + return `flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 ${this.class}`.trim(); + } + + toggleOpen(): void { + if (this.disabled) return; + this.isOpen.update((v) => !v); + } + + selectOption(option: SelectOption): void { + this.value = option.value; + this.valueChange.emit(option.value); + this.onChange(option.value); + this.onTouched(); + this.isOpen.set(false); + } + + onDocumentClick(event: Event): void { + const target = event.target as HTMLElement; + if (!target.closest('ui-select')) { + this.isOpen.set(false); + } + } + + writeValue(value: string): void { + this.value = value; + } + + registerOnChange(fn: (value: string) => void): void { + this.onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + } +} diff --git a/ui/src/app/ui/separator/separator.component.ts b/ui/src/app/ui/separator/separator.component.ts new file mode 100644 index 0000000..b6f80f0 --- /dev/null +++ b/ui/src/app/ui/separator/separator.component.ts @@ -0,0 +1,21 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'ui-separator', + standalone: true, + template: ``, + host: { + '[class]': 'computedClasses', + role: 'separator', + }, +}) +export class SeparatorComponent { + @Input() orientation: 'horizontal' | 'vertical' = 'horizontal'; + @Input() class = ''; + + get computedClasses(): string { + const orientationClasses = + this.orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]'; + return `shrink-0 bg-border ${orientationClasses} ${this.class}`.trim(); + } +} diff --git a/ui/src/app/ui/skeleton/skeleton.component.ts b/ui/src/app/ui/skeleton/skeleton.component.ts new file mode 100644 index 0000000..c3d9579 --- /dev/null +++ b/ui/src/app/ui/skeleton/skeleton.component.ts @@ -0,0 +1,19 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'ui-skeleton', + standalone: true, + template: ``, + host: { + '[class]': 'computedClasses', + }, +}) +export class SkeletonComponent { + @Input() class = ''; + + private baseClasses = 'animate-pulse rounded-md bg-muted'; + + get computedClasses(): string { + return `${this.baseClasses} ${this.class}`.trim(); + } +} diff --git a/ui/src/app/ui/switch/switch.component.ts b/ui/src/app/ui/switch/switch.component.ts new file mode 100644 index 0000000..106d4bb --- /dev/null +++ b/ui/src/app/ui/switch/switch.component.ts @@ -0,0 +1,69 @@ +import { Component, Input, Output, EventEmitter, forwardRef } from '@angular/core'; +import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms'; + +@Component({ + selector: 'ui-switch', + standalone: true, + template: ` + + `, + providers: [ + { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => SwitchComponent), + multi: true, + }, + ], +}) +export class SwitchComponent implements ControlValueAccessor { + @Input() checked = false; + @Input() disabled = false; + @Input() class = ''; + @Output() checkedChange = new EventEmitter(); + + private onChange: (value: boolean) => void = () => {}; + private onTouched: () => void = () => {}; + + get buttonClasses(): string { + const stateClass = this.checked ? 'bg-primary' : 'bg-input'; + return `peer inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 ${stateClass} ${this.class}`.trim(); + } + + get thumbClasses(): string { + const translateClass = this.checked ? 'translate-x-5' : 'translate-x-0'; + return `pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform ${translateClass}`; + } + + toggle(): void { + if (this.disabled) return; + this.checked = !this.checked; + this.checkedChange.emit(this.checked); + this.onChange(this.checked); + this.onTouched(); + } + + writeValue(value: boolean): void { + this.checked = value; + } + + registerOnChange(fn: (value: boolean) => void): void { + this.onChange = fn; + } + + registerOnTouched(fn: () => void): void { + this.onTouched = fn; + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + } +} diff --git a/ui/src/app/ui/table/table.component.ts b/ui/src/app/ui/table/table.component.ts new file mode 100644 index 0000000..f7d3783 --- /dev/null +++ b/ui/src/app/ui/table/table.component.ts @@ -0,0 +1,148 @@ +import { Component, Input } from '@angular/core'; + +@Component({ + selector: 'ui-table', + standalone: true, + template: ``, + host: { + '[class]': 'computedClasses', + }, + styles: [` + :host { + display: table; + width: 100%; + border-collapse: collapse; + } + `], +}) +export class TableComponent { + @Input() class = ''; + + private baseClasses = 'w-full caption-bottom text-sm'; + + get computedClasses(): string { + return `${this.baseClasses} ${this.class}`.trim(); + } +} + +@Component({ + selector: 'ui-table-header', + standalone: true, + template: ``, + host: { + '[class]': 'computedClasses', + }, + styles: [':host { display: table-header-group; }'], +}) +export class TableHeaderComponent { + @Input() class = ''; + + private baseClasses = ''; + + get computedClasses(): string { + return `${this.baseClasses} ${this.class}`.trim(); + } +} + +@Component({ + selector: 'ui-table-body', + standalone: true, + template: ``, + host: { + '[class]': 'computedClasses', + }, + styles: [':host { display: table-row-group; }'], +}) +export class TableBodyComponent { + @Input() class = ''; + + private baseClasses = ''; + + get computedClasses(): string { + return `${this.baseClasses} ${this.class}`.trim(); + } +} + +@Component({ + selector: 'ui-table-row', + standalone: true, + template: ``, + host: { + '[class]': 'computedClasses', + }, + styles: [` + :host { + display: table-row; + } + :host:not(:last-child) { + border-bottom: 1px solid hsl(var(--border)); + } + :host:hover { + background-color: hsl(var(--muted) / 0.5); + } + `], +}) +export class TableRowComponent { + @Input() class = ''; + + private baseClasses = 'transition-colors'; + + get computedClasses(): string { + return `${this.baseClasses} ${this.class}`.trim(); + } +} + +@Component({ + selector: 'ui-table-head', + standalone: true, + template: ``, + host: { + '[class]': 'computedClasses', + }, + styles: [` + :host { + display: table-cell; + height: 3rem; + padding: 0 1rem; + text-align: left; + vertical-align: middle; + font-weight: 500; + color: hsl(var(--muted-foreground)); + border-bottom: 1px solid hsl(var(--border)); + } + `], +}) +export class TableHeadComponent { + @Input() class = ''; + + private baseClasses = ''; + + get computedClasses(): string { + return `${this.baseClasses} ${this.class}`.trim(); + } +} + +@Component({ + selector: 'ui-table-cell', + standalone: true, + template: ``, + host: { + '[class]': 'computedClasses', + }, + styles: [` + :host { + display: table-cell; + padding: 1rem; + vertical-align: middle; + } + `], +}) +export class TableCellComponent { + @Input() class = ''; + + private baseClasses = ''; + + get computedClasses(): string { + return `${this.baseClasses} ${this.class}`.trim(); + } +} diff --git a/ui/src/app/ui/toast/toast.component.ts b/ui/src/app/ui/toast/toast.component.ts new file mode 100644 index 0000000..6341e0d --- /dev/null +++ b/ui/src/app/ui/toast/toast.component.ts @@ -0,0 +1,66 @@ +import { Component, Input, Output, EventEmitter } from '@angular/core'; +import { IToast, ToastType } from '../../core/types/api.types'; + +@Component({ + selector: 'ui-toast', + standalone: true, + template: ` +
+
+
+ @switch (toast.type) { + @case ('success') { + + + + } + @case ('error') { + + + + } + @case ('warning') { + + + + } + @case ('info') { + + + + } + } +
+

{{ toast.message }}

+ +
+
+ `, +}) +export class ToastComponent { + @Input({ required: true }) toast!: IToast; + @Output() dismiss = new EventEmitter(); + + private variantClasses: Record = { + success: 'border-success/50 bg-success/10 text-success', + error: 'border-destructive/50 bg-destructive/10 text-destructive', + warning: 'border-warning/50 bg-warning/10 text-warning', + info: 'border-primary/50 bg-primary/10 text-primary', + }; + + get computedClasses(): string { + return `pointer-events-auto w-full max-w-sm rounded-lg border p-4 shadow-lg animate-slide-in-right ${this.variantClasses[this.toast.type]}`; + } + + get iconContainerClasses(): string { + return 'flex-shrink-0'; + } +} diff --git a/ui/src/app/ui/toast/toaster.component.ts b/ui/src/app/ui/toast/toaster.component.ts new file mode 100644 index 0000000..3b62de0 --- /dev/null +++ b/ui/src/app/ui/toast/toaster.component.ts @@ -0,0 +1,19 @@ +import { Component, inject } from '@angular/core'; +import { ToastService } from '../../core/services/toast.service'; +import { ToastComponent } from './toast.component'; + +@Component({ + selector: 'ui-toaster', + standalone: true, + imports: [ToastComponent], + template: ` +
+ @for (toast of toastService.toasts(); track toast.id) { + + } +
+ `, +}) +export class ToasterComponent { + toastService = inject(ToastService); +} diff --git a/ui/src/favicon.ico b/ui/src/favicon.ico deleted file mode 100644 index 37727f4..0000000 --- a/ui/src/favicon.ico +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/ui/src/index.html b/ui/src/index.html index 2b9eace..8b04055 100644 --- a/ui/src/index.html +++ b/ui/src/index.html @@ -2,12 +2,20 @@ - Onebox - Container Platform + Onebox + + + + - + diff --git a/ui/src/main.ts b/ui/src/main.ts index 2409287..35b00f3 100644 --- a/ui/src/main.ts +++ b/ui/src/main.ts @@ -1,13 +1,6 @@ import { bootstrapApplication } from '@angular/platform-browser'; -import { provideRouter } from '@angular/router'; -import { provideHttpClient, withInterceptors } from '@angular/common/http'; +import { appConfig } from './app/app.config'; import { AppComponent } from './app/app.component'; -import { routes } from './app/app.routes'; -import { authInterceptor } from './app/core/interceptors/auth.interceptor'; -bootstrapApplication(AppComponent, { - providers: [ - provideRouter(routes), - provideHttpClient(withInterceptors([authInterceptor])), - ], -}).catch((err) => console.error(err)); +bootstrapApplication(AppComponent, appConfig) + .catch((err) => console.error(err)); diff --git a/ui/src/styles.css b/ui/src/styles.css index a4f3c8d..c5b891b 100644 --- a/ui/src/styles.css +++ b/ui/src/styles.css @@ -2,56 +2,91 @@ @tailwind components; @tailwind utilities; -@layer components { - .btn { - @apply px-4 py-2 rounded-lg font-medium transition-colors duration-200; +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 222.2 84% 4.9%; + --card: 0 0% 100%; + --card-foreground: 222.2 84% 4.9%; + --popover: 0 0% 100%; + --popover-foreground: 222.2 84% 4.9%; + --primary: 221.2 83.2% 53.3%; + --primary-foreground: 210 40% 98%; + --secondary: 210 40% 96.1%; + --secondary-foreground: 222.2 47.4% 11.2%; + --muted: 210 40% 96.1%; + --muted-foreground: 215.4 16.3% 46.9%; + --accent: 210 40% 96.1%; + --accent-foreground: 222.2 47.4% 11.2%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 210 40% 98%; + --success: 142.1 76.2% 36.3%; + --success-foreground: 355.7 100% 97.3%; + --warning: 38 92% 50%; + --warning-foreground: 48 96% 89%; + --border: 214.3 31.8% 91.4%; + --input: 214.3 31.8% 91.4%; + --ring: 221.2 83.2% 53.3%; + --radius: 0.5rem; } - .btn-primary { - @apply bg-primary-600 text-white hover:bg-primary-700; - } - - .btn-secondary { - @apply bg-gray-200 text-gray-800 hover:bg-gray-300; - } - - .btn-danger { - @apply bg-red-600 text-white hover:bg-red-700; - } - - .btn-success { - @apply bg-green-600 text-white hover:bg-green-700; - } - - .card { - @apply bg-white rounded-lg shadow-md p-6; - } - - .input { - @apply w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500; - } - - .label { - @apply block text-sm font-medium text-gray-700 mb-1; - } - - .badge { - @apply px-2 py-1 text-xs font-semibold rounded-full; - } - - .badge-success { - @apply bg-green-100 text-green-800; - } - - .badge-warning { - @apply bg-yellow-100 text-yellow-800; - } - - .badge-danger { - @apply bg-red-100 text-red-800; - } - - .badge-info { - @apply bg-blue-100 text-blue-800; + .dark { + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + --primary: 217.2 91.2% 59.8%; + --primary-foreground: 222.2 47.4% 11.2%; + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + --accent: 217.2 32.6% 17.5%; + --accent-foreground: 210 40% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + --success: 142.1 70.6% 45.3%; + --success-foreground: 144.9 80.4% 10%; + --warning: 48 96% 53%; + --warning-foreground: 36 45% 15%; + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + --ring: 224.3 76.3% 48%; + } +} + +@layer base { + * { + @apply border-border; + } + + body { + @apply bg-background text-foreground; + font-feature-settings: "rlig" 1, "calt" 1; + } +} + +@layer utilities { + .scrollbar-thin { + scrollbar-width: thin; + } + + .scrollbar-thin::-webkit-scrollbar { + width: 8px; + height: 8px; + } + + .scrollbar-thin::-webkit-scrollbar-track { + @apply bg-muted; + } + + .scrollbar-thin::-webkit-scrollbar-thumb { + @apply bg-muted-foreground/30 rounded-full; + } + + .scrollbar-thin::-webkit-scrollbar-thumb:hover { + @apply bg-muted-foreground/50; } } diff --git a/ui/tailwind.config.js b/ui/tailwind.config.js index a117091..ae1cb6e 100644 --- a/ui/tailwind.config.js +++ b/ui/tailwind.config.js @@ -1,25 +1,97 @@ /** @type {import('tailwindcss').Config} */ module.exports = { - content: [ - "./src/**/*.{html,ts}", - ], + darkMode: ['class'], + content: ['./src/**/*.{html,ts}'], theme: { extend: { colors: { + border: 'hsl(var(--border))', + input: 'hsl(var(--input))', + ring: 'hsl(var(--ring))', + background: 'hsl(var(--background))', + foreground: 'hsl(var(--foreground))', primary: { - 50: '#f0f9ff', - 100: '#e0f2fe', - 200: '#bae6fd', - 300: '#7dd3fc', - 400: '#38bdf8', - 500: '#0ea5e9', - 600: '#0284c7', - 700: '#0369a1', - 800: '#075985', - 900: '#0c4a6e', + DEFAULT: 'hsl(var(--primary))', + foreground: 'hsl(var(--primary-foreground))', }, + secondary: { + DEFAULT: 'hsl(var(--secondary))', + foreground: 'hsl(var(--secondary-foreground))', + }, + destructive: { + DEFAULT: 'hsl(var(--destructive))', + foreground: 'hsl(var(--destructive-foreground))', + }, + success: { + DEFAULT: 'hsl(var(--success))', + foreground: 'hsl(var(--success-foreground))', + }, + warning: { + DEFAULT: 'hsl(var(--warning))', + foreground: 'hsl(var(--warning-foreground))', + }, + muted: { + DEFAULT: 'hsl(var(--muted))', + foreground: 'hsl(var(--muted-foreground))', + }, + accent: { + DEFAULT: 'hsl(var(--accent))', + foreground: 'hsl(var(--accent-foreground))', + }, + popover: { + DEFAULT: 'hsl(var(--popover))', + foreground: 'hsl(var(--popover-foreground))', + }, + card: { + DEFAULT: 'hsl(var(--card))', + foreground: 'hsl(var(--card-foreground))', + }, + }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)', + }, + keyframes: { + 'fade-in': { + from: { opacity: '0' }, + to: { opacity: '1' }, + }, + 'fade-out': { + from: { opacity: '1' }, + to: { opacity: '0' }, + }, + 'slide-in-right': { + from: { transform: 'translateX(100%)' }, + to: { transform: 'translateX(0)' }, + }, + 'slide-out-right': { + from: { transform: 'translateX(0)' }, + to: { transform: 'translateX(100%)' }, + }, + 'slide-in-bottom': { + from: { transform: 'translateY(100%)' }, + to: { transform: 'translateY(0)' }, + }, + 'accordion-down': { + from: { height: '0' }, + to: { height: 'var(--radix-accordion-content-height)' }, + }, + 'accordion-up': { + from: { height: 'var(--radix-accordion-content-height)' }, + to: { height: '0' }, + }, + }, + animation: { + 'fade-in': 'fade-in 150ms ease-out', + 'fade-out': 'fade-out 150ms ease-in', + 'slide-in-right': 'slide-in-right 200ms ease-out', + 'slide-out-right': 'slide-out-right 200ms ease-in', + 'slide-in-bottom': 'slide-in-bottom 200ms ease-out', + 'accordion-down': 'accordion-down 200ms ease-out', + 'accordion-up': 'accordion-up 200ms ease-out', }, }, }, plugins: [], -} +}; diff --git a/ui/tsconfig.app.json b/ui/tsconfig.app.json index 7d7c716..3775b37 100644 --- a/ui/tsconfig.app.json +++ b/ui/tsconfig.app.json @@ -1,3 +1,5 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ { "extends": "./tsconfig.json", "compilerOptions": { diff --git a/ui/tsconfig.json b/ui/tsconfig.json index aeed08f..5525117 100644 --- a/ui/tsconfig.json +++ b/ui/tsconfig.json @@ -1,26 +1,22 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ { "compileOnSave": false, "compilerOptions": { "outDir": "./dist/out-tsc", - "forceConsistentCasingInFileNames": true, "strict": true, "noImplicitOverride": true, "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, + "skipLibCheck": true, + "isolatedModules": true, "esModuleInterop": true, - "sourceMap": true, - "declaration": false, "experimentalDecorators": true, - "moduleResolution": "node", + "moduleResolution": "bundler", "importHelpers": true, "target": "ES2022", - "module": "ES2022", - "useDefineForClassFields": false, - "lib": [ - "ES2022", - "dom" - ] + "module": "ES2022" }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, diff --git a/ui/tsconfig.spec.json b/ui/tsconfig.spec.json new file mode 100644 index 0000000..5fb748d --- /dev/null +++ b/ui/tsconfig.spec.json @@ -0,0 +1,15 @@ +/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */ +/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./out-tsc/spec", + "types": [ + "jasmine" + ] + }, + "include": [ + "src/**/*.spec.ts", + "src/**/*.d.ts" + ] +}