From 3b4a841bf52e272c34c6b954343ff84cdf105f4c Mon Sep 17 00:00:00 2001 From: Blomios Date: Mon, 18 May 2026 08:24:02 +0200 Subject: [PATCH] feat: add first page with auth and containers list and agents --- .env.example | 9 + .gitignore | 39 + CLAUDE.md | 41 + Makefile | 44 + agent/Cargo.lock | 2318 +++++++ agent/Cargo.toml | 29 + agent/Dockerfile | 36 + agent/build.rs | 6 + agent/src/docker.rs | 385 ++ agent/src/main.rs | 434 ++ docker-compose.agent.yml | 13 + docker-compose.server.yml | 38 + package-lock.json | 6 + proto/agent/v1/agent.proto | 100 + server/Dockerfile | 20 + server/cmd/server/main.go | 139 + server/go.mod | 21 + server/go.sum | 26 + server/internal/api/api_test.go | 550 ++ server/internal/api/auth.go | 125 + server/internal/api/context.go | 16 + server/internal/api/handlers.go | 301 + server/internal/api/router.go | 35 + server/internal/auth/auth.go | 49 + server/internal/auth/auth_test.go | 64 + server/internal/broker/broker.go | 55 + server/internal/broker/broker_test.go | 123 + server/internal/grpc/gateway.go | 137 + server/internal/grpc/registry.go | 105 + server/internal/grpc/registry_test.go | 155 + server/internal/store/store.go | 183 + server/internal/store/store_test.go | 208 + web/package-lock.json | 8613 +++++++++++++++++++++++++ web/package.json | 33 + web/postcss.config.js | 6 + web/src/app.css | 86 + web/src/app.html | 15 + web/src/lib/LogModal.svelte | 121 + web/src/lib/api.ts | 138 + web/src/lib/auth.ts | 35 + web/src/routes/+layout.svelte | 19 + web/src/routes/+layout.ts | 2 + web/src/routes/+page.svelte | 351 + web/src/routes/admin/+page.svelte | 206 + web/src/routes/login/+page.svelte | 94 + web/src/tests/LogModal.test.ts | 147 + web/src/tests/api.test.ts | 296 + web/src/tests/auth.test.ts | 120 + web/src/tests/setup.ts | 1 + web/src/tests/stripAnsi.test.ts | 54 + web/static/icon-192.png | Bin 0 -> 8219 bytes web/static/icon-512.png | Bin 0 -> 212032 bytes web/svelte.config.js | 14 + web/tailwind.config.js | 46 + web/tsconfig.json | 13 + web/vite.config.ts | 47 + 56 files changed, 16267 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 CLAUDE.md create mode 100644 Makefile create mode 100644 agent/Cargo.lock create mode 100644 agent/Cargo.toml create mode 100644 agent/Dockerfile create mode 100644 agent/build.rs create mode 100644 agent/src/docker.rs create mode 100644 agent/src/main.rs create mode 100644 docker-compose.agent.yml create mode 100644 docker-compose.server.yml create mode 100644 package-lock.json create mode 100644 proto/agent/v1/agent.proto create mode 100644 server/Dockerfile create mode 100644 server/cmd/server/main.go create mode 100644 server/go.mod create mode 100644 server/go.sum create mode 100644 server/internal/api/api_test.go create mode 100644 server/internal/api/auth.go create mode 100644 server/internal/api/context.go create mode 100644 server/internal/api/handlers.go create mode 100644 server/internal/api/router.go create mode 100644 server/internal/auth/auth.go create mode 100644 server/internal/auth/auth_test.go create mode 100644 server/internal/broker/broker.go create mode 100644 server/internal/broker/broker_test.go create mode 100644 server/internal/grpc/gateway.go create mode 100644 server/internal/grpc/registry.go create mode 100644 server/internal/grpc/registry_test.go create mode 100644 server/internal/store/store.go create mode 100644 server/internal/store/store_test.go create mode 100644 web/package-lock.json create mode 100644 web/package.json create mode 100644 web/postcss.config.js create mode 100644 web/src/app.css create mode 100644 web/src/app.html create mode 100644 web/src/lib/LogModal.svelte create mode 100644 web/src/lib/api.ts create mode 100644 web/src/lib/auth.ts create mode 100644 web/src/routes/+layout.svelte create mode 100644 web/src/routes/+layout.ts create mode 100644 web/src/routes/+page.svelte create mode 100644 web/src/routes/admin/+page.svelte create mode 100644 web/src/routes/login/+page.svelte create mode 100644 web/src/tests/LogModal.test.ts create mode 100644 web/src/tests/api.test.ts create mode 100644 web/src/tests/auth.test.ts create mode 100644 web/src/tests/setup.ts create mode 100644 web/src/tests/stripAnsi.test.ts create mode 100644 web/static/icon-192.png create mode 100644 web/static/icon-512.png create mode 100644 web/svelte.config.js create mode 100644 web/tailwind.config.js create mode 100644 web/tsconfig.json create mode 100644 web/vite.config.ts diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..e4a1e84 --- /dev/null +++ b/.env.example @@ -0,0 +1,9 @@ +# Server +JWT_SECRET=change-me-to-a-random-secret + +# Token for the local agent (generate with: uuidgen) +LOCAL_AGENT_TOKEN=change-me-to-a-random-token + +# Remote agents only (docker-compose.agent.yml) +CONTAINARR_SERVER_URL=http://:9090 +CONTAINARR_AGENT_TOKEN= diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..76a4e7e --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +# ── Rust (agent/) ───────────────────────────────────────────────────────────── +agent/target/ + +# ── Go (server/) ────────────────────────────────────────────────────────────── +server/vendor/ +server/*.test +server/*.out + +# Generated protobuf (rebuilt with `make proto`) +server/internal/proto/ + +# ── SvelteKit (web/) ────────────────────────────────────────────────────────── +web/node_modules/ +web/.svelte-kit/ +web/build/ +web/dist/ +web/.env +web/.env.* + +# ── Docker ──────────────────────────────────────────────────────────────────── +*.tar + +# ── Données & secrets ───────────────────────────────────────────────────────── +*.db +*.db-wal +*.db-shm +.env +.env.* +!.env.example + +# ── OS ──────────────────────────────────────────────────────────────────────── +.DS_Store +Thumbs.db + +# ── Éditeurs ────────────────────────────────────────────────────────────────── +.idea/ +.vscode/ +*.swp +*.swo diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..d96832c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,41 @@ +# Containarr — Guide Claude Code + +## Rôle de Claude + +Claude est **lead tech** sur ce projet. Il ne code pas directement — il planifie, répartit les tâches entre agents spécialisés, et valide les résultats. + +## Agents spécialisés + +| Agent | Domaine | Répertoire | +|-------|---------|-----------| +| `containarr-rust-agent` | Rust · bollard · tonic/gRPC · tokio | `agent/` | +| `containarr-go-server` | Go · Chi · gRPC gateway · SQLite · JWT | `server/` | +| `containarr-svelte-ui` | SvelteKit · Svelte 5 runes · Tailwind · TypeScript | `web/` | + +## Règles strictes + +1. **Jamais de code Rust direct** → déléguer à `containarr-rust-agent` +2. **Jamais de code Go direct** → déléguer à `containarr-go-server` +3. **Jamais de code SvelteKit/TS direct** → déléguer à `containarr-svelte-ui` +4. **Tests obligatoires** : après chaque feature, l'agent doit écrire/mettre à jour les tests et les lancer +5. **Validation** : Claude vérifie le résultat avant de clore une tâche + +## Stack + +- `agent/` — Rust (bollard + tonic gRPC) : agent Docker, snapshots toutes les 10s, actions + log streaming +- `server/` — Go (Chi + gRPC + SQLite) : tunnel gRPC, API REST, JWT auth, broker WebSocket +- `web/` — SvelteKit + Tailwind (thème "abyss" sombre) : dashboard par host, admin, log viewer +- `proto/agent/v1/agent.proto` — tunnel bidirectionnel AgentMessage / ServerMessage + +## Commandes clés + +```bash +make proto # Regénérer le code Go depuis le .proto +make server # Build Go +make agent # Build Rust (release) +make web # Build SvelteKit +make dev-server # Lancer le serveur en dev (go run) +make dev-web # Lancer le frontend en dev (vite) +make up-server # Docker Compose serveur +make up-agent # Docker Compose agent +``` diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..09862b1 --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +.PHONY: proto server agent web dev up-server up-agent + +PROTO_DIR := proto/agent/v1 +PROTO_OUT := server/internal/proto/agentv1 +export PATH := $(HOME)/go/bin:/usr/local/go/bin:$(PATH) + +# ── Protobuf codegen (Go side) ──────────────────────────────────────────────── +proto: + mkdir -p $(PROTO_OUT) + protoc \ + --go_out=server --go_opt=module=github.com/containarr/server \ + --go-grpc_out=server --go-grpc_opt=module=github.com/containarr/server \ + -I proto \ + $(PROTO_DIR)/agent.proto + +# ── Build ───────────────────────────────────────────────────────────────────── +server: + cd server && go build ./... + +agent: + cd agent && cargo build --release + +web: + cd web && npm run build + +# ── Docker ──────────────────────────────────────────────────────────────────── +up-server: + docker compose -f docker-compose.server.yml up --build -d + +up-agent: + docker compose -f docker-compose.agent.yml up --build -d + +down-server: + docker compose -f docker-compose.server.yml down + +down-agent: + docker compose -f docker-compose.agent.yml down + +# ── Dev (local, no Docker) ─────────────────────────────────────────────────── +dev-server: + cd server && go run ./cmd/server + +dev-web: + cd web && npm run dev diff --git a/agent/Cargo.lock b/agent/Cargo.lock new file mode 100644 index 0000000..fde7e53 --- /dev/null +++ b/agent/Cargo.lock @@ -0,0 +1,2318 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "axum" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper", + "tower 0.5.3", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + +[[package]] +name = "bollard" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d41711ad46fda47cd701f6908e59d1bd6b9a2b7464c0d0aeab95c6d37096ff8a" +dependencies = [ + "base64", + "bollard-stubs", + "bytes", + "futures-core", + "futures-util", + "hex", + "http", + "http-body-util", + "hyper", + "hyper-named-pipe", + "hyper-util", + "hyperlocal", + "log", + "pin-project-lite", + "serde", + "serde_derive", + "serde_json", + "serde_repr", + "serde_urlencoded", + "thiserror", + "tokio", + "tokio-util", + "tower-service", + "url", + "winapi", +] + +[[package]] +name = "bollard-stubs" +version = "1.45.0-rc.26.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7c5415e3a6bc6d3e99eff6268e488fd4ee25e7b28c10f08fa6760bd9de16e4" +dependencies = [ + "serde", + "serde_repr", + "serde_with", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cc" +version = "1.2.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link", +] + +[[package]] +name = "containarr-agent" +version = "0.1.0" +dependencies = [ + "anyhow", + "bollard", + "bytes", + "futures-util", + "prost", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tonic", + "tonic-build", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", + "serde_core", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-macro", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "h2" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.14.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-named-pipe" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278" +dependencies = [ + "hex", + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", + "winapi", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "libc", + "pin-project-lite", + "socket2 0.6.3", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "hyperlocal" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "986c5ce3b994526b3cd75578e62554abd09f0899d6206de48b3e96ab34ccc8c7" +dependencies = [ + "hex", + "http-body-util", + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "matchers" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" +dependencies = [ + "regex-automata", +] + +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-conv" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap 2.14.0", +] + +[[package]] +name = "pin-project" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" +dependencies = [ + "heck", + "itertools", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +dependencies = [ + "prost", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e72c1c2cb7b223fafb600a619537a871c2818583d619401b785e7c0b746ccde2" +dependencies = [ + "base64", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.0", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "time", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.3", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tonic" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" +dependencies = [ + "async-stream", + "async-trait", + "axum", + "base64", + "bytes", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "prost", + "rustls-pemfile", + "socket2 0.5.10", + "tokio", + "tokio-rustls", + "tokio-stream", + "tower 0.4.13", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9557ce109ea773b399c9b9e5dca39294110b74f1f342cb347a80d1fce8c26a11" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "prost-types", + "quote", + "syn", +] + +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "indexmap 1.9.3", + "pin-project", + "pin-project-lite", + "rand", + "slab", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap 2.14.0", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap 2.14.0", + "semver", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap 2.14.0", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap 2.14.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.14.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "yoke" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/agent/Cargo.toml b/agent/Cargo.toml new file mode 100644 index 0000000..2b7cad5 --- /dev/null +++ b/agent/Cargo.toml @@ -0,0 +1,29 @@ +[package] +name = "containarr-agent" +version = "0.1.0" +edition = "2021" +rust-version = "1.88" + +[[bin]] +name = "containarr-agent" +path = "src/main.rs" + +[dependencies] +tokio = { version = "1", features = ["full"] } +tonic = { version = "0.12", features = ["tls"] } +prost = "0.13" +bollard = "0.17" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] } +anyhow = "1" +tokio-stream = "0.1" +futures-util = "0.3" + +[dev-dependencies] +# `bytes::Bytes` is used in MockBackend::logs() to construct LogOutput variants +bytes = "1" + +[build-dependencies] +tonic-build = "0.12" diff --git a/agent/Dockerfile b/agent/Dockerfile new file mode 100644 index 0000000..603e437 --- /dev/null +++ b/agent/Dockerfile @@ -0,0 +1,36 @@ +# Context: project root (needed so build.rs can access proto/) +FROM rust:slim AS builder + +RUN apt-get update && apt-get install -y --no-install-recommends \ + protobuf-compiler \ + pkg-config \ + libssl-dev \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /src + +# Copy proto first (referenced by build.rs as ../proto/...) +COPY proto/ ./proto/ + +# Cache dependencies: copy manifests, build with dummy main, then discard. +COPY agent/Cargo.toml agent/Cargo.lock ./agent/ +COPY agent/build.rs ./agent/ +WORKDIR /src/agent +RUN mkdir src && echo "fn main(){}" > src/main.rs && \ + cargo build --release && \ + rm -rf src + +# Full build. +COPY agent/src ./src +RUN touch src/main.rs && cargo build --release + +# ── Runtime ─────────────────────────────────────────────────────────────────── +FROM debian:bookworm-slim + +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +COPY --from=builder /src/agent/target/release/containarr-agent /usr/local/bin/containarr-agent + +ENTRYPOINT ["containarr-agent"] diff --git a/agent/build.rs b/agent/build.rs new file mode 100644 index 0000000..5d75adc --- /dev/null +++ b/agent/build.rs @@ -0,0 +1,6 @@ +fn main() -> Result<(), Box> { + tonic_build::configure() + .build_server(false) + .compile_protos(&["../proto/agent/v1/agent.proto"], &["../proto"])?; + Ok(()) +} diff --git a/agent/src/docker.rs b/agent/src/docker.rs new file mode 100644 index 0000000..27d19fa --- /dev/null +++ b/agent/src/docker.rs @@ -0,0 +1,385 @@ +use anyhow::Result; +use bollard::{ + container::{ + ListContainersOptions, LogOutput, LogsOptions, RemoveContainerOptions, + StartContainerOptions, StopContainerOptions, + }, + Docker, +}; +use futures_util::Stream; +use std::{collections::HashMap, pin::Pin}; + +use crate::proto::{ContainerInfo, ContainerPort}; + +// ── Public trait ───────────────────────────────────────────────────────────── + +/// Abstraction over Docker operations, allowing tests to provide a mock backend. +pub trait ContainerBackend: Clone + Send + Sync + 'static { + fn list_containers( + &self, + ) -> impl std::future::Future>> + Send; + + fn start(&self, id: &str) -> impl std::future::Future> + Send; + fn stop(&self, id: &str) -> impl std::future::Future> + Send; + fn restart(&self, id: &str) -> impl std::future::Future> + Send; + fn remove(&self, id: &str) -> impl std::future::Future> + Send; + + fn logs( + &self, + id: &str, + follow: bool, + tail: i32, + ) -> Pin> + Send>>; +} + +// ── Real implementation ─────────────────────────────────────────────────────── + +#[derive(Clone)] +pub struct DockerClient { + inner: Docker, +} + +impl DockerClient { + pub fn new() -> Result { + Ok(Self { + inner: Docker::connect_with_socket_defaults()?, + }) + } +} + +impl ContainerBackend for DockerClient { + async fn list_containers(&self) -> Result> { + let opts = ListContainersOptions:: { + all: true, + ..Default::default() + }; + let containers = self.inner.list_containers(Some(opts)).await?; + + let result = containers + .into_iter() + .map(|c| { + let id = c.id.unwrap_or_default(); + let name = c + .names + .unwrap_or_default() + .into_iter() + .next() + .unwrap_or_default() + .trim_start_matches('/') + .to_string(); + + let ports = c + .ports + .unwrap_or_default() + .into_iter() + .map(|p| ContainerPort { + host_port: p.public_port.unwrap_or(0) as u32, + container_port: p.private_port as u32, + protocol: p + .typ + .map(|t| format!("{:?}", t).to_lowercase()) + .unwrap_or_default(), + host_ip: p.ip.unwrap_or_default(), + }) + .collect(); + + let labels: HashMap = c.labels.unwrap_or_default(); + let compose_project = labels + .get("com.docker.compose.project") + .cloned() + .unwrap_or_default(); + + ContainerInfo { + id, + name, + image: c.image.unwrap_or_default(), + status: c.status.unwrap_or_default(), + state: c.state.unwrap_or_default(), + ports, + created_at: c.created.unwrap_or(0), + labels, + compose_project, + } + }) + .collect(); + + Ok(result) + } + + async fn start(&self, id: &str) -> Result<()> { + self.inner + .start_container(id, None::>) + .await?; + Ok(()) + } + + async fn stop(&self, id: &str) -> Result<()> { + self.inner + .stop_container(id, Some(StopContainerOptions { t: 10 })) + .await?; + Ok(()) + } + + async fn restart(&self, id: &str) -> Result<()> { + self.inner.restart_container(id, None).await?; + Ok(()) + } + + async fn remove(&self, id: &str) -> Result<()> { + self.inner + .remove_container( + id, + Some(RemoveContainerOptions { + force: true, + ..Default::default() + }), + ) + .await?; + Ok(()) + } + + fn logs( + &self, + id: &str, + follow: bool, + tail: i32, + ) -> Pin> + Send>> { + let tail_str = if tail > 0 { + tail.to_string() + } else { + "100".to_string() + }; + Box::pin(self.inner.logs( + id, + Some(LogsOptions:: { + stdout: true, + stderr: true, + follow, + tail: tail_str, + timestamps: false, + ..Default::default() + }), + )) + } +} + +// ── Tests ───────────────────────────────────────────────────────────────────── + +#[cfg(test)] +pub mod tests { + use super::*; + use bytes::Bytes; + use futures_util::StreamExt; + use std::sync::{Arc, Mutex}; + use tokio_stream::once as stream_once; + + // ── Minimal mock backend ────────────────────────────────────────────────── + + /// Records which method was last called and with what container id, so + /// tests can assert on behaviour without a real Docker daemon. + #[derive(Clone, Default)] + pub struct MockBackend { + pub calls: Arc>>, + /// When Some(msg) every async method returns Err(anyhow!(msg)). + pub fail_with: Option, + } + + impl MockBackend { + pub fn new() -> Self { + Self::default() + } + + pub fn failing(msg: &str) -> Self { + Self { + calls: Default::default(), + fail_with: Some(msg.to_owned()), + } + } + + fn record(&self, entry: String) { + self.calls.lock().unwrap().push(entry); + } + + fn maybe_err(&self) -> Result<()> { + if let Some(ref m) = self.fail_with { + anyhow::bail!("{}", m); + } + Ok(()) + } + } + + impl ContainerBackend for MockBackend { + async fn list_containers(&self) -> Result> { + self.record("list".to_string()); + self.maybe_err()?; + Ok(vec![ContainerInfo { + id: "abc123".to_string(), + name: "test-container".to_string(), + image: "nginx:latest".to_string(), + status: "Up 2 hours".to_string(), + state: "running".to_string(), + ports: vec![ContainerPort { + host_port: 8080, + container_port: 80, + protocol: "tcp".to_string(), + host_ip: "0.0.0.0".to_string(), + }], + created_at: 1_700_000_000, + labels: HashMap::new(), + compose_project: String::new(), + }]) + } + + async fn start(&self, id: &str) -> Result<()> { + self.record(format!("start:{id}")); + self.maybe_err() + } + + async fn stop(&self, id: &str) -> Result<()> { + self.record(format!("stop:{id}")); + self.maybe_err() + } + + async fn restart(&self, id: &str) -> Result<()> { + self.record(format!("restart:{id}")); + self.maybe_err() + } + + async fn remove(&self, id: &str) -> Result<()> { + self.record(format!("remove:{id}")); + self.maybe_err() + } + + fn logs( + &self, + id: &str, + _follow: bool, + _tail: i32, + ) -> Pin> + Send>> + { + self.record(format!("logs:{id}")); + let chunk = LogOutput::StdOut { + message: Bytes::from("hello from mock\n"), + }; + Box::pin(stream_once(Ok(chunk))) + } + } + + // ── list_containers ─────────────────────────────────────────────────────── + + #[tokio::test] + async fn mock_list_containers_returns_one_entry() { + let backend = MockBackend::new(); + let containers = backend.list_containers().await.unwrap(); + assert_eq!(containers.len(), 1); + assert_eq!(containers[0].id, "abc123"); + assert_eq!(containers[0].name, "test-container"); + } + + #[tokio::test] + async fn mock_list_containers_records_call() { + let backend = MockBackend::new(); + backend.list_containers().await.unwrap(); + let calls = backend.calls.lock().unwrap().clone(); + assert_eq!(calls, vec!["list"]); + } + + #[tokio::test] + async fn mock_list_containers_propagates_error() { + let backend = MockBackend::failing("docker down"); + let err = backend.list_containers().await.unwrap_err(); + assert!(err.to_string().contains("docker down")); + } + + // ── start / stop / restart / remove ────────────────────────────────────── + + #[tokio::test] + async fn mock_start_records_id() { + let backend = MockBackend::new(); + backend.start("cid-1").await.unwrap(); + assert_eq!(*backend.calls.lock().unwrap(), vec!["start:cid-1"]); + } + + #[tokio::test] + async fn mock_stop_records_id() { + let backend = MockBackend::new(); + backend.stop("cid-2").await.unwrap(); + assert_eq!(*backend.calls.lock().unwrap(), vec!["stop:cid-2"]); + } + + #[tokio::test] + async fn mock_restart_records_id() { + let backend = MockBackend::new(); + backend.restart("cid-3").await.unwrap(); + assert_eq!(*backend.calls.lock().unwrap(), vec!["restart:cid-3"]); + } + + #[tokio::test] + async fn mock_remove_records_id() { + let backend = MockBackend::new(); + backend.remove("cid-4").await.unwrap(); + assert_eq!(*backend.calls.lock().unwrap(), vec!["remove:cid-4"]); + } + + #[tokio::test] + async fn mock_operations_propagate_errors() { + let backend = MockBackend::failing("socket gone"); + assert!(backend.start("x").await.is_err()); + assert!(backend.stop("x").await.is_err()); + assert!(backend.restart("x").await.is_err()); + assert!(backend.remove("x").await.is_err()); + } + + // ── logs stream ────────────────────────────────────────────────────────── + + #[tokio::test] + async fn mock_logs_yields_stdout_chunk() { + let backend = MockBackend::new(); + let mut stream = backend.logs("cid-5", false, 10); + let item = stream.next().await.unwrap().unwrap(); + match item { + LogOutput::StdOut { message } => { + assert_eq!(message.as_ref(), b"hello from mock\n"); + } + other => panic!("unexpected variant: {:?}", other), + } + } + + #[tokio::test] + async fn mock_logs_records_id() { + let backend = MockBackend::new(); + let mut stream = backend.logs("cid-5", false, 10); + // drain the stream + while stream.next().await.is_some() {} + assert_eq!(*backend.calls.lock().unwrap(), vec!["logs:cid-5"]); + } + + // ── ContainerInfo / ContainerPort field mapping ─────────────────────────── + + #[test] + fn container_info_fields_are_accessible() { + let port = ContainerPort { + host_port: 443, + container_port: 8443, + protocol: "tcp".to_string(), + host_ip: "127.0.0.1".to_string(), + }; + assert_eq!(port.host_port, 443); + assert_eq!(port.container_port, 8443); + assert_eq!(port.protocol, "tcp"); + + let info = ContainerInfo { + id: "id1".to_string(), + name: "name1".to_string(), + image: "img".to_string(), + status: "running".to_string(), + state: "running".to_string(), + ports: vec![port], + created_at: 42, + labels: HashMap::new(), + compose_project: "proj".to_string(), + }; + assert_eq!(info.ports.len(), 1); + assert_eq!(info.compose_project, "proj"); + } +} diff --git a/agent/src/main.rs b/agent/src/main.rs new file mode 100644 index 0000000..575abea --- /dev/null +++ b/agent/src/main.rs @@ -0,0 +1,434 @@ +mod docker; + +pub mod proto { + tonic::include_proto!("containarr.agent.v1"); +} + +use anyhow::{Context, Result}; +use bollard::container::LogOutput; +use docker::{ContainerBackend, DockerClient}; +use futures_util::StreamExt as _; +use proto::{ + agent_gateway_client::AgentGatewayClient, + agent_message, server_message, + AgentHandshake, AgentMessage, ContainerAction, ContainerSnapshot, +}; +use std::{collections::HashMap, env, time::Duration}; +use tokio::{sync::mpsc, task::JoinHandle, time}; +use tonic::Request; +use tracing::{error, info, warn}; + +const SNAPSHOT_INTERVAL: Duration = Duration::from_secs(10); +const RECONNECT_DELAY: Duration = Duration::from_secs(5); + +#[tokio::main] +async fn main() -> Result<()> { + tracing_subscriber::fmt() + .json() + .with_env_filter( + tracing_subscriber::EnvFilter::try_from_default_env() + .unwrap_or_else(|_| "info".into()), + ) + .init(); + + let server_url = env::var("CONTAINARR_SERVER_URL") + .context("CONTAINARR_SERVER_URL not set")?; + let token = env::var("CONTAINARR_AGENT_TOKEN") + .context("CONTAINARR_AGENT_TOKEN not set")?; + let hostname = env::var("HOSTNAME").unwrap_or_else(|_| "unknown".into()); + + let docker = DockerClient::new().context("connect to Docker socket")?; + + loop { + if let Err(e) = run(&server_url, &token, &hostname, docker.clone()).await { + error!("connection lost: {:#}", e); + } + info!("reconnecting in {:?}", RECONNECT_DELAY); + time::sleep(RECONNECT_DELAY).await; + } +} + +async fn run(url: &str, token: &str, hostname: &str, docker: DockerClient) -> Result<()> { + info!("connecting to server at {}", url); + let mut client = AgentGatewayClient::connect(url.to_string()).await?; + + let (tx, rx) = mpsc::channel::(64); + + tx.send(AgentMessage { + payload: Some(agent_message::Payload::Handshake(AgentHandshake { + token: token.to_string(), + hostname: hostname.to_string(), + arch: std::env::consts::ARCH.to_string(), + os: std::env::consts::OS.to_string(), + })), + }) + .await?; + + let outbound = tokio_stream::wrappers::ReceiverStream::new(rx); + let mut inbound = client.tunnel(Request::new(outbound)).await?.into_inner(); + + let mut snapshot_ticker = time::interval(SNAPSHOT_INTERVAL); + let mut log_tasks: HashMap> = HashMap::new(); + + loop { + tokio::select! { + _ = snapshot_ticker.tick() => { + match time::timeout(Duration::from_secs(5), docker.list_containers()).await { + Err(_) => warn!("docker list timed out"), + Ok(Err(e)) => warn!("docker list failed: {:#}", e), + Ok(Ok(containers)) => { + let msg = AgentMessage { + payload: Some(agent_message::Payload::Snapshot(ContainerSnapshot { + containers, + timestamp: unix_now(), + })), + }; + if tx.send(msg).await.is_err() { + break; + } + } + } + } + + result = inbound.message() => { + match result? { + None => break, + Some(msg) => match msg.payload { + Some(server_message::Payload::ContainerCmd(cmd)) => { + let ok = execute_action(&docker, &cmd.container_id, cmd.action).await; + let _ = tx.send(AgentMessage { + payload: Some(agent_message::Payload::Result(proto::CommandResult { + command_id: cmd.command_id, + success: ok.is_ok(), + error: ok.err().map(|e| e.to_string()).unwrap_or_default(), + })), + }).await; + } + Some(server_message::Payload::StreamLogs(cmd)) => { + if let Some(old) = log_tasks.remove(&cmd.container_id) { + old.abort(); + } + let docker_clone = docker.clone(); + let tx_clone = tx.clone(); + let cid = cmd.container_id.clone(); + let handle = tokio::spawn(async move { + let mut stream = docker_clone.logs(&cid, cmd.follow, cmd.tail); + while let Some(result) = stream.next().await { + match result { + Ok(output) => { + let (stream_name, data) = match output { + LogOutput::StdOut { message } => ("stdout", message), + LogOutput::StdErr { message } => ("stderr", message), + _ => continue, + }; + let msg = AgentMessage { + payload: Some(agent_message::Payload::LogChunk( + proto::LogChunk { + container_id: cid.clone(), + stream: stream_name.to_string(), + data: data.to_vec(), + timestamp: unix_now(), + }, + )), + }; + if tx_clone.send(msg).await.is_err() { + break; + } + } + Err(e) => { + warn!("log stream error for {}: {:#}", cid, e); + break; + } + } + } + }); + log_tasks.insert(cmd.container_id, handle); + } + None => {} + } + } + } + } + } + + for (_, task) in log_tasks { + task.abort(); + } + + Ok(()) +} + +pub(crate) async fn execute_action( + docker: &B, + id: &str, + action: i32, +) -> Result<()> { + match ContainerAction::try_from(action)? { + ContainerAction::Start => docker.start(id).await, + ContainerAction::Stop => docker.stop(id).await, + ContainerAction::Restart => docker.restart(id).await, + ContainerAction::Remove => docker.remove(id).await, + ContainerAction::Unspecified => anyhow::bail!("unspecified action"), + } +} + +pub(crate) fn unix_now() -> i64 { + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap_or_default() + .as_secs() as i64 +} + +// ── Tests ───────────────────────────────────────────────────────────────────── + +#[cfg(test)] +mod tests { + use super::*; + use crate::docker::tests::MockBackend; + use proto::ContainerAction; + + // ── unix_now ────────────────────────────────────────────────────────────── + + #[test] + fn unix_now_is_positive_and_recent() { + let t = unix_now(); + // Should be somewhere past 2020-01-01 (1_577_836_800) + assert!(t > 1_577_836_800, "unix_now returned {t}, expected > 2020"); + // And not absurdly far in the future (year 2100 = 4_102_444_800) + assert!(t < 4_102_444_800, "unix_now returned {t}, looks wrong"); + } + + #[test] + fn unix_now_is_monotone_or_equal() { + let t1 = unix_now(); + let t2 = unix_now(); + // Same second or the second just ticked forward — never backwards + assert!(t2 >= t1); + } + + // ── ContainerAction enum parsing ────────────────────────────────────────── + + #[test] + fn container_action_from_valid_int() { + assert_eq!(ContainerAction::try_from(0).unwrap(), ContainerAction::Unspecified); + assert_eq!(ContainerAction::try_from(1).unwrap(), ContainerAction::Start); + assert_eq!(ContainerAction::try_from(2).unwrap(), ContainerAction::Stop); + assert_eq!(ContainerAction::try_from(3).unwrap(), ContainerAction::Restart); + assert_eq!(ContainerAction::try_from(4).unwrap(), ContainerAction::Remove); + } + + #[test] + fn container_action_from_invalid_int_errors() { + assert!(ContainerAction::try_from(99).is_err()); + assert!(ContainerAction::try_from(-1).is_err()); + } + + // ── execute_action — routing ────────────────────────────────────────────── + + #[tokio::test] + async fn execute_action_start_calls_start() { + let backend = MockBackend::new(); + execute_action(&backend, "container-a", ContainerAction::Start as i32) + .await + .unwrap(); + assert_eq!(*backend.calls.lock().unwrap(), vec!["start:container-a"]); + } + + #[tokio::test] + async fn execute_action_stop_calls_stop() { + let backend = MockBackend::new(); + execute_action(&backend, "container-b", ContainerAction::Stop as i32) + .await + .unwrap(); + assert_eq!(*backend.calls.lock().unwrap(), vec!["stop:container-b"]); + } + + #[tokio::test] + async fn execute_action_restart_calls_restart() { + let backend = MockBackend::new(); + execute_action(&backend, "container-c", ContainerAction::Restart as i32) + .await + .unwrap(); + assert_eq!(*backend.calls.lock().unwrap(), vec!["restart:container-c"]); + } + + #[tokio::test] + async fn execute_action_remove_calls_remove() { + let backend = MockBackend::new(); + execute_action(&backend, "container-d", ContainerAction::Remove as i32) + .await + .unwrap(); + assert_eq!(*backend.calls.lock().unwrap(), vec!["remove:container-d"]); + } + + #[tokio::test] + async fn execute_action_unspecified_returns_error() { + let backend = MockBackend::new(); + let result = + execute_action(&backend, "container-e", ContainerAction::Unspecified as i32).await; + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains("unspecified action")); + } + + #[tokio::test] + async fn execute_action_invalid_int_returns_error() { + let backend = MockBackend::new(); + let result = execute_action(&backend, "container-f", 999).await; + assert!(result.is_err()); + } + + // ── execute_action — error propagation ─────────────────────────────────── + + #[tokio::test] + async fn execute_action_start_propagates_backend_error() { + let backend = MockBackend::failing("docker not reachable"); + let result = + execute_action(&backend, "x", ContainerAction::Start as i32).await; + assert!(result.is_err()); + assert!(result.unwrap_err().to_string().contains("docker not reachable")); + } + + #[tokio::test] + async fn execute_action_stop_propagates_backend_error() { + let backend = MockBackend::failing("timeout"); + let result = + execute_action(&backend, "x", ContainerAction::Stop as i32).await; + assert!(result.is_err()); + } + + #[tokio::test] + async fn execute_action_restart_propagates_backend_error() { + let backend = MockBackend::failing("timeout"); + let result = + execute_action(&backend, "x", ContainerAction::Restart as i32).await; + assert!(result.is_err()); + } + + #[tokio::test] + async fn execute_action_remove_propagates_backend_error() { + let backend = MockBackend::failing("permission denied"); + let result = + execute_action(&backend, "x", ContainerAction::Remove as i32).await; + assert!(result.is_err()); + } + + // ── Log-task abort logic (unit-level) ───────────────────────────────────── + + /// Spawn a long-running task and verify it is aborted when its JoinHandle + /// is dropped via abort() — exercises the same flow as the StreamLogs branch. + #[tokio::test] + async fn spawned_task_can_be_aborted() { + use tokio::sync::oneshot; + let (started_tx, started_rx) = oneshot::channel::<()>(); + let handle = tokio::spawn(async move { + let _ = started_tx.send(()); + // Block indefinitely simulating a live log stream + tokio::time::sleep(Duration::from_secs(3600)).await; + }); + // Wait until the task is definitely running + started_rx.await.unwrap(); + handle.abort(); + let result = handle.await; + assert!(result.is_err()); // JoinError with is_cancelled() == true + assert!(result.unwrap_err().is_cancelled()); + } + + /// Spawning a second task for the same container_id aborts the first one, + /// mirroring the log_tasks.remove() + old.abort() pattern in main.rs. + #[tokio::test] + async fn second_stream_aborts_first() { + use tokio::sync::oneshot; + let mut log_tasks: HashMap> = HashMap::new(); + let cid = "my-container".to_string(); + + let (tx1, rx1) = oneshot::channel::<()>(); + let h1 = tokio::spawn(async move { + let _ = tx1.send(()); + tokio::time::sleep(Duration::from_secs(3600)).await; + }); + rx1.await.unwrap(); + log_tasks.insert(cid.clone(), h1); + + // Second StreamLogs for the same cid — abort the old task + if let Some(old) = log_tasks.remove(&cid) { + old.abort(); + } + let (tx2, rx2) = oneshot::channel::<()>(); + let h2 = tokio::spawn(async move { + let _ = tx2.send(()); + tokio::time::sleep(Duration::from_secs(3600)).await; + }); + rx2.await.unwrap(); + log_tasks.insert(cid.clone(), h2); + + assert_eq!(log_tasks.len(), 1); + // Clean up + for (_, h) in log_tasks { + h.abort(); + } + } + + // ── Proto message construction ──────────────────────────────────────────── + + #[test] + fn agent_handshake_fields_roundtrip() { + let hs = AgentHandshake { + token: "tok".to_string(), + hostname: "host".to_string(), + arch: "x86_64".to_string(), + os: "linux".to_string(), + }; + assert_eq!(hs.token, "tok"); + assert_eq!(hs.hostname, "host"); + } + + #[test] + fn agent_message_wraps_handshake() { + let msg = AgentMessage { + payload: Some(agent_message::Payload::Handshake(AgentHandshake { + token: "t".to_string(), + hostname: "h".to_string(), + arch: "arm64".to_string(), + os: "linux".to_string(), + })), + }; + assert!(matches!( + msg.payload, + Some(agent_message::Payload::Handshake(_)) + )); + } + + #[test] + fn command_result_ok_fields() { + let r = proto::CommandResult { + command_id: "cmd-1".to_string(), + success: true, + error: String::new(), + }; + assert!(r.success); + assert!(r.error.is_empty()); + } + + #[test] + fn command_result_err_fields() { + let r = proto::CommandResult { + command_id: "cmd-2".to_string(), + success: false, + error: "container not found".to_string(), + }; + assert!(!r.success); + assert_eq!(r.error, "container not found"); + } + + #[test] + fn log_chunk_fields() { + let chunk = proto::LogChunk { + container_id: "cid".to_string(), + stream: "stdout".to_string(), + data: b"hello".to_vec(), + timestamp: 12345, + }; + assert_eq!(chunk.stream, "stdout"); + assert_eq!(chunk.data, b"hello"); + } +} diff --git a/docker-compose.agent.yml b/docker-compose.agent.yml new file mode 100644 index 0000000..d5bda0a --- /dev/null +++ b/docker-compose.agent.yml @@ -0,0 +1,13 @@ +services: + agent: + image: ghcr.io/containarr/agent:latest # or build locally + # build: + # context: . + # dockerfile: agent/Dockerfile + restart: unless-stopped + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + environment: + CONTAINARR_SERVER_URL: "${CONTAINARR_SERVER_URL}" + CONTAINARR_AGENT_TOKEN: "${CONTAINARR_AGENT_TOKEN}" + RUST_LOG: "info" diff --git a/docker-compose.server.yml b/docker-compose.server.yml new file mode 100644 index 0000000..91ba4c2 --- /dev/null +++ b/docker-compose.server.yml @@ -0,0 +1,38 @@ +services: + server: + build: + context: ./server + image: containarr-server:latest + restart: unless-stopped + ports: + - "8080:8080" # HTTP + WebSocket (PWA) + - "9090:9090" # gRPC (agents) + volumes: + - containarr-data:/data + environment: + DB_PATH: /data/containarr.db + HTTP_ADDR: ":8080" + GRPC_ADDR: ":9090" + JWT_SECRET: "${JWT_SECRET}" + ADMIN_USER: "${ADMIN_USER}" + ADMIN_PASSWORD: "${ADMIN_PASSWORD}" + BOOTSTRAP_TOKENS: "local:${LOCAL_AGENT_TOKEN}" + + # Agent for the local VM (same host as the server). + agent: + build: + context: . + dockerfile: agent/Dockerfile + image: containarr-agent:latest + restart: unless-stopped + depends_on: + - server + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + environment: + CONTAINARR_SERVER_URL: "http://server:9090" + CONTAINARR_AGENT_TOKEN: "${LOCAL_AGENT_TOKEN}" + RUST_LOG: "info" + +volumes: + containarr-data: diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..fbecc26 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "Containarr", + "lockfileVersion": 3, + "requires": true, + "packages": {} +} diff --git a/proto/agent/v1/agent.proto b/proto/agent/v1/agent.proto new file mode 100644 index 0000000..6fb7867 --- /dev/null +++ b/proto/agent/v1/agent.proto @@ -0,0 +1,100 @@ +syntax = "proto3"; + +package containarr.agent.v1; + +option go_package = "github.com/containarr/server/internal/proto/agentv1"; + +// ── Shared types ───────────────────────────────────────────────────────────── + +message ContainerPort { + uint32 host_port = 1; + uint32 container_port = 2; + string protocol = 3; + string host_ip = 4; +} + +message ContainerInfo { + string id = 1; + string name = 2; + string image = 3; + string status = 4; + string state = 5; + repeated ContainerPort ports = 6; + int64 created_at = 7; + map labels = 8; + string compose_project = 9; +} + +// ── Agent → Server ──────────────────────────────────────────────────────────── + +message AgentHandshake { + string token = 1; + string hostname = 2; + string arch = 3; + string os = 4; +} + +message ContainerSnapshot { + repeated ContainerInfo containers = 1; + int64 timestamp = 2; +} + +message CommandResult { + string command_id = 1; + bool success = 2; + string error = 3; +} + +message LogChunk { + string container_id = 1; + string stream = 2; // "stdout" | "stderr" + bytes data = 3; + int64 timestamp = 4; +} + +message AgentMessage { + oneof payload { + AgentHandshake handshake = 1; + ContainerSnapshot snapshot = 2; + CommandResult result = 3; + LogChunk log_chunk = 4; + } +} + +// ── Server → Agent ──────────────────────────────────────────────────────────── + +enum ContainerAction { + CONTAINER_ACTION_UNSPECIFIED = 0; + CONTAINER_ACTION_START = 1; + CONTAINER_ACTION_STOP = 2; + CONTAINER_ACTION_RESTART = 3; + CONTAINER_ACTION_REMOVE = 4; +} + +message ContainerCommand { + string command_id = 1; + string container_id = 2; + ContainerAction action = 3; +} + +message StreamLogsCommand { + string command_id = 1; + string container_id = 2; + bool follow = 3; + int32 tail = 4; +} + +message ServerMessage { + oneof payload { + ContainerCommand container_cmd = 1; + StreamLogsCommand stream_logs = 2; + } +} + +// ── Service ─────────────────────────────────────────────────────────────────── + +service AgentGateway { + // Bidirectional stream: agent connects once and maintains the tunnel. + // First AgentMessage must be AgentHandshake. + rpc Tunnel(stream AgentMessage) returns (stream ServerMessage); +} diff --git a/server/Dockerfile b/server/Dockerfile new file mode 100644 index 0000000..8577b04 --- /dev/null +++ b/server/Dockerfile @@ -0,0 +1,20 @@ +FROM golang:1.23-alpine AS builder + +RUN apk add --no-cache gcc musl-dev + +WORKDIR /src +COPY go.mod go.sum ./ +COPY . . +RUN go mod tidy && CGO_ENABLED=1 GOOS=linux go build -ldflags="-s -w" -o /bin/containarr-server ./cmd/server + +# ── Runtime ─────────────────────────────────────────────────────────────────── +FROM alpine:3.20 + +RUN apk add --no-cache ca-certificates tzdata + +COPY --from=builder /bin/containarr-server /usr/local/bin/containarr-server + +VOLUME ["/data"] +EXPOSE 8080 9090 + +ENTRYPOINT ["containarr-server"] diff --git a/server/cmd/server/main.go b/server/cmd/server/main.go new file mode 100644 index 0000000..9e8d8fe --- /dev/null +++ b/server/cmd/server/main.go @@ -0,0 +1,139 @@ +package main + +import ( + "context" + "log/slog" + "net" + "net/http" + "os" + "os/signal" + "strings" + "syscall" + "time" + + "github.com/google/uuid" + "golang.org/x/crypto/bcrypt" + + "github.com/containarr/server/internal/api" + "github.com/containarr/server/internal/broker" + grpcgateway "github.com/containarr/server/internal/grpc" + agentv1 "github.com/containarr/server/internal/proto/agentv1" + "github.com/containarr/server/internal/store" + "google.golang.org/grpc" +) + +func main() { + slog.SetDefault(slog.New(slog.NewJSONHandler(os.Stdout, nil))) + + dbPath := getenv("DB_PATH", "/data/containarr.db") + httpAddr := getenv("HTTP_ADDR", ":8080") + grpcAddr := getenv("GRPC_ADDR", ":9090") + + db, err := store.New(dbPath) + must(err, "open store") + defer db.Close() + + bootstrapAdmin(db) + bootstrapTokens(db) + + reg := grpcgateway.NewRegistry() + brk := broker.New() + + // gRPC server. + gw := grpcgateway.NewGateway(db, reg, brk) + grpcServer := grpc.NewServer() + agentv1.RegisterAgentGatewayServer(grpcServer, gw) + + lis, err := net.Listen("tcp", grpcAddr) + must(err, "listen grpc") + + go func() { + slog.Info("gRPC listening", "addr", grpcAddr) + if err := grpcServer.Serve(lis); err != nil { + slog.Error("gRPC serve", "err", err) + } + }() + + // HTTP server. + h := api.NewHandler(db, reg, brk) + httpServer := &http.Server{ + Addr: httpAddr, + Handler: api.NewRouter(h), + ReadTimeout: 10 * time.Second, + WriteTimeout: 0, // disabled for WebSocket handlers + } + + go func() { + slog.Info("HTTP listening", "addr", httpAddr) + if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { + slog.Error("HTTP serve", "err", err) + } + }() + + // Graceful shutdown. + quit := make(chan os.Signal, 1) + signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + <-quit + + slog.Info("shutting down") + grpcServer.GracefulStop() + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + _ = httpServer.Shutdown(ctx) +} + +func getenv(key, fallback string) string { + if v := os.Getenv(key); v != "" { + return v + } + return fallback +} + +// bootstrapAdmin creates the admin user from env vars if it doesn't exist yet. +func bootstrapAdmin(db *store.Store) { + username := getenv("ADMIN_USER", "admin") + password := getenv("ADMIN_PASSWORD", "admin") + + exists, err := db.UserExists(username) + if err != nil || exists { + return + } + hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + slog.Error("bcrypt admin", "err", err) + return + } + if err := db.UpsertUser(username, string(hash)); err != nil { + slog.Error("seed admin user", "err", err) + return + } + slog.Info("admin user created", "username", username) +} + +// bootstrapTokens seeds agent tokens from BOOTSTRAP_TOKENS env var. +// Format: "hostname:token,hostname2:token2" +func bootstrapTokens(db *store.Store) { + raw := os.Getenv("BOOTSTRAP_TOKENS") + if raw == "" { + return + } + for _, pair := range strings.Split(raw, ",") { + parts := strings.SplitN(strings.TrimSpace(pair), ":", 2) + if len(parts) != 2 { + continue + } + hostname, token := parts[0], parts[1] + if err := db.CreateAgentToken(uuid.NewString(), token, hostname); err != nil { + slog.Warn("bootstrap token already exists", "hostname", hostname) + } else { + slog.Info("bootstrapped agent token", "hostname", hostname) + } + } +} + +func must(err error, msg string) { + if err != nil { + slog.Error(msg, "err", err) + os.Exit(1) + } +} diff --git a/server/go.mod b/server/go.mod new file mode 100644 index 0000000..5bc8561 --- /dev/null +++ b/server/go.mod @@ -0,0 +1,21 @@ +module github.com/containarr/server + +go 1.23 + +require ( + github.com/go-chi/chi/v5 v5.1.0 + github.com/golang-jwt/jwt/v5 v5.2.1 + github.com/google/uuid v1.6.0 + github.com/gorilla/websocket v1.5.3 + github.com/mattn/go-sqlite3 v1.14.22 + golang.org/x/crypto v0.21.0 + google.golang.org/grpc v1.64.0 + google.golang.org/protobuf v1.34.2 +) + +require ( + golang.org/x/net v0.22.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect +) diff --git a/server/go.sum b/server/go.sum new file mode 100644 index 0000000..b879bb4 --- /dev/null +++ b/server/go.sum @@ -0,0 +1,26 @@ +github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= +github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= +github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= +google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= diff --git a/server/internal/api/api_test.go b/server/internal/api/api_test.go new file mode 100644 index 0000000..4a6c8d3 --- /dev/null +++ b/server/internal/api/api_test.go @@ -0,0 +1,550 @@ +package api + +import ( + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" + "time" + + "github.com/containarr/server/internal/broker" + grpcgateway "github.com/containarr/server/internal/grpc" + agentv1 "github.com/containarr/server/internal/proto/agentv1" + "github.com/containarr/server/internal/store" + "github.com/go-chi/chi/v5" + "github.com/golang-jwt/jwt/v5" + "golang.org/x/crypto/bcrypt" +) + +// ── helpers ─────────────────────────────────────────────────────────────────── + +func newTestHandler(t *testing.T) (*Handler, *store.Store, *grpcgateway.Registry, *broker.Broker) { + t.Helper() + s, err := store.New(":memory:") + if err != nil { + t.Fatalf("store.New: %v", err) + } + t.Cleanup(func() { s.Close() }) + + reg := grpcgateway.NewRegistry() + b := broker.New() + h := NewHandler(s, reg, b) + return h, s, reg, b +} + +func makeJWT(t *testing.T, subject string) string { + t.Helper() + token := jwt.NewWithClaims(jwt.SigningMethodHS256, &jwtClaims{ + RegisteredClaims: jwt.RegisteredClaims{ + Subject: subject, + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)), + IssuedAt: jwt.NewNumericDate(time.Now()), + }, + }) + signed, err := token.SignedString(jwtSecret()) + if err != nil { + t.Fatalf("makeJWT: %v", err) + } + return signed +} + +func bearerHeader(token string) string { + return "Bearer " + token +} + +func postJSON(t *testing.T, handler http.HandlerFunc, path string, body any) *httptest.ResponseRecorder { + t.Helper() + b, _ := json.Marshal(body) + req := httptest.NewRequest(http.MethodPost, path, bytes.NewReader(b)) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + handler(w, req) + return w +} + +func postJSONAuth(t *testing.T, handler http.HandlerFunc, path string, body any, token string) *httptest.ResponseRecorder { + t.Helper() + b, _ := json.Marshal(body) + req := httptest.NewRequest(http.MethodPost, path, bytes.NewReader(b)) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", bearerHeader(token)) + w := httptest.NewRecorder() + + // Wrap handler with requireJWT so claims land in context. + requireJWT(handler).ServeHTTP(w, req) + return w +} + +// ── extractToken ────────────────────────────────────────────────────────────── + +func TestExtractToken_BearerHeader(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.Header.Set("Authorization", "Bearer mytoken") + if got := extractToken(req); got != "mytoken" { + t.Errorf("expected 'mytoken', got %q", got) + } +} + +func TestExtractToken_QueryParam(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/?token=querytoken", nil) + if got := extractToken(req); got != "querytoken" { + t.Errorf("expected 'querytoken', got %q", got) + } +} + +func TestExtractToken_Empty(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/", nil) + if got := extractToken(req); got != "" { + t.Errorf("expected empty, got %q", got) + } +} + +func TestExtractToken_ShortAuthHeader(t *testing.T) { + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.Header.Set("Authorization", "Bear") // len < 7 + if got := extractToken(req); got != "" { + t.Errorf("expected empty for short header, got %q", got) + } +} + +// ── requireJWT middleware ───────────────────────────────────────────────────── + +func TestRequireJWT_MissingToken(t *testing.T) { + called := false + next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { called = true }) + + req := httptest.NewRequest(http.MethodGet, "/", nil) + w := httptest.NewRecorder() + requireJWT(next).ServeHTTP(w, req) + + if w.Code != http.StatusUnauthorized { + t.Errorf("expected 401, got %d", w.Code) + } + if called { + t.Error("handler should not be called without token") + } +} + +func TestRequireJWT_InvalidToken(t *testing.T) { + next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}) + + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.Header.Set("Authorization", "Bearer not.a.real.token") + w := httptest.NewRecorder() + requireJWT(next).ServeHTTP(w, req) + + if w.Code != http.StatusUnauthorized { + t.Errorf("expected 401, got %d", w.Code) + } +} + +func TestRequireJWT_ValidToken(t *testing.T) { + token := makeJWT(t, "alice") + called := false + var gotSubject string + next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + called = true + c, ok := claimsFromContext(r) + if !ok { + t.Error("claims not in context") + return + } + gotSubject = c.Subject + }) + + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.Header.Set("Authorization", bearerHeader(token)) + w := httptest.NewRecorder() + requireJWT(next).ServeHTTP(w, req) + + if !called { + t.Error("handler was not called") + } + if gotSubject != "alice" { + t.Errorf("expected subject 'alice', got %q", gotSubject) + } +} + +func TestRequireJWT_WrongSecret(t *testing.T) { + // Sign with a different secret + token := jwt.NewWithClaims(jwt.SigningMethodHS256, &jwtClaims{ + RegisteredClaims: jwt.RegisteredClaims{ + Subject: "hacker", + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour)), + }, + }) + signed, _ := token.SignedString([]byte("wrong-secret")) + + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.Header.Set("Authorization", bearerHeader(signed)) + w := httptest.NewRecorder() + requireJWT(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})).ServeHTTP(w, req) + + if w.Code != http.StatusUnauthorized { + t.Errorf("expected 401, got %d", w.Code) + } +} + +// ── Login ───────────────────────────────────────────────────────────────────── + +func TestLogin_Success(t *testing.T) { + h, s, _, _ := newTestHandler(t) + + hash, _ := bcrypt.GenerateFromPassword([]byte("password"), bcrypt.MinCost) + _ = s.UpsertUser("alice", string(hash)) + + w := postJSON(t, h.Login, "/api/v1/auth/login", map[string]string{ + "username": "alice", + "password": "password", + }) + + if w.Code != http.StatusOK { + t.Fatalf("expected 200, got %d — body: %s", w.Code, w.Body.String()) + } + + var resp map[string]string + if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { + t.Fatalf("decode response: %v", err) + } + if resp["token"] == "" { + t.Error("expected non-empty token in response") + } +} + +func TestLogin_WrongPassword(t *testing.T) { + h, s, _, _ := newTestHandler(t) + + hash, _ := bcrypt.GenerateFromPassword([]byte("correct"), bcrypt.MinCost) + _ = s.UpsertUser("alice", string(hash)) + + w := postJSON(t, h.Login, "/api/v1/auth/login", map[string]string{ + "username": "alice", + "password": "wrong", + }) + + if w.Code != http.StatusUnauthorized { + t.Errorf("expected 401, got %d", w.Code) + } +} + +func TestLogin_UnknownUser(t *testing.T) { + h, _, _, _ := newTestHandler(t) + + w := postJSON(t, h.Login, "/api/v1/auth/login", map[string]string{ + "username": "nobody", + "password": "pass", + }) + + if w.Code != http.StatusUnauthorized { + t.Errorf("expected 401, got %d", w.Code) + } +} + +func TestLogin_BadBody(t *testing.T) { + h, _, _, _ := newTestHandler(t) + + req := httptest.NewRequest(http.MethodPost, "/api/v1/auth/login", strings.NewReader("not-json")) + w := httptest.NewRecorder() + h.Login(w, req) + + if w.Code != http.StatusBadRequest { + t.Errorf("expected 400, got %d", w.Code) + } +} + +func TestLogin_EmptyFields(t *testing.T) { + h, _, _, _ := newTestHandler(t) + + w := postJSON(t, h.Login, "/api/v1/auth/login", map[string]string{ + "username": "", + "password": "", + }) + + if w.Code != http.StatusBadRequest { + t.Errorf("expected 400, got %d", w.Code) + } +} + +// ── ChangePassword ───────────────────────────────────────────────────────────── + +func TestChangePassword_Success(t *testing.T) { + h, s, _, _ := newTestHandler(t) + os.Setenv("JWT_SECRET", "test-secret-change-pw") + defer os.Unsetenv("JWT_SECRET") + + hash, _ := bcrypt.GenerateFromPassword([]byte("oldpass"), bcrypt.MinCost) + _ = s.UpsertUser("alice", string(hash)) + + token := makeJWT(t, "alice") + w := postJSONAuth(t, h.ChangePassword, "/api/v1/auth/change-password", map[string]string{ + "current_password": "oldpass", + "new_password": "newpass", + }, token) + + if w.Code != http.StatusNoContent { + t.Fatalf("expected 204, got %d — body: %s", w.Code, w.Body.String()) + } + + // Verify new hash is stored + newHash, _ := s.GetUserHash("alice") + if bcrypt.CompareHashAndPassword([]byte(newHash), []byte("newpass")) != nil { + t.Error("new password hash does not match") + } +} + +func TestChangePassword_WrongCurrentPassword(t *testing.T) { + h, s, _, _ := newTestHandler(t) + + hash, _ := bcrypt.GenerateFromPassword([]byte("correct"), bcrypt.MinCost) + _ = s.UpsertUser("alice", string(hash)) + + token := makeJWT(t, "alice") + w := postJSONAuth(t, h.ChangePassword, "/api/v1/auth/change-password", map[string]string{ + "current_password": "wrong", + "new_password": "newpass", + }, token) + + if w.Code != http.StatusUnauthorized { + t.Errorf("expected 401, got %d", w.Code) + } +} + +// ── ListAgents ──────────────────────────────────────────────────────────────── + +func TestListAgents_Empty(t *testing.T) { + h, _, _, _ := newTestHandler(t) + + req := httptest.NewRequest(http.MethodGet, "/api/v1/agents", nil) + w := httptest.NewRecorder() + h.ListAgents(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("expected 200, got %d", w.Code) + } + + var agents []agentDTO + if err := json.NewDecoder(w.Body).Decode(&agents); err != nil { + t.Fatalf("decode: %v", err) + } + if len(agents) != 0 { + t.Errorf("expected empty list, got %d", len(agents)) + } +} + +func TestListAgents_PersistenceAndLive(t *testing.T) { + h, s, reg, _ := newTestHandler(t) + + _ = s.CreateAgentToken("a1", "t1", "host1") + // Register a2 in the registry (simulating live agent) + reg.Register("a2", "host2", "alias2", "192.168.1.1", "arm64", "linux") + _ = s.CreateAgentToken("a2", "t2", "host2") + + req := httptest.NewRequest(http.MethodGet, "/api/v1/agents", nil) + w := httptest.NewRecorder() + h.ListAgents(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("expected 200, got %d", w.Code) + } + + var agents []agentDTO + json.NewDecoder(w.Body).Decode(&agents) + + if len(agents) != 2 { + t.Fatalf("expected 2 agents, got %d", len(agents)) + } + + // Find a2 — it should be online + var a2 *agentDTO + for i := range agents { + if agents[i].ID == "a2" { + a2 = &agents[i] + } + } + if a2 == nil { + t.Fatal("a2 not found in list") + } + if !a2.Online { + t.Error("a2 should be online (registered in registry)") + } +} + +// ── CreateAgentToken ────────────────────────────────────────────────────────── + +func TestCreateAgentToken_Success(t *testing.T) { + h, _, _, _ := newTestHandler(t) + + b, _ := json.Marshal(map[string]string{"hostname": "new-agent"}) + req := httptest.NewRequest(http.MethodPost, "/api/v1/agents/token", bytes.NewReader(b)) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + h.CreateAgentToken(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("expected 200, got %d — body: %s", w.Code, w.Body.String()) + } + + var resp map[string]string + json.NewDecoder(w.Body).Decode(&resp) + + if resp["agent_id"] == "" || resp["token"] == "" { + t.Errorf("missing agent_id or token in response: %v", resp) + } +} + +func TestCreateAgentToken_MissingHostname(t *testing.T) { + h, _, _, _ := newTestHandler(t) + + b, _ := json.Marshal(map[string]string{"hostname": ""}) + req := httptest.NewRequest(http.MethodPost, "/api/v1/agents/token", bytes.NewReader(b)) + w := httptest.NewRecorder() + h.CreateAgentToken(w, req) + + if w.Code != http.StatusBadRequest { + t.Errorf("expected 400, got %d", w.Code) + } +} + +// ── UpdateAgent ─────────────────────────────────────────────────────────────── + +func TestUpdateAgent_Success(t *testing.T) { + h, s, reg, _ := newTestHandler(t) + + _ = s.CreateAgentToken("a1", "t1", "host1") + reg.Register("a1", "host1", "old", "ip", "arch", "os") + + body, _ := json.Marshal(map[string]string{"alias": "new-alias"}) + + router := chi.NewRouter() + router.Patch("/api/v1/agents/{agentID}", h.UpdateAgent) + + req, _ := http.NewRequest(http.MethodPatch, "/api/v1/agents/a1", bytes.NewReader(body)) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("expected 200, got %d — body: %s", w.Code, w.Body.String()) + } + + var resp agentDTO + json.NewDecoder(w.Body).Decode(&resp) + if resp.Alias != "new-alias" { + t.Errorf("expected alias 'new-alias', got %q", resp.Alias) + } + + // Confirm registry also updated + state, ok := reg.Get("a1") + if !ok { + t.Fatal("agent not in registry") + } + if state.Alias != "new-alias" { + t.Errorf("registry alias not updated, got %q", state.Alias) + } +} + +// ── ListContainers ───────────────────────────────────────────────────────────── + +func TestListContainers_Empty(t *testing.T) { + h, _, _, _ := newTestHandler(t) + + req := httptest.NewRequest(http.MethodGet, "/api/v1/containers", nil) + w := httptest.NewRecorder() + h.ListContainers(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("expected 200, got %d", w.Code) + } +} + +func TestListContainers_WithData(t *testing.T) { + h, _, reg, _ := newTestHandler(t) + + reg.Register("a1", "host1", "alias1", "10.0.0.1", "amd64", "linux") + reg.UpdateContainers("a1", []*agentv1.ContainerInfo{ + {Id: "c1", Name: "web"}, + {Id: "c2", Name: "db"}, + }) + + req := httptest.NewRequest(http.MethodGet, "/api/v1/containers", nil) + w := httptest.NewRecorder() + h.ListContainers(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("expected 200, got %d", w.Code) + } + + var out []struct { + AgentID string `json:"agent_id"` + Container *agentv1.ContainerInfo `json:"container"` + } + json.NewDecoder(w.Body).Decode(&out) + + if len(out) != 2 { + t.Errorf("expected 2 containers, got %d", len(out)) + } +} + +// ── ContainerAction ─────────────────────────────────────────────────────────── + +func TestContainerAction_AgentNotConnected(t *testing.T) { + h, _, _, _ := newTestHandler(t) + + body, _ := json.Marshal(map[string]string{"action": "start"}) + req := httptest.NewRequest(http.MethodPost, "/api/v1/agents/ghost/containers/c1/action", bytes.NewReader(body)) + req.Header.Set("Content-Type", "application/json") + + router := chi.NewRouter() + router.Post("/api/v1/agents/{agentID}/containers/{containerID}/action", h.ContainerAction) + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + if w.Code != http.StatusServiceUnavailable { + t.Errorf("expected 503, got %d", w.Code) + } +} + +func TestContainerAction_InvalidAction(t *testing.T) { + h, _, reg, _ := newTestHandler(t) + reg.Register("a1", "h", "a", "ip", "arch", "os") + + body, _ := json.Marshal(map[string]string{"action": "explode"}) + req := httptest.NewRequest(http.MethodPost, "/api/v1/agents/a1/containers/c1/action", bytes.NewReader(body)) + req.Header.Set("Content-Type", "application/json") + + router := chi.NewRouter() + router.Post("/api/v1/agents/{agentID}/containers/{containerID}/action", h.ContainerAction) + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + if w.Code != http.StatusBadRequest { + t.Errorf("expected 400, got %d", w.Code) + } +} + +func TestContainerAction_Success(t *testing.T) { + h, _, reg, _ := newTestHandler(t) + reg.Register("a1", "h", "a", "ip", "arch", "os") + + body, _ := json.Marshal(map[string]string{"action": "stop"}) + req := httptest.NewRequest(http.MethodPost, "/api/v1/agents/a1/containers/c1/action", bytes.NewReader(body)) + req.Header.Set("Content-Type", "application/json") + + router := chi.NewRouter() + router.Post("/api/v1/agents/{agentID}/containers/{containerID}/action", h.ContainerAction) + w := httptest.NewRecorder() + router.ServeHTTP(w, req) + + if w.Code != http.StatusOK { + t.Fatalf("expected 200, got %d — body: %s", w.Code, w.Body.String()) + } + + var resp map[string]string + json.NewDecoder(w.Body).Decode(&resp) + if resp["command_id"] == "" { + t.Error("expected command_id in response") + } +} diff --git a/server/internal/api/auth.go b/server/internal/api/auth.go new file mode 100644 index 0000000..f5e1b83 --- /dev/null +++ b/server/internal/api/auth.go @@ -0,0 +1,125 @@ +package api + +import ( + "encoding/json" + "net/http" + "os" + "time" + + "github.com/golang-jwt/jwt/v5" + "golang.org/x/crypto/bcrypt" +) + +func jwtSecret() []byte { + if s := os.Getenv("JWT_SECRET"); s != "" { + return []byte(s) + } + return []byte("dev-secret-change-me") +} + +type jwtClaims struct { + jwt.RegisteredClaims +} + +func (h *Handler) Login(w http.ResponseWriter, r *http.Request) { + var body struct { + Username string `json:"username"` + Password string `json:"password"` + } + if err := json.NewDecoder(r.Body).Decode(&body); err != nil || body.Username == "" || body.Password == "" { + http.Error(w, "invalid body", http.StatusBadRequest) + return + } + + hash, err := h.store.GetUserHash(body.Username) + if err != nil { + http.Error(w, "invalid credentials", http.StatusUnauthorized) + return + } + if bcrypt.CompareHashAndPassword([]byte(hash), []byte(body.Password)) != nil { + http.Error(w, "invalid credentials", http.StatusUnauthorized) + return + } + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, &jwtClaims{ + RegisteredClaims: jwt.RegisteredClaims{ + Subject: body.Username, + ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), + IssuedAt: jwt.NewNumericDate(time.Now()), + }, + }) + signed, err := token.SignedString(jwtSecret()) + if err != nil { + http.Error(w, "internal error", http.StatusInternalServerError) + return + } + jsonOK(w, map[string]string{"token": signed}) +} + +func (h *Handler) ChangePassword(w http.ResponseWriter, r *http.Request) { + claims, ok := claimsFromContext(r) + if !ok { + http.Error(w, "unauthorized", http.StatusUnauthorized) + return + } + + var body struct { + CurrentPassword string `json:"current_password"` + NewPassword string `json:"new_password"` + } + if err := json.NewDecoder(r.Body).Decode(&body); err != nil || body.NewPassword == "" { + http.Error(w, "invalid body", http.StatusBadRequest) + return + } + + hash, err := h.store.GetUserHash(claims.Subject) + if err != nil { + http.Error(w, "user not found", http.StatusNotFound) + return + } + if bcrypt.CompareHashAndPassword([]byte(hash), []byte(body.CurrentPassword)) != nil { + http.Error(w, "mot de passe actuel incorrect", http.StatusUnauthorized) + return + } + + newHash, err := bcrypt.GenerateFromPassword([]byte(body.NewPassword), bcrypt.DefaultCost) + if err != nil { + http.Error(w, "internal error", http.StatusInternalServerError) + return + } + if err := h.store.UpsertUser(claims.Subject, string(newHash)); err != nil { + http.Error(w, "store error", http.StatusInternalServerError) + return + } + w.WriteHeader(http.StatusNoContent) +} + +func requireJWT(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + raw := extractToken(r) + if raw == "" { + http.Error(w, "unauthorized", http.StatusUnauthorized) + return + } + t, err := jwt.ParseWithClaims(raw, &jwtClaims{}, func(t *jwt.Token) (any, error) { + if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, jwt.ErrSignatureInvalid + } + return jwtSecret(), nil + }) + if err != nil || !t.Valid { + http.Error(w, "unauthorized", http.StatusUnauthorized) + return + } + next.ServeHTTP(w, r.WithContext( + contextWithClaims(r.Context(), t.Claims.(*jwtClaims)), + )) + }) +} + +func extractToken(r *http.Request) string { + if auth := r.Header.Get("Authorization"); len(auth) > 7 && auth[:7] == "Bearer " { + return auth[7:] + } + return r.URL.Query().Get("token") +} diff --git a/server/internal/api/context.go b/server/internal/api/context.go new file mode 100644 index 0000000..1280bc7 --- /dev/null +++ b/server/internal/api/context.go @@ -0,0 +1,16 @@ +package api + +import "context" + +type contextKey int + +const claimsKey contextKey = iota + +func contextWithClaims(ctx context.Context, c *jwtClaims) context.Context { + return context.WithValue(ctx, claimsKey, c) +} + +func claimsFromContext(r interface{ Context() context.Context }) (*jwtClaims, bool) { + c, ok := r.Context().Value(claimsKey).(*jwtClaims) + return c, ok && c != nil +} diff --git a/server/internal/api/handlers.go b/server/internal/api/handlers.go new file mode 100644 index 0000000..e1a0f8b --- /dev/null +++ b/server/internal/api/handlers.go @@ -0,0 +1,301 @@ +package api + +import ( + "encoding/json" + "net/http" + "strconv" + "time" + + "github.com/containarr/server/internal/broker" + grpcgateway "github.com/containarr/server/internal/grpc" + agentv1 "github.com/containarr/server/internal/proto/agentv1" + "github.com/containarr/server/internal/store" + "github.com/go-chi/chi/v5" + "github.com/google/uuid" + "github.com/gorilla/websocket" +) + +var upgrader = websocket.Upgrader{ + CheckOrigin: func(r *http.Request) bool { return true }, +} + +type Handler struct { + store *store.Store + registry *grpcgateway.Registry + broker *broker.Broker +} + +func NewHandler(s *store.Store, r *grpcgateway.Registry, b *broker.Broker) *Handler { + return &Handler{store: s, registry: r, broker: b} +} + +type agentDTO struct { + ID string `json:"id"` + Hostname string `json:"hostname"` + Alias string `json:"alias"` + IPAddress string `json:"ip_address"` + Arch string `json:"arch"` + OS string `json:"os"` + Online bool `json:"online"` + LastSeenAt time.Time `json:"last_seen_at"` +} + +// ── Agents ──────────────────────────────────────────────────────────────────── + +func (h *Handler) ListAgents(w http.ResponseWriter, r *http.Request) { + persisted, err := h.store.ListAgents() + if err != nil { + http.Error(w, "store error", http.StatusInternalServerError) + return + } + liveByID := map[string]*grpcgateway.AgentState{} + for _, s := range h.registry.List() { + liveByID[s.ID] = s + } + out := make([]agentDTO, 0, len(persisted)) + for _, a := range persisted { + dto := agentDTO{ + ID: a.ID, + Hostname: a.Hostname, + Alias: a.Alias, + IPAddress: a.IPAddress, + Arch: a.Arch, + OS: a.OS, + } + if live, ok := liveByID[a.ID]; ok { + dto.Online = true + dto.IPAddress = live.IPAddress + dto.LastSeenAt = live.LastSeenAt + } + out = append(out, dto) + } + jsonOK(w, out) +} + +func (h *Handler) UpdateAgent(w http.ResponseWriter, r *http.Request) { + agentID := chi.URLParam(r, "agentID") + var body struct { + Alias string `json:"alias"` + } + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + http.Error(w, "invalid body", http.StatusBadRequest) + return + } + if err := h.store.UpdateAgentAlias(agentID, body.Alias); err != nil { + http.Error(w, "store error", http.StatusInternalServerError) + return + } + h.registry.UpdateAlias(agentID, body.Alias) + + a, err := h.store.GetAgent(agentID) + if err != nil { + http.Error(w, "not found", http.StatusNotFound) + return + } + jsonOK(w, agentDTO{ + ID: a.ID, + Hostname: a.Hostname, + Alias: a.Alias, + IPAddress: a.IPAddress, + Arch: a.Arch, + OS: a.OS, + Online: a.Online, + }) +} + +// ── Containers ──────────────────────────────────────────────────────────────── + +func (h *Handler) ListContainers(w http.ResponseWriter, r *http.Request) { + type containerDTO struct { + AgentID string `json:"agent_id"` + Hostname string `json:"hostname"` + Alias string `json:"alias"` + IPAddress string `json:"ip_address"` + Container *agentv1.ContainerInfo `json:"container"` + } + var out []containerDTO + for _, agent := range h.registry.List() { + for _, c := range agent.Containers { + out = append(out, containerDTO{ + AgentID: agent.ID, + Hostname: agent.Hostname, + Alias: agent.Alias, + IPAddress: agent.IPAddress, + Container: c, + }) + } + } + jsonOK(w, out) +} + +func (h *Handler) ContainerAction(w http.ResponseWriter, r *http.Request) { + agentID := chi.URLParam(r, "agentID") + containerID := chi.URLParam(r, "containerID") + + var body struct { + Action string `json:"action"` + } + if err := json.NewDecoder(r.Body).Decode(&body); err != nil { + http.Error(w, "invalid body", http.StatusBadRequest) + return + } + + action, ok := map[string]agentv1.ContainerAction{ + "start": agentv1.ContainerAction_CONTAINER_ACTION_START, + "stop": agentv1.ContainerAction_CONTAINER_ACTION_STOP, + "restart": agentv1.ContainerAction_CONTAINER_ACTION_RESTART, + "remove": agentv1.ContainerAction_CONTAINER_ACTION_REMOVE, + }[body.Action] + if !ok { + http.Error(w, "unknown action", http.StatusBadRequest) + return + } + + cmdID := uuid.NewString() + sent := h.registry.Send(agentID, &agentv1.ServerMessage{ + Payload: &agentv1.ServerMessage_ContainerCmd{ + ContainerCmd: &agentv1.ContainerCommand{ + CommandId: cmdID, + ContainerId: containerID, + Action: action, + }, + }, + }) + if !sent { + http.Error(w, "agent not connected", http.StatusServiceUnavailable) + return + } + jsonOK(w, map[string]string{"command_id": cmdID}) +} + +// ── Agent token provisioning ────────────────────────────────────────────────── + +func (h *Handler) CreateAgentToken(w http.ResponseWriter, r *http.Request) { + var body struct { + Hostname string `json:"hostname"` + } + if err := json.NewDecoder(r.Body).Decode(&body); err != nil || body.Hostname == "" { + http.Error(w, "hostname required", http.StatusBadRequest) + return + } + id := uuid.NewString() + token := uuid.NewString() + if err := h.store.CreateAgentToken(id, token, body.Hostname); err != nil { + http.Error(w, "store error", http.StatusInternalServerError) + return + } + jsonOK(w, map[string]string{"agent_id": id, "token": token}) +} + +// ── Container log stream ────────────────────────────────────────────────────── + +func (h *Handler) LogsWS(w http.ResponseWriter, r *http.Request) { + agentID := chi.URLParam(r, "agentID") + containerID := chi.URLParam(r, "containerID") + + follow := r.URL.Query().Get("follow") != "false" + tail := int32(100) + if t := r.URL.Query().Get("tail"); t != "" { + if n, err := strconv.Atoi(t); err == nil && n > 0 { + tail = int32(n) + } + } + + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + return + } + defer conn.Close() + + sent := h.registry.Send(agentID, &agentv1.ServerMessage{ + Payload: &agentv1.ServerMessage_StreamLogs{ + StreamLogs: &agentv1.StreamLogsCommand{ + CommandId: uuid.NewString(), + ContainerId: containerID, + Follow: follow, + Tail: tail, + }, + }, + }) + if !sent { + conn.WriteMessage(websocket.TextMessage, []byte(`{"error":"agent not connected"}`)) + return + } + + sub := h.broker.Subscribe() + defer h.broker.Unsubscribe(sub) + + done := make(chan struct{}) + go func() { + defer close(done) + for { + if _, _, err := conn.ReadMessage(); err != nil { + return + } + } + }() + + for { + select { + case <-done: + return + case raw, ok := <-sub: + if !ok { + return + } + var envelope struct { + Type string `json:"type"` + AgentID string `json:"agent_id"` + Payload json.RawMessage `json:"payload"` + } + if json.Unmarshal(raw, &envelope) != nil { + continue + } + if envelope.Type != "log.chunk" || envelope.AgentID != agentID { + continue + } + var chunk struct { + ContainerID string `json:"container_id"` + Stream string `json:"stream"` + Data []byte `json:"data"` + } + if json.Unmarshal(envelope.Payload, &chunk) != nil { + continue + } + if chunk.ContainerID != containerID { + continue + } + msg, _ := json.Marshal(map[string]string{ + "stream": chunk.Stream, + "line": string(chunk.Data), + }) + if err := conn.WriteMessage(websocket.TextMessage, msg); err != nil { + return + } + } + } +} + +// ── WebSocket event stream ──────────────────────────────────────────────────── + +func (h *Handler) EventsWS(w http.ResponseWriter, r *http.Request) { + conn, err := upgrader.Upgrade(w, r, nil) + if err != nil { + return + } + defer conn.Close() + + sub := h.broker.Subscribe() + defer h.broker.Unsubscribe(sub) + + for data := range sub { + if err := conn.WriteMessage(websocket.TextMessage, data); err != nil { + return + } + } +} + +func jsonOK(w http.ResponseWriter, v any) { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(v) +} diff --git a/server/internal/api/router.go b/server/internal/api/router.go new file mode 100644 index 0000000..5600ef1 --- /dev/null +++ b/server/internal/api/router.go @@ -0,0 +1,35 @@ +package api + +import ( + "net/http" + + "github.com/go-chi/chi/v5" + "github.com/go-chi/chi/v5/middleware" +) + +func NewRouter(h *Handler) http.Handler { + r := chi.NewRouter() + r.Use(middleware.Logger) + r.Use(middleware.Recoverer) + r.Use(middleware.RealIP) + + r.Route("/api/v1", func(r chi.Router) { + r.Post("/auth/login", h.Login) + + r.Group(func(r chi.Router) { + r.Use(requireJWT) + r.Post("/auth/change-password", h.ChangePassword) + r.Get("/agents", h.ListAgents) + r.Post("/agents/token", h.CreateAgentToken) + r.Patch("/agents/{agentID}", h.UpdateAgent) + r.Get("/containers", h.ListContainers) + r.Post("/agents/{agentID}/containers/{containerID}/action", h.ContainerAction) + r.Get("/agents/{agentID}/containers/{containerID}/logs", h.LogsWS) + r.Get("/events", h.EventsWS) + }) + }) + + r.Handle("/*", http.FileServer(http.Dir("./web/dist"))) + + return r +} diff --git a/server/internal/auth/auth.go b/server/internal/auth/auth.go new file mode 100644 index 0000000..d90a71f --- /dev/null +++ b/server/internal/auth/auth.go @@ -0,0 +1,49 @@ +package auth + +import ( + "errors" + "time" + + "github.com/golang-jwt/jwt/v5" +) + +type Claims struct { + UserID string `json:"uid"` + jwt.RegisteredClaims +} + +type Service struct { + secret []byte +} + +func New(secret string) *Service { + return &Service{secret: []byte(secret)} +} + +func (s *Service) Sign(userID string) (string, error) { + claims := Claims{ + UserID: userID, + RegisteredClaims: jwt.RegisteredClaims{ + ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)), + IssuedAt: jwt.NewNumericDate(time.Now()), + }, + } + return jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString(s.secret) +} + +func (s *Service) Verify(tokenStr string) (*Claims, error) { + token, err := jwt.ParseWithClaims(tokenStr, &Claims{}, func(t *jwt.Token) (any, error) { + if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, errors.New("unexpected signing method") + } + return s.secret, nil + }) + if err != nil { + return nil, err + } + claims, ok := token.Claims.(*Claims) + if !ok || !token.Valid { + return nil, errors.New("invalid token") + } + return claims, nil +} diff --git a/server/internal/auth/auth_test.go b/server/internal/auth/auth_test.go new file mode 100644 index 0000000..8cd6ca5 --- /dev/null +++ b/server/internal/auth/auth_test.go @@ -0,0 +1,64 @@ +package auth + +import ( + "testing" + "time" +) + +func TestSignAndVerify(t *testing.T) { + svc := New("test-secret") + + token, err := svc.Sign("user42") + if err != nil { + t.Fatalf("Sign: %v", err) + } + if token == "" { + t.Fatal("expected non-empty token") + } + + claims, err := svc.Verify(token) + if err != nil { + t.Fatalf("Verify: %v", err) + } + if claims.UserID != "user42" { + t.Errorf("expected UserID 'user42', got %q", claims.UserID) + } + if claims.ExpiresAt == nil || claims.ExpiresAt.Before(time.Now()) { + t.Error("token should not be expired") + } +} + +func TestVerify_InvalidToken(t *testing.T) { + svc := New("test-secret") + _, err := svc.Verify("not.a.valid.token") + if err == nil { + t.Fatal("expected error for invalid token") + } +} + +func TestVerify_WrongSecret(t *testing.T) { + svc1 := New("secret-a") + svc2 := New("secret-b") + + token, err := svc1.Sign("user1") + if err != nil { + t.Fatalf("Sign: %v", err) + } + + _, err = svc2.Verify(token) + if err == nil { + t.Fatal("expected error when verifying with different secret") + } +} + +func TestVerify_TamperedToken(t *testing.T) { + svc := New("test-secret") + token, _ := svc.Sign("admin") + + // Append garbage to corrupt the signature. + tampered := token + "x" + _, err := svc.Verify(tampered) + if err == nil { + t.Fatal("expected error for tampered token") + } +} diff --git a/server/internal/broker/broker.go b/server/internal/broker/broker.go new file mode 100644 index 0000000..3cef1f7 --- /dev/null +++ b/server/internal/broker/broker.go @@ -0,0 +1,55 @@ +package broker + +import ( + "encoding/json" + "sync" +) + +// Event is a JSON-serialisable message pushed to WebSocket clients. +type Event struct { + Type string `json:"type"` + AgentID string `json:"agent_id,omitempty"` + Payload any `json:"payload"` +} + +type subscriber chan []byte + +// Broker fan-outs events to all registered WebSocket subscribers. +type Broker struct { + mu sync.RWMutex + subs map[subscriber]struct{} +} + +func New() *Broker { + return &Broker{subs: make(map[subscriber]struct{})} +} + +func (b *Broker) Subscribe() subscriber { + ch := make(subscriber, 32) + b.mu.Lock() + b.subs[ch] = struct{}{} + b.mu.Unlock() + return ch +} + +func (b *Broker) Unsubscribe(ch subscriber) { + b.mu.Lock() + delete(b.subs, ch) + b.mu.Unlock() + close(ch) +} + +func (b *Broker) Publish(evt Event) { + data, err := json.Marshal(evt) + if err != nil { + return + } + b.mu.RLock() + defer b.mu.RUnlock() + for ch := range b.subs { + select { + case ch <- data: + default: // drop if subscriber is slow + } + } +} diff --git a/server/internal/broker/broker_test.go b/server/internal/broker/broker_test.go new file mode 100644 index 0000000..9614709 --- /dev/null +++ b/server/internal/broker/broker_test.go @@ -0,0 +1,123 @@ +package broker + +import ( + "encoding/json" + "testing" + "time" +) + +func TestSubscribePublishUnsubscribe(t *testing.T) { + b := New() + + sub := b.Subscribe() + + evt := Event{Type: "test.event", AgentID: "agent1", Payload: map[string]string{"k": "v"}} + b.Publish(evt) + + select { + case raw := <-sub: + var got Event + if err := json.Unmarshal(raw, &got); err != nil { + t.Fatalf("unmarshal: %v", err) + } + if got.Type != "test.event" || got.AgentID != "agent1" { + t.Errorf("unexpected event: %+v", got) + } + case <-time.After(time.Second): + t.Fatal("timed out waiting for event") + } + + b.Unsubscribe(sub) + + // channel must be closed after unsubscribe + select { + case _, ok := <-sub: + if ok { + t.Error("expected channel to be closed") + } + case <-time.After(time.Second): + t.Fatal("timed out waiting for channel close") + } +} + +func TestMultipleSubscribers(t *testing.T) { + b := New() + + sub1 := b.Subscribe() + sub2 := b.Subscribe() + defer b.Unsubscribe(sub1) + defer b.Unsubscribe(sub2) + + b.Publish(Event{Type: "ping", Payload: nil}) + + for i, sub := range []subscriber{sub1, sub2} { + select { + case <-sub: + case <-time.After(time.Second): + t.Fatalf("subscriber %d did not receive event", i) + } + } +} + +func TestPublishDropsWhenSubscriberSlow(t *testing.T) { + b := New() + + // Channel size is 32; fill it up and then publish one more — it must not block. + sub := b.Subscribe() + defer b.Unsubscribe(sub) + + // Fill the buffer + for i := 0; i < 32; i++ { + b.Publish(Event{Type: "flood", Payload: i}) + } + + // This extra publish must return immediately (dropped, not block). + done := make(chan struct{}) + go func() { + b.Publish(Event{Type: "dropped", Payload: nil}) + close(done) + }() + + select { + case <-done: + case <-time.After(time.Second): + t.Fatal("Publish blocked on slow subscriber") + } +} + +func TestPublishNoSubscribers(t *testing.T) { + b := New() + // Should not panic or block + b.Publish(Event{Type: "nobody", Payload: nil}) +} + +func TestPublishInvalidPayload(t *testing.T) { + b := New() + sub := b.Subscribe() + defer b.Unsubscribe(sub) + + // json.Marshal of a channel fails — Publish must not send anything. + b.Publish(Event{Type: "bad", Payload: make(chan int)}) + + select { + case <-sub: + t.Error("should not have received a message for an unmarshalable event") + default: + // correct: nothing sent + } +} + +func TestUnsubscribeRemovesFromBroker(t *testing.T) { + b := New() + sub := b.Subscribe() + b.Unsubscribe(sub) + + // After unsubscribe the broker's map should be empty. + b.mu.RLock() + n := len(b.subs) + b.mu.RUnlock() + + if n != 0 { + t.Errorf("expected 0 subscribers after unsubscribe, got %d", n) + } +} diff --git a/server/internal/grpc/gateway.go b/server/internal/grpc/gateway.go new file mode 100644 index 0000000..ec9c63f --- /dev/null +++ b/server/internal/grpc/gateway.go @@ -0,0 +1,137 @@ +package grpc + +import ( + "io" + "log/slog" + "net" + + "github.com/containarr/server/internal/broker" + agentv1 "github.com/containarr/server/internal/proto/agentv1" + "github.com/containarr/server/internal/store" + "github.com/google/uuid" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/peer" + "google.golang.org/grpc/status" +) + +type Gateway struct { + agentv1.UnimplementedAgentGatewayServer + store *store.Store + registry *Registry + broker *broker.Broker +} + +func NewGateway(s *store.Store, r *Registry, b *broker.Broker) *Gateway { + return &Gateway{store: s, registry: r, broker: b} +} + +func (g *Gateway) Tunnel(stream agentv1.AgentGateway_TunnelServer) error { + if err := stream.SendHeader(metadata.MD{}); err != nil { + return status.Errorf(codes.Internal, "send header: %v", err) + } + + first, err := stream.Recv() + if err != nil { + return err + } + hs := first.GetHandshake() + if hs == nil { + return status.Error(codes.InvalidArgument, "first message must be AgentHandshake") + } + + existing, err := g.store.AgentByToken(hs.Token) + if err != nil { + return status.Error(codes.Unauthenticated, "unknown agent token") + } + + // Extract peer IP from the gRPC connection. + ipAddress := "" + if p, ok := peer.FromContext(stream.Context()); ok { + if host, _, err := net.SplitHostPort(p.Addr.String()); err == nil { + ipAddress = host + } + } + + agentID := existing.ID + slog.Info("agent connected", "id", agentID, "hostname", hs.Hostname, "ip", ipAddress) + + state := g.registry.Register(agentID, hs.Hostname, existing.Alias, ipAddress, hs.Arch, hs.Os) + _ = g.store.UpsertAgent(&store.Agent{ + ID: agentID, + Token: hs.Token, + Hostname: hs.Hostname, + Alias: existing.Alias, + IPAddress: ipAddress, + Arch: hs.Arch, + OS: hs.Os, + Online: true, + }) + + g.broker.Publish(broker.Event{ + Type: "agent.connected", + AgentID: agentID, + Payload: map[string]string{"hostname": hs.Hostname}, + }) + + defer func() { + g.registry.Deregister(agentID) + _ = g.store.SetAgentOffline(agentID) + g.broker.Publish(broker.Event{Type: "agent.disconnected", AgentID: agentID, Payload: nil}) + slog.Info("agent disconnected", "id", agentID) + }() + + errCh := make(chan error, 1) + go func() { + for msg := range state.cmdCh { + if err := stream.Send(msg); err != nil { + errCh <- err + return + } + } + }() + + for { + select { + case err := <-errCh: + return err + default: + } + + msg, err := stream.Recv() + if err == io.EOF { + return nil + } + if err != nil { + return err + } + + switch p := msg.Payload.(type) { + case *agentv1.AgentMessage_Snapshot: + g.registry.UpdateContainers(agentID, p.Snapshot.Containers) + g.broker.Publish(broker.Event{ + Type: "containers.updated", + AgentID: agentID, + Payload: p.Snapshot.Containers, + }) + + case *agentv1.AgentMessage_Result: + g.broker.Publish(broker.Event{ + Type: "command.result", + AgentID: agentID, + Payload: p.Result, + }) + + case *agentv1.AgentMessage_LogChunk: + g.broker.Publish(broker.Event{ + Type: "log.chunk", + AgentID: agentID, + Payload: p.LogChunk, + }) + } + } +} + +func newCommandID() string { + return uuid.NewString() +} diff --git a/server/internal/grpc/registry.go b/server/internal/grpc/registry.go new file mode 100644 index 0000000..9811421 --- /dev/null +++ b/server/internal/grpc/registry.go @@ -0,0 +1,105 @@ +package grpc + +import ( + "sync" + "time" + + agentv1 "github.com/containarr/server/internal/proto/agentv1" +) + +type AgentState struct { + ID string + Hostname string + Alias string + IPAddress string + Arch string + OS string + LastSeenAt time.Time + Containers []*agentv1.ContainerInfo + + cmdCh chan *agentv1.ServerMessage +} + +type Registry struct { + mu sync.RWMutex + agents map[string]*AgentState +} + +func NewRegistry() *Registry { + return &Registry{agents: make(map[string]*AgentState)} +} + +func (r *Registry) Register(id, hostname, alias, ipAddress, arch, os string) *AgentState { + state := &AgentState{ + ID: id, + Hostname: hostname, + Alias: alias, + IPAddress: ipAddress, + Arch: arch, + OS: os, + cmdCh: make(chan *agentv1.ServerMessage, 16), + } + r.mu.Lock() + r.agents[id] = state + r.mu.Unlock() + return state +} + +func (r *Registry) Deregister(id string) { + r.mu.Lock() + if s, ok := r.agents[id]; ok { + close(s.cmdCh) + delete(r.agents, id) + } + r.mu.Unlock() +} + +func (r *Registry) Get(id string) (*AgentState, bool) { + r.mu.RLock() + defer r.mu.RUnlock() + s, ok := r.agents[id] + return s, ok +} + +func (r *Registry) List() []*AgentState { + r.mu.RLock() + defer r.mu.RUnlock() + out := make([]*AgentState, 0, len(r.agents)) + for _, s := range r.agents { + out = append(out, s) + } + return out +} + +func (r *Registry) UpdateContainers(id string, containers []*agentv1.ContainerInfo) { + r.mu.Lock() + defer r.mu.Unlock() + if s, ok := r.agents[id]; ok { + s.Containers = containers + s.LastSeenAt = time.Now() + } +} + +// UpdateAlias refreshes the alias for a live agent (called after an admin update). +func (r *Registry) UpdateAlias(id, alias string) { + r.mu.Lock() + defer r.mu.Unlock() + if s, ok := r.agents[id]; ok { + s.Alias = alias + } +} + +func (r *Registry) Send(agentID string, msg *agentv1.ServerMessage) bool { + r.mu.RLock() + s, ok := r.agents[agentID] + r.mu.RUnlock() + if !ok { + return false + } + select { + case s.cmdCh <- msg: + return true + default: + return false + } +} diff --git a/server/internal/grpc/registry_test.go b/server/internal/grpc/registry_test.go new file mode 100644 index 0000000..3f2d3d8 --- /dev/null +++ b/server/internal/grpc/registry_test.go @@ -0,0 +1,155 @@ +package grpc + +import ( + "testing" + "time" + + agentv1 "github.com/containarr/server/internal/proto/agentv1" +) + +func TestRegisterAndGet(t *testing.T) { + r := NewRegistry() + + state := r.Register("id1", "hostname1", "alias1", "10.0.0.1", "amd64", "linux") + if state == nil { + t.Fatal("Register returned nil") + } + + got, ok := r.Get("id1") + if !ok { + t.Fatal("Get returned false for registered agent") + } + if got.ID != "id1" || got.Hostname != "hostname1" || got.Alias != "alias1" { + t.Errorf("unexpected state: %+v", got) + } +} + +func TestGet_NotFound(t *testing.T) { + r := NewRegistry() + _, ok := r.Get("nonexistent") + if ok { + t.Error("expected false for unknown agent") + } +} + +func TestDeregister(t *testing.T) { + r := NewRegistry() + r.Register("id1", "h", "a", "ip", "arch", "os") + + r.Deregister("id1") + + _, ok := r.Get("id1") + if ok { + t.Error("agent should not exist after Deregister") + } +} + +func TestDeregister_NotExist(t *testing.T) { + r := NewRegistry() + // must not panic + r.Deregister("ghost") +} + +func TestList(t *testing.T) { + r := NewRegistry() + + if len(r.List()) != 0 { + t.Error("expected empty list") + } + + r.Register("a1", "h1", "", "", "", "") + r.Register("a2", "h2", "", "", "", "") + + if len(r.List()) != 2 { + t.Errorf("expected 2 agents, got %d", len(r.List())) + } +} + +func TestUpdateContainers(t *testing.T) { + r := NewRegistry() + r.Register("id1", "h", "a", "ip", "arch", "os") + + before := time.Now() + containers := []*agentv1.ContainerInfo{ + {Id: "c1", Name: "web"}, + {Id: "c2", Name: "db"}, + } + r.UpdateContainers("id1", containers) + + got, _ := r.Get("id1") + if len(got.Containers) != 2 { + t.Errorf("expected 2 containers, got %d", len(got.Containers)) + } + if got.LastSeenAt.Before(before) { + t.Error("LastSeenAt should have been updated") + } +} + +func TestUpdateContainers_UnknownAgent(t *testing.T) { + r := NewRegistry() + // must not panic + r.UpdateContainers("ghost", nil) +} + +func TestUpdateAlias(t *testing.T) { + r := NewRegistry() + r.Register("id1", "h", "old-alias", "ip", "arch", "os") + + r.UpdateAlias("id1", "new-alias") + + got, _ := r.Get("id1") + if got.Alias != "new-alias" { + t.Errorf("expected 'new-alias', got %q", got.Alias) + } +} + +func TestUpdateAlias_UnknownAgent(t *testing.T) { + r := NewRegistry() + // must not panic + r.UpdateAlias("ghost", "alias") +} + +func TestSend(t *testing.T) { + r := NewRegistry() + state := r.Register("id1", "h", "a", "ip", "arch", "os") + + msg := &agentv1.ServerMessage{} + ok := r.Send("id1", msg) + if !ok { + t.Fatal("Send returned false for connected agent") + } + + // Drain the channel to verify the message arrived. + select { + case got := <-state.cmdCh: + if got != msg { + t.Error("received wrong message") + } + case <-time.After(time.Second): + t.Fatal("timed out reading from cmdCh") + } +} + +func TestSend_UnknownAgent(t *testing.T) { + r := NewRegistry() + ok := r.Send("ghost", &agentv1.ServerMessage{}) + if ok { + t.Error("Send should return false for unknown agent") + } +} + +func TestSend_FullChannel(t *testing.T) { + r := NewRegistry() + r.Register("id1", "h", "a", "ip", "arch", "os") + + // Fill the buffer (size 16) + for i := 0; i < 16; i++ { + r.Send("id1", &agentv1.ServerMessage{}) + } + + // Next send on a full channel should return false + ok := r.Send("id1", &agentv1.ServerMessage{}) + if ok { + t.Error("Send should return false when channel is full") + } +} diff --git a/server/internal/store/store.go b/server/internal/store/store.go new file mode 100644 index 0000000..2795be5 --- /dev/null +++ b/server/internal/store/store.go @@ -0,0 +1,183 @@ +package store + +import ( + "database/sql" + "time" + + _ "github.com/mattn/go-sqlite3" +) + +type Agent struct { + ID string + Token string + Hostname string + Alias string + IPAddress string + Arch string + OS string + LastSeenAt time.Time + Online bool +} + +type Store struct { + db *sql.DB +} + +func New(path string) (*Store, error) { + db, err := sql.Open("sqlite3", path+"?_journal_mode=WAL&_foreign_keys=on") + if err != nil { + return nil, err + } + s := &Store{db: db} + return s, s.migrate() +} + +func (s *Store) migrate() error { + _, err := s.db.Exec(` + CREATE TABLE IF NOT EXISTS users ( + username TEXT PRIMARY KEY, + password_hash TEXT NOT NULL + ); + CREATE TABLE IF NOT EXISTS agents ( + id TEXT PRIMARY KEY, + token TEXT UNIQUE NOT NULL, + hostname TEXT NOT NULL, + alias TEXT NOT NULL DEFAULT '', + ip_address TEXT NOT NULL DEFAULT '', + arch TEXT NOT NULL DEFAULT '', + os TEXT NOT NULL DEFAULT '', + last_seen_at DATETIME, + online INTEGER NOT NULL DEFAULT 0 + ); + `) + if err != nil { + return err + } + // Idempotent — ignore error if column already exists. + for _, col := range []string{ + `ALTER TABLE agents ADD COLUMN alias TEXT NOT NULL DEFAULT ''`, + `ALTER TABLE agents ADD COLUMN ip_address TEXT NOT NULL DEFAULT ''`, + } { + s.db.Exec(col) + } + return nil +} + +func (s *Store) Close() error { return s.db.Close() } + +func (s *Store) UpsertAgent(a *Agent) error { + _, err := s.db.Exec(` + INSERT INTO agents (id, token, hostname, alias, ip_address, arch, os, last_seen_at, online) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT(token) DO UPDATE SET + hostname = excluded.hostname, + ip_address = excluded.ip_address, + arch = excluded.arch, + os = excluded.os, + last_seen_at = excluded.last_seen_at, + online = excluded.online + `, a.ID, a.Token, a.Hostname, a.Alias, a.IPAddress, a.Arch, a.OS, a.LastSeenAt, boolToInt(a.Online)) + return err +} + +func (s *Store) AgentByToken(token string) (*Agent, error) { + row := s.db.QueryRow(` + SELECT id, token, hostname, alias, ip_address, arch, os, last_seen_at, online + FROM agents WHERE token = ?`, token) + return scanAgent(row) +} + +func (s *Store) GetAgent(id string) (*Agent, error) { + row := s.db.QueryRow(` + SELECT id, token, hostname, alias, ip_address, arch, os, last_seen_at, online + FROM agents WHERE id = ?`, id) + return scanAgent(row) +} + +func (s *Store) ListAgents() ([]*Agent, error) { + rows, err := s.db.Query(` + SELECT id, token, hostname, alias, ip_address, arch, os, last_seen_at, online + FROM agents ORDER BY hostname`) + if err != nil { + return nil, err + } + defer rows.Close() + + var agents []*Agent + for rows.Next() { + a := &Agent{} + var online int + var lastSeen sql.NullTime + if err := rows.Scan(&a.ID, &a.Token, &a.Hostname, &a.Alias, &a.IPAddress, &a.Arch, &a.OS, &lastSeen, &online); err != nil { + return nil, err + } + if lastSeen.Valid { + a.LastSeenAt = lastSeen.Time + } + a.Online = online == 1 + agents = append(agents, a) + } + return agents, rows.Err() +} + +func (s *Store) SetAgentOffline(id string) error { + _, err := s.db.Exec(`UPDATE agents SET online = 0 WHERE id = ?`, id) + return err +} + +func (s *Store) CreateAgentToken(id, token, hostname string) error { + _, err := s.db.Exec(` + INSERT OR IGNORE INTO agents (id, token, hostname, arch, os, online) + VALUES (?, ?, ?, '', '', 0) + `, id, token, hostname) + return err +} + +func (s *Store) UpdateAgentAlias(id, alias string) error { + _, err := s.db.Exec(`UPDATE agents SET alias = ? WHERE id = ?`, alias, id) + return err +} + +// ── Users ───────────────────────────────────────────────────────────────────── + +func (s *Store) GetUserHash(username string) (string, error) { + var hash string + err := s.db.QueryRow(`SELECT password_hash FROM users WHERE username = ?`, username).Scan(&hash) + return hash, err +} + +func (s *Store) UpsertUser(username, hash string) error { + _, err := s.db.Exec(` + INSERT INTO users (username, password_hash) VALUES (?, ?) + ON CONFLICT(username) DO UPDATE SET password_hash = excluded.password_hash + `, username, hash) + return err +} + +func (s *Store) UserExists(username string) (bool, error) { + var n int + err := s.db.QueryRow(`SELECT COUNT(*) FROM users WHERE username = ?`, username).Scan(&n) + return n > 0, err +} + +func scanAgent(row *sql.Row) (*Agent, error) { + a := &Agent{} + var online int + var lastSeen sql.NullTime + err := row.Scan(&a.ID, &a.Token, &a.Hostname, &a.Alias, &a.IPAddress, &a.Arch, &a.OS, &lastSeen, &online) + if err != nil { + return nil, err + } + if lastSeen.Valid { + a.LastSeenAt = lastSeen.Time + } + a.Online = online == 1 + return a, nil +} + +func boolToInt(b bool) int { + if b { + return 1 + } + return 0 +} diff --git a/server/internal/store/store_test.go b/server/internal/store/store_test.go new file mode 100644 index 0000000..adeade8 --- /dev/null +++ b/server/internal/store/store_test.go @@ -0,0 +1,208 @@ +package store + +import ( + "testing" +) + +func newTestStore(t *testing.T) *Store { + t.Helper() + s, err := New(":memory:") + if err != nil { + t.Fatalf("failed to open in-memory store: %v", err) + } + t.Cleanup(func() { s.Close() }) + return s +} + +// ── Users ───────────────────────────────────────────────────────────────────── + +func TestUpsertAndGetUserHash(t *testing.T) { + s := newTestStore(t) + + if err := s.UpsertUser("alice", "hash123"); err != nil { + t.Fatalf("UpsertUser: %v", err) + } + + h, err := s.GetUserHash("alice") + if err != nil { + t.Fatalf("GetUserHash: %v", err) + } + if h != "hash123" { + t.Errorf("expected hash123, got %q", h) + } +} + +func TestGetUserHash_NotFound(t *testing.T) { + s := newTestStore(t) + + _, err := s.GetUserHash("nobody") + if err == nil { + t.Fatal("expected error for missing user, got nil") + } +} + +func TestUpsertUser_Update(t *testing.T) { + s := newTestStore(t) + + if err := s.UpsertUser("alice", "first"); err != nil { + t.Fatalf("UpsertUser: %v", err) + } + if err := s.UpsertUser("alice", "second"); err != nil { + t.Fatalf("UpsertUser update: %v", err) + } + + h, err := s.GetUserHash("alice") + if err != nil { + t.Fatalf("GetUserHash: %v", err) + } + if h != "second" { + t.Errorf("expected second, got %q", h) + } +} + +func TestUserExists(t *testing.T) { + s := newTestStore(t) + + ok, err := s.UserExists("alice") + if err != nil { + t.Fatalf("UserExists: %v", err) + } + if ok { + t.Error("expected false for non-existent user") + } + + _ = s.UpsertUser("alice", "hash") + + ok, err = s.UserExists("alice") + if err != nil { + t.Fatalf("UserExists: %v", err) + } + if !ok { + t.Error("expected true after insert") + } +} + +// ── Agents ──────────────────────────────────────────────────────────────────── + +func TestCreateAgentToken(t *testing.T) { + s := newTestStore(t) + + if err := s.CreateAgentToken("id1", "tok1", "host1"); err != nil { + t.Fatalf("CreateAgentToken: %v", err) + } + + a, err := s.AgentByToken("tok1") + if err != nil { + t.Fatalf("AgentByToken: %v", err) + } + if a.ID != "id1" || a.Hostname != "host1" { + t.Errorf("unexpected agent: %+v", a) + } +} + +func TestAgentByToken_NotFound(t *testing.T) { + s := newTestStore(t) + + _, err := s.AgentByToken("doesnotexist") + if err == nil { + t.Fatal("expected error for unknown token") + } +} + +func TestUpsertAgent(t *testing.T) { + s := newTestStore(t) + + a := &Agent{ + ID: "agent1", + Token: "tok1", + Hostname: "myhost", + Alias: "myalias", + IPAddress: "10.0.0.1", + Arch: "amd64", + OS: "linux", + Online: true, + } + if err := s.UpsertAgent(a); err != nil { + t.Fatalf("UpsertAgent: %v", err) + } + + got, err := s.GetAgent("agent1") + if err != nil { + t.Fatalf("GetAgent: %v", err) + } + if got.Hostname != "myhost" || got.Alias != "myalias" { + t.Errorf("unexpected agent: %+v", got) + } +} + +func TestListAgents(t *testing.T) { + s := newTestStore(t) + + _ = s.CreateAgentToken("a1", "t1", "host-b") + _ = s.CreateAgentToken("a2", "t2", "host-a") + + agents, err := s.ListAgents() + if err != nil { + t.Fatalf("ListAgents: %v", err) + } + if len(agents) != 2 { + t.Fatalf("expected 2 agents, got %d", len(agents)) + } + // ORDER BY hostname: host-a < host-b + if agents[0].Hostname != "host-a" || agents[1].Hostname != "host-b" { + t.Errorf("unexpected order: %v %v", agents[0].Hostname, agents[1].Hostname) + } +} + +func TestSetAgentOffline(t *testing.T) { + s := newTestStore(t) + + _ = s.UpsertAgent(&Agent{ + ID: "a1", + Token: "t1", + Hostname: "h1", + Online: true, + }) + + if err := s.SetAgentOffline("a1"); err != nil { + t.Fatalf("SetAgentOffline: %v", err) + } + + a, err := s.GetAgent("a1") + if err != nil { + t.Fatalf("GetAgent: %v", err) + } + if a.Online { + t.Error("expected Online=false after SetAgentOffline") + } +} + +func TestUpdateAgentAlias(t *testing.T) { + s := newTestStore(t) + + _ = s.CreateAgentToken("a1", "t1", "host1") + + if err := s.UpdateAgentAlias("a1", "newalias"); err != nil { + t.Fatalf("UpdateAgentAlias: %v", err) + } + + a, err := s.GetAgent("a1") + if err != nil { + t.Fatalf("GetAgent: %v", err) + } + if a.Alias != "newalias" { + t.Errorf("expected alias 'newalias', got %q", a.Alias) + } +} + +func TestCreateAgentToken_IdempotentIgnore(t *testing.T) { + s := newTestStore(t) + + // INSERT OR IGNORE — second call should not error + if err := s.CreateAgentToken("id1", "tok1", "h1"); err != nil { + t.Fatalf("first call: %v", err) + } + if err := s.CreateAgentToken("id1", "tok1", "h1"); err != nil { + t.Fatalf("second call (should be idempotent): %v", err) + } +} diff --git a/web/package-lock.json b/web/package-lock.json new file mode 100644 index 0000000..661601d --- /dev/null +++ b/web/package-lock.json @@ -0,0 +1,8613 @@ +{ + "name": "containarr-web", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "containarr-web", + "version": "0.1.0", + "devDependencies": { + "@sveltejs/adapter-static": "^3.0.6", + "@sveltejs/kit": "^2.16.0", + "@sveltejs/vite-plugin-svelte": "^5.0.3", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/svelte": "^5.3.1", + "@vite-pwa/sveltekit": "^1.1.0", + "@vitest/coverage-v8": "^4.1.6", + "autoprefixer": "^10.4.20", + "jsdom": "^29.1.1", + "postcss": "^8.5.1", + "svelte": "^5.0.0", + "svelte-check": "^4.1.4", + "tailwindcss": "^3.4.17", + "typescript": "^5.7.3", + "vite": "^6.0.7", + "vitest": "^4.1.6", + "workbox-window": "^7.3.0" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", + "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@apideck/better-ajv-errors": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.7.tgz", + "integrity": "sha512-TajUJwGWbDwkCx/CZi7tRE8PVB7simCvKJfHUsSdvps+aTM/PDPP4gkLmKnc+x3CE//y9i/nj74GqdL/hwk7Iw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsonpointer": "^5.0.1", + "leven": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "ajv": ">=8" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "5.1.11", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz", + "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@csstools/css-calc": "^3.2.0", + "@csstools/css-color-parser": "^4.1.0", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz", + "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/generational-cache": "^1.0.1", + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.2.1", + "is-potential-custom-element-name": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/generational-cache": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz", + "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.3.tgz", + "integrity": "sha512-RpLYy2sb51oNLjuu1iD3bwBqCBWUzjO0ocp+iaCP/lJtb2CPLcnC2Fftw+4sAzaMELGeWTgExSKADbdo0GFVzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.29.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.28.5.tgz", + "integrity": "sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "regexpu-core": "^6.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", + "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "debug": "^4.4.3", + "lodash.debounce": "^4.0.8", + "resolve": "^1.22.11" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", + "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz", + "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.28.5", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.28.6.tgz", + "integrity": "sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.28.5.tgz", + "integrity": "sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", + "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.3.tgz", + "integrity": "sha512-SRS46DFR4HqzUzCVgi90/xMoL+zeBDBvWdKYXSEzh79kXswNFEglUpMKxR04//dPqwYXWUBJ3mpUd933ru9Kmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.28.6.tgz", + "integrity": "sha512-a0aBScVTlNaiUe35UtfxAN7A/tehvvG4/ByO6+46VPKTRSlfnAFsgKy0FUh+qAkQrDTmhDkT+IBOKlOoMUxQ0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.28.6.tgz", + "integrity": "sha512-pSJUpFHdx9z5nqTSirOCMtYVP2wFgoWhP0p3g8ONK/4IHhLIBd0B9NYqAvIUAhq+OkhO4VM1tENCt0cjlsNShw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.0.tgz", + "integrity": "sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.28.6.tgz", + "integrity": "sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-remap-async-to-generator": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.6.tgz", + "integrity": "sha512-tt/7wOtBmwHPNMPu7ax4pdPz6shjFrmHDghvNC+FG9Qvj7D6mJcoRQIF5dy4njmxR941l6rgtvfSB2zX3VlUIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.28.6.tgz", + "integrity": "sha512-dY2wS3I2G7D697VHndN91TJr8/AAfXQNt5ynCTI/MpxMsSzHp+52uNivYT5wCPax3whc47DR8Ba7cmlQMg24bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.28.6.tgz", + "integrity": "sha512-rfQ++ghVwTWTqQ7w8qyDxL1XGihjBss4CmTgGRCTAC9RIbhVpyp4fOeZtta0Lbf+dTNIVJer6ych2ibHwkZqsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.6.tgz", + "integrity": "sha512-EF5KONAqC5zAqT783iMGuM2ZtmEBy+mJMOKl2BCvPZ2lVrwvXnB6o+OBWCS+CoeCCpVRF2sA2RBKUxvT8tQT5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-globals": "^7.28.0", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-replace-supers": "^7.28.6", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.28.6.tgz", + "integrity": "sha512-bcc3k0ijhHbc2lEfpFHgx7eYw9KNXqOerKWfzbxEHUGKnS3sz9C4CNL9OiFN1297bDNfUiSO7DaLzbvHQQQ1BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/template": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", + "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.28.6.tgz", + "integrity": "sha512-SljjowuNKB7q5Oayv4FoPzeB74g3QgLt8IVJw9ADvWy3QnUb/01aw8I4AVv8wYnPvQz2GDDZ/g3GhcNyDBI4Bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.0.tgz", + "integrity": "sha512-zBPcW2lFGxdiD8PUnPwJjag2J9otbcLQzvbiOzDxpYXyCuYX9agOwMPGn1prVH0a4qzhCKu24rlH4c1f7yA8rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-explicit-resource-management": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.28.6.tgz", + "integrity": "sha512-Iao5Konzx2b6g7EPqTy40UZbcdXE126tTxVFr/nAIj+WItNxjKSYTEw3RC+A2/ZetmdJsgueL1KhaMCQHkLPIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.28.6.tgz", + "integrity": "sha512-WitabqiGjV/vJ0aPOLSFfNY1u9U3R7W36B03r5I2KoNix+a3sOhJ3pKFB3R5It9/UiK78NiO0KE9P21cMhlPkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.28.6.tgz", + "integrity": "sha512-Nr+hEN+0geQkzhbdgQVPoqr47lZbm+5fCUmO70722xJZd0Mvb59+33QLImGj6F+DkK3xgDi1YVysP8whD6FQAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.28.6.tgz", + "integrity": "sha512-+anKKair6gpi8VsM/95kmomGNMD0eLz1NQ8+Pfw5sAwWH9fGYXT50E55ZpV0pHUHWf6IUTWPM+f/7AAff+wr9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.28.6.tgz", + "integrity": "sha512-jppVbf8IV9iWWwWTQIxJMAJCWBuuKx71475wHwYytrRGQ2CWiDvYlADQno3tcYpS/T2UUWFQp3nVtYfK/YBQrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.29.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.4.tgz", + "integrity": "sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.0.tgz", + "integrity": "sha512-1CZQA5KNAD6ZYQLPw7oi5ewtDNxH/2vuCh+6SmvgDfhumForvs8a1o9n0UrEoBD8HU4djO2yWngTQlXl1NDVEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.28.6.tgz", + "integrity": "sha512-3wKbRgmzYbw24mDJXT7N+ADXw8BC/imU9yo9c9X9NKaLF1fW+e5H1U5QjMUBe4Qo4Ox/o++IyUkl1sVCLgevKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.28.6.tgz", + "integrity": "sha512-SJR8hPynj8outz+SlStQSwvziMN4+Bq99it4tMIf5/Caq+3iOc0JtKyse8puvyXkk3eFRIA5ID/XfunGgO5i6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.28.6.tgz", + "integrity": "sha512-5rh+JR4JBC4pGkXLAcYdLHZjXudVxWMXbB6u6+E9lRL5TrGVbHt1TjxGbZ8CkmYw9zjkB7jutzOROArsqtncEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.28.6.tgz", + "integrity": "sha512-R8ja/Pyrv0OGAvAXQhSTmWyPJPml+0TMqXlO5w+AsMEiwb2fg3WkOvob7UxFSL3OIttFSGSRFKQsOhJ/X6HQdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.28.6.tgz", + "integrity": "sha512-A4zobikRGJTsX9uqVFdafzGkqD30t26ck2LmOzAuLL8b2x6k3TIqRiT2xVvA9fNmFeTX484VpsdgmKNA0bS23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", + "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.28.6.tgz", + "integrity": "sha512-piiuapX9CRv7+0st8lmuUlRSmX6mBcVeNQ1b4AYzJxfCMuBfB0vBXDiGSmm03pKJw1v6cZ8KSeM+oUnM6yAExg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.28.6.tgz", + "integrity": "sha512-b97jvNSOb5+ehyQmBpmhOCiUC5oVK4PMnpRvO7+ymFBoqYjeDHIU9jnrNUuwHOiL9RpGDoKBpSViarV+BU+eVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.27.3", + "@babel/helper-create-class-features-plugin": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.0.tgz", + "integrity": "sha512-FijqlqMA7DmRdg/aINBSs04y8XNTYw/lr1gJ2WsmBnnaNw1iS43EPkJW+zK7z65auG3AWRFXWj+NcTQwYptUog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.28.6.tgz", + "integrity": "sha512-QGWAepm9qxpaIs7UM9FvUSnCGlb8Ua1RhyM4/veAxLwt3gMat/LSGrZixyuj4I6+Kn9iwvqCyPTtbdxanYoWYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.28.6.tgz", + "integrity": "sha512-9U4QObUC0FtJl05AsUcodau/RWDytrU6uKgkxu09mLR9HLDAtUMoPuuskm5huQsoktmsYpI+bGmq+iapDcriKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.28.6.tgz", + "integrity": "sha512-4Wlbdl/sIZjzi/8St0evF0gEZrgOswVO6aOzqxh1kDZOl9WmLrHq2HtGhnOJZmHZYKP8WZ1MDLCt5DAWwRo57A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.28.6.tgz", + "integrity": "sha512-/wHc/paTUmsDYN7SZkpWxogTOBNnlx7nBQYfy6JJlCT7G3mVhltk3e++N7zV0XfgGsrqBxd4rJQt9H16I21Y1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.28.5", + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.29.5", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.5.tgz", + "integrity": "sha512-/69t2aEzGKHD76DyLbHysF/QH2LJOB8iFnYO37unDTKBTubzcMRv0f3H5EiN1Q6ajOd/eB7dAInF0qdFVS06kA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.3", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.28.5", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.3", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.6", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.28.6", + "@babel/plugin-syntax-import-attributes": "^7.28.6", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.29.0", + "@babel/plugin-transform-async-to-generator": "^7.28.6", + "@babel/plugin-transform-block-scoped-functions": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.6", + "@babel/plugin-transform-class-properties": "^7.28.6", + "@babel/plugin-transform-class-static-block": "^7.28.6", + "@babel/plugin-transform-classes": "^7.28.6", + "@babel/plugin-transform-computed-properties": "^7.28.6", + "@babel/plugin-transform-destructuring": "^7.28.5", + "@babel/plugin-transform-dotall-regex": "^7.28.6", + "@babel/plugin-transform-duplicate-keys": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.0", + "@babel/plugin-transform-dynamic-import": "^7.27.1", + "@babel/plugin-transform-explicit-resource-management": "^7.28.6", + "@babel/plugin-transform-exponentiation-operator": "^7.28.6", + "@babel/plugin-transform-export-namespace-from": "^7.27.1", + "@babel/plugin-transform-for-of": "^7.27.1", + "@babel/plugin-transform-function-name": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.28.6", + "@babel/plugin-transform-literals": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.28.6", + "@babel/plugin-transform-member-expression-literals": "^7.27.1", + "@babel/plugin-transform-modules-amd": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.28.6", + "@babel/plugin-transform-modules-systemjs": "^7.29.4", + "@babel/plugin-transform-modules-umd": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.0", + "@babel/plugin-transform-new-target": "^7.27.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.28.6", + "@babel/plugin-transform-numeric-separator": "^7.28.6", + "@babel/plugin-transform-object-rest-spread": "^7.28.6", + "@babel/plugin-transform-object-super": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.28.6", + "@babel/plugin-transform-optional-chaining": "^7.28.6", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/plugin-transform-private-methods": "^7.28.6", + "@babel/plugin-transform-private-property-in-object": "^7.28.6", + "@babel/plugin-transform-property-literals": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.29.0", + "@babel/plugin-transform-regexp-modifiers": "^7.28.6", + "@babel/plugin-transform-reserved-words": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-spread": "^7.28.6", + "@babel/plugin-transform-sticky-regex": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-typeof-symbol": "^7.27.1", + "@babel/plugin-transform-unicode-escapes": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.28.6", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.28.6", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.15", + "babel-plugin-polyfill-corejs3": "^0.14.0", + "babel-plugin-polyfill-regenerator": "^0.6.6", + "core-js-compat": "^3.48.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", + "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.1.tgz", + "integrity": "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.1.tgz", + "integrity": "sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.0.2", + "@csstools/css-calc": "^3.2.1" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.4.tgz", + "integrity": "sha512-wgsqt92b7C7tQhIdPNxj0n9zuUbQlvAuI1exyzeNrOKOi62SD7ren8zqszmpVREjAOqg8cD2FqYhQfAuKjk4sw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@exodus/bytes": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", + "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "node_modules/@isaacs/cliui": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-babel": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.1.0.tgz", + "integrity": "sha512-dFZNuFD2YRcoomP4oYf+DvQNSUA9ih+A3vUqopQx5EdtPGo3WBnQcI/S8pwpz91UsGfL0HsMSOlaMld8HrbubA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@rollup/pluginutils": "^5.0.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "@types/babel__core": { + "optional": true + }, + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.3.tgz", + "integrity": "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-replace": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.3.tgz", + "integrity": "sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-terser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-1.0.0.tgz", + "integrity": "sha512-FnCxhTBx6bMOYQrar6C8h3scPt8/JwIzw3+AJ2K++6guogH5fYaIFia+zZuhqv0eo1RN7W1Pz630SyvLbDjhtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "serialize-javascript": "^7.0.3", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", + "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz", + "integrity": "sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz", + "integrity": "sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz", + "integrity": "sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz", + "integrity": "sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz", + "integrity": "sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz", + "integrity": "sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz", + "integrity": "sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz", + "integrity": "sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz", + "integrity": "sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz", + "integrity": "sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz", + "integrity": "sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz", + "integrity": "sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz", + "integrity": "sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz", + "integrity": "sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz", + "integrity": "sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz", + "integrity": "sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz", + "integrity": "sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz", + "integrity": "sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz", + "integrity": "sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz", + "integrity": "sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz", + "integrity": "sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz", + "integrity": "sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz", + "integrity": "sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz", + "integrity": "sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.4.tgz", + "integrity": "sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sveltejs/acorn-typescript": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.9.tgz", + "integrity": "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^8.9.0" + } + }, + "node_modules/@sveltejs/adapter-static": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-static/-/adapter-static-3.0.10.tgz", + "integrity": "sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@sveltejs/kit": "^2.0.0" + } + }, + "node_modules/@sveltejs/kit": { + "version": "2.60.1", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.60.1.tgz", + "integrity": "sha512-mQjlkNo+rJvpln7V2IGY2j99BqhcFbS4UN0AQNKNYfhBAFZTuCDAdW3a1sgf330mvtNvsBXn3HpAhcmvdJTcIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@sveltejs/acorn-typescript": "^1.0.5", + "@types/cookie": "^0.6.0", + "acorn": "^8.14.1", + "cookie": "^0.6.0", + "devalue": "^5.8.1", + "esm-env": "^1.2.2", + "kleur": "^4.1.5", + "magic-string": "^0.30.5", + "mrmime": "^2.0.0", + "set-cookie-parser": "^3.0.0", + "sirv": "^3.0.0" + }, + "bin": { + "svelte-kit": "svelte-kit.js" + }, + "engines": { + "node": ">=18.13" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.0.0", + "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0 || ^7.0.0", + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": "^5.3.3 || ^6.0.0", + "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@sveltejs/vite-plugin-svelte": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-5.1.1.tgz", + "integrity": "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", + "debug": "^4.4.1", + "deepmerge": "^4.3.1", + "kleur": "^4.1.5", + "magic-string": "^0.30.17", + "vitefu": "^1.0.6" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22" + }, + "peerDependencies": { + "svelte": "^5.0.0", + "vite": "^6.0.0" + } + }, + "node_modules/@sveltejs/vite-plugin-svelte-inspector": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-4.0.1.tgz", + "integrity": "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.7" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22" + }, + "peerDependencies": { + "@sveltejs/vite-plugin-svelte": "^5.0.0", + "svelte": "^5.0.0", + "vite": "^6.0.0" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/@testing-library/dom/node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/svelte": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/@testing-library/svelte/-/svelte-5.3.1.tgz", + "integrity": "sha512-8Ez7ZOqW5geRf9PF5rkuopODe5RGy3I9XR+kc7zHh26gBiktLaxTfKmhlGaSHYUOTQE7wFsLMN9xCJVCszw47w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@testing-library/dom": "9.x.x || 10.x.x", + "@testing-library/svelte-core": "1.0.0" + }, + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0", + "vite": "*", + "vitest": "*" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + }, + "vitest": { + "optional": true + } + } + }, + "node_modules/@testing-library/svelte-core": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@testing-library/svelte-core/-/svelte-core-1.0.0.tgz", + "integrity": "sha512-VkUePoLV6oOYwSUvX6ShA8KLnJqZiYMIbP2JW2t0GLWLkJxKGvuH5qrrZBV/X7cXFnLGuFQEC7RheYiZOW68KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "svelte": "^3 || ^4 || ^5 || ^5.0.0-next.0" + } + }, + "node_modules/@trickfilm400/rollup-plugin-off-main-thread": { + "version": "3.0.0-pre1", + "resolved": "https://registry.npmjs.org/@trickfilm400/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-3.0.0-pre1.tgz", + "integrity": "sha512-/67zpWDBLV+oYAEL682s1ktXL0HgqX76f6gaVGkGnVZlBbm1zd0v4Bz8MFF2GGhoX9rvfq3KSQHubFHwa6w6/Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "ejs": "^3.1.10", + "json5": "^2.2.3", + "magic-string": "^0.30.21", + "string.prototype.matchall": "^4.0.12" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vite-pwa/sveltekit": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@vite-pwa/sveltekit/-/sveltekit-1.1.0.tgz", + "integrity": "sha512-mMIf2tY+7Hg8jecpu/WY+Ki2ikoXy3hVmt3tOxi0K+lYYnKQrDYthuHireI0S+26Mg9BXzL7qQF1xeB5VYlYlg==", + "dev": true, + "license": "MIT", + "dependencies": { + "kolorist": "^1.8.0", + "tinyglobby": "^0.2.9", + "vite-plugin-pwa": "^1.2.0" + }, + "engines": { + "node": ">=18.13" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@sveltejs/kit": "^1.3.1 || ^2.0.1", + "@vite-pwa/assets-generator": "^1.0.0" + }, + "peerDependenciesMeta": { + "@vite-pwa/assets-generator": { + "optional": true + } + } + }, + "node_modules/@vitest/coverage-v8": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.6.tgz", + "integrity": "sha512-36l628fQ/9a/8ihy97eOtEnvWQEdqULQOJtcaxtoNq0G1w3Mxd4szSahOaMM9/NGyZ+hyKcMtIW/WIxq0XQViQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/utils": "4.1.6", + "ast-v8-to-istanbul": "^1.0.0", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.2", + "obug": "^2.1.1", + "std-env": "^4.0.0-rc.1", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "4.1.6", + "vitest": "4.1.6" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.6.tgz", + "integrity": "sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.6", + "@vitest/utils": "4.1.6", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.6.tgz", + "integrity": "sha512-MCFc63czMjEInOlcY2cpQCvCN+KgbAn+60xu9cMgP4sKaLC5JNAKw7JH8QdAnoAC88hW1IiSNZ+GgVXlN1UcMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.6", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/mocker/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.6.tgz", + "integrity": "sha512-h5SxD/IzNhZYnrSZRsUZQIC+vD0GY8cUvq0iwsmkFKixRCKLLWqCXa/FIQ4S1R+sI+PGoojkHsdNrbZiM9Qpgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.6.tgz", + "integrity": "sha512-nOPCmn2+yD0ZNmKdsXGv/UxMMWbMuKeD6GyYncNwdkYDxpQvrPSKYj2rWuDjC2Y4b6w6hjip5dBKFzEUuZe3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.6", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.6.tgz", + "integrity": "sha512-YhsdE6xAVfTDmzjxL2ZDUvjj+ZsgyOKe+TdQzqkD72wIOmHka8NuGQ6NpTNZv9D2Z63fbwWKJPeVpEw4EQgYxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.6", + "@vitest/utils": "4.1.6", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.6.tgz", + "integrity": "sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.6.tgz", + "integrity": "sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.6", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/aria-query": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.1.tgz", + "integrity": "sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/ast-v8-to-istanbul": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.0.tgz", + "integrity": "sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.0.tgz", + "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "caniuse-lite": "^1.0.30001787", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", + "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-define-polyfill-provider": "^0.6.8", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", + "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8", + "core-js-compat": "^3.48.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", + "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.30", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.30.tgz", + "integrity": "sha512-xjOFN16Ha1+Rz4nFYKqHU/LSB+gx/Vi3yQLX7r7sAW+Wa+8hhF2h4pvqTrTMc8+WcDBEunnUurr46Jvv0jk3Vg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001793", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/core-js-compat": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", + "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/data-urls/node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/data-urls/node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/devalue": { + "version": "5.8.1", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.1.tgz", + "integrity": "sha512-4CXDYRBGqN+57wVJkuXBYmpAVUSg3L6JAQa/DFqm238G73E1wuyc/JhGQJzN7vUf/CMphYau2zXbfWzDR5aTEw==", + "dev": true, + "license": "MIT" + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.357", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.357.tgz", + "integrity": "sha512-NHlTIQDK8fmVwHwuIzmXYEJ1Ewq3D9wDNc0cWXxDGysP6Pb21giwGNkxiTifyKy/4SoPuN5l6GLP1W9Sv7zB2g==", + "dev": true, + "license": "ISC" + }, + "node_modules/entities": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-abstract": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/esm-env": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.2.2.tgz", + "integrity": "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esrap": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.9.tgz", + "integrity": "sha512-4KijP+NxCWthMCUC3qHbE6n4vCjqgJS1uAYKhuT/GWfFTf1Qyive2TgOjep+gzbSzRfnNyaN/UU9YmdOt8Eg0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "peerDependencies": { + "@typescript-eslint/types": "^8.2.0" + }, + "peerDependenciesMeta": { + "@typescript-eslint/types": { + "optional": true + } + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eta": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eta/-/eta-4.6.0.tgz", + "integrity": "sha512-lW6is4T1NFOYnmqGZIfvixqj7A7sSvScF+DN8EK6K58xI5MZ5UvYe0GjopxOXQtZvUn4eDdVuZ8XSoYWTMEKwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/bgub/eta?sponsor=1" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true, + "license": "ISC" + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-reference": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz", + "integrity": "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.6" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", + "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^9.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsdom": { + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz", + "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^5.1.11", + "@asamuzakjp/dom-selector": "^7.1.1", + "@bramus/specificity": "^2.4.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.3", + "@exodus/bytes": "^1.15.0", + "css-tree": "^3.2.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.3.5", + "parse5": "^8.0.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.1", + "undici": "^7.25.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.1", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/lru-cache": { + "version": "11.3.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz", + "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/jsdom/node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/jsdom/node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/jsdom/node_modules/whatwg-url": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/kolorist": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", + "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-character": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz", + "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/magicast": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.3.tgz", + "integrity": "sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.3", + "@babel/types": "^7.29.0", + "source-map-js": "^1.2.1" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.44", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.44.tgz", + "integrity": "sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.3.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz", + "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pretty-bytes": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz", + "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", + "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpu-core": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", + "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.2", + "regjsgen": "^0.8.0", + "regjsparser": "^0.13.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.2.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.1.tgz", + "integrity": "sha512-dLsljMd9sqwRkby8zhO1gSg3PnJIBFid8f4CQj/sXx+7cKx+E7u0PKhZ+U4wmhx7EfmtvnA318oVaIkAB1lRJw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.1.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.60.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz", + "integrity": "sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.4", + "@rollup/rollup-android-arm64": "4.60.4", + "@rollup/rollup-darwin-arm64": "4.60.4", + "@rollup/rollup-darwin-x64": "4.60.4", + "@rollup/rollup-freebsd-arm64": "4.60.4", + "@rollup/rollup-freebsd-x64": "4.60.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.4", + "@rollup/rollup-linux-arm-musleabihf": "4.60.4", + "@rollup/rollup-linux-arm64-gnu": "4.60.4", + "@rollup/rollup-linux-arm64-musl": "4.60.4", + "@rollup/rollup-linux-loong64-gnu": "4.60.4", + "@rollup/rollup-linux-loong64-musl": "4.60.4", + "@rollup/rollup-linux-ppc64-gnu": "4.60.4", + "@rollup/rollup-linux-ppc64-musl": "4.60.4", + "@rollup/rollup-linux-riscv64-gnu": "4.60.4", + "@rollup/rollup-linux-riscv64-musl": "4.60.4", + "@rollup/rollup-linux-s390x-gnu": "4.60.4", + "@rollup/rollup-linux-x64-gnu": "4.60.4", + "@rollup/rollup-linux-x64-musl": "4.60.4", + "@rollup/rollup-openbsd-x64": "4.60.4", + "@rollup/rollup-openharmony-arm64": "4.60.4", + "@rollup/rollup-win32-arm64-msvc": "4.60.4", + "@rollup/rollup-win32-ia32-msvc": "4.60.4", + "@rollup/rollup-win32-x64-gnu": "4.60.4", + "@rollup/rollup-win32-x64-msvc": "4.60.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup/node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/sade": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", + "integrity": "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mri": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/serialize-javascript": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz", + "integrity": "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/set-cookie-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.0.tgz", + "integrity": "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/smob": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.6.2.tgz", + "integrity": "sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "deprecated": "The work that was done in this beta branch won't be included in future versions", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", + "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svelte": { + "version": "5.55.7", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.55.7.tgz", + "integrity": "sha512-ymI5ykLPwIHW839E053FQbI1G+jnRFJEw3Kv5Y4njixVWywQBx+NUFpkkKyk5LIb36Fg9DVXSYpqiGekLD0hyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.4", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@sveltejs/acorn-typescript": "^1.0.5", + "@types/estree": "^1.0.5", + "@types/trusted-types": "^2.0.7", + "acorn": "^8.12.1", + "aria-query": "5.3.1", + "axobject-query": "^4.1.0", + "clsx": "^2.1.1", + "devalue": "^5.8.1", + "esm-env": "^1.2.1", + "esrap": "^2.2.4", + "is-reference": "^3.0.3", + "locate-character": "^3.0.0", + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/svelte-check": { + "version": "4.4.8", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.4.8.tgz", + "integrity": "sha512-67adfgBox5eNSNIvIIwgFizKGdcRrGpiMoNO2obHcYuLz7iTa8Xgm/NGU3ntMFnNm8K1grFOIG6HhMLX/vcN8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "chokidar": "^4.0.1", + "fdir": "^6.2.0", + "picocolors": "^1.0.0", + "sade": "^1.7.4" + }, + "bin": { + "svelte-check": "bin/svelte-check" + }, + "engines": { + "node": ">= 18.0.0" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.0", + "typescript": ">=5.0.0" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/tailwindcss/node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tailwindcss/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tailwindcss/node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tempy": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", + "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/terser": { + "version": "5.47.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.47.1.tgz", + "integrity": "sha512-tPbLXTI6ohPASb/1YViL428oEHu6/qv1OxqYnfaonVCFHqx4+wCd95pHrQWsL5X4pl90CTyW9piSAsS2L0VoMw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "7.0.30", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.30.tgz", + "integrity": "sha512-ELrFxuqsDdHUwoh0XxDbxuLD3Wnz49Z57IFvTtvWy1hJdcMZjXLIuonjilCiWHlT2GbE4Wlv1wKVTzDFnXH1aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.30" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.30", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.30.tgz", + "integrity": "sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", + "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", + "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", + "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", + "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz", + "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-plugin-pwa": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-1.3.0.tgz", + "integrity": "sha512-c5kMgN+ITrOtHXp8PAtk2uOIEea6XjP/unCGxOWWBzQ6qa65qj/awHg0wf+QF9E/2u9vh86LqxPwzEPNbM2r5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.6", + "pretty-bytes": "^6.1.1", + "tinyglobby": "^0.2.10", + "workbox-build": "^7.4.1", + "workbox-window": "^7.4.1" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vite-pwa/assets-generator": "^1.0.0", + "vite": "^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0", + "workbox-build": "^7.4.1", + "workbox-window": "^7.4.1" + }, + "peerDependenciesMeta": { + "@vite-pwa/assets-generator": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "dev": true, + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.6.tgz", + "integrity": "sha512-6lvjbS3p9b4CrdCmguzbh2/4uoXhGE2q71R4OX5sqF9R1bo9Xd6fGrMAfvp5wnCzlBnFVdCOp6onuTQVbo8iUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.6", + "@vitest/mocker": "4.1.6", + "@vitest/pretty-format": "4.1.6", + "@vitest/runner": "4.1.6", + "@vitest/snapshot": "4.1.6", + "@vitest/spy": "4.1.6", + "@vitest/utils": "4.1.6", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.6", + "@vitest/browser-preview": "4.1.6", + "@vitest/browser-webdriverio": "4.1.6", + "@vitest/coverage-istanbul": "4.1.6", + "@vitest/coverage-v8": "4.1.6", + "@vitest/ui": "4.1.6", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.20", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.20.tgz", + "integrity": "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/workbox-background-sync": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-7.4.1.tgz", + "integrity": "sha512-HhT7KE8tOWDm02wRNshXUnUPofMlhenF2DBdUnDPOubhizzPeItkYTmAB6td1Z2cjYPa98vzEiPLEuzn5hN66g==", + "dev": true, + "license": "MIT", + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "7.4.1" + } + }, + "node_modules/workbox-broadcast-update": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-7.4.1.tgz", + "integrity": "sha512-uAlgslKLvbQY+suirIdnBCSYrcgBhjp81Nj4l1lj/Jmj0MJO2CJERnCJjT0GFVwmReV0N+zs78K6gqd5gr9/+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.1" + } + }, + "node_modules/workbox-build": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-7.4.1.tgz", + "integrity": "sha512-SDhxIvEAde9Gy/5w4Yo1Jh/M49Z0qE3q0oteyE8zGq0DScxFqVBcCtIXFuLtmtxRQZCMbf0prco4VyEu3KBQuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@apideck/better-ajv-errors": "^0.3.1", + "@babel/core": "^7.24.4", + "@babel/preset-env": "^7.11.0", + "@babel/runtime": "^7.11.2", + "@rollup/plugin-babel": "^6.1.0", + "@rollup/plugin-node-resolve": "^16.0.3", + "@rollup/plugin-replace": "^6.0.3", + "@rollup/plugin-terser": "^1.0.0", + "@trickfilm400/rollup-plugin-off-main-thread": "^3.0.0-pre1", + "ajv": "^8.6.0", + "common-tags": "^1.8.0", + "eta": "^4.5.1", + "fast-json-stable-stringify": "^2.1.0", + "fs-extra": "^9.0.1", + "glob": "^11.0.1", + "pretty-bytes": "^5.3.0", + "rollup": "^4.53.3", + "source-map": "^0.8.0-beta.0", + "stringify-object": "^3.3.0", + "strip-comments": "^2.0.1", + "tempy": "^0.6.0", + "upath": "^1.2.0", + "workbox-background-sync": "7.4.1", + "workbox-broadcast-update": "7.4.1", + "workbox-cacheable-response": "7.4.1", + "workbox-core": "7.4.1", + "workbox-expiration": "7.4.1", + "workbox-google-analytics": "7.4.1", + "workbox-navigation-preload": "7.4.1", + "workbox-precaching": "7.4.1", + "workbox-range-requests": "7.4.1", + "workbox-recipes": "7.4.1", + "workbox-routing": "7.4.1", + "workbox-strategies": "7.4.1", + "workbox-streams": "7.4.1", + "workbox-sw": "7.4.1", + "workbox-window": "7.4.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/workbox-build/node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/workbox-cacheable-response": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-7.4.1.tgz", + "integrity": "sha512-8xaFoJdDc2OjrlbbL3gEeBO1WKcMwRqwLRupgqahYXu75yXajPLuwrbXMrIGZuWYXrQwk0xDjOxZ/ujCy/oJYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.1" + } + }, + "node_modules/workbox-core": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-7.4.1.tgz", + "integrity": "sha512-DT+vu46eh/2vRsSHTY4Xmc32Z1rr9PRlQUXr1Dx30ZuXRWwOsvZgGgcwxcasubQLQmbTNYZjv44LkBAQ4tT5tQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/workbox-expiration": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-7.4.1.tgz", + "integrity": "sha512-lRKUF7b+OGbeXkQk1s6MHXOa3d7Xxf7Of31W6c6hCfipfIyrtdWZ89stq21AHZMaoG7VNFoHply4Ox+rU31TWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "7.4.1" + } + }, + "node_modules/workbox-google-analytics": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-7.4.1.tgz", + "integrity": "sha512-Mks1JwLEt++ZAkF6sS1OpSh9RtAMIsiDgRpK+codiHGIPXeaUOgi4cPc3GFadUl8V5QPeypEk8Oxgl3HlwVzHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-background-sync": "7.4.1", + "workbox-core": "7.4.1", + "workbox-routing": "7.4.1", + "workbox-strategies": "7.4.1" + } + }, + "node_modules/workbox-navigation-preload": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-7.4.1.tgz", + "integrity": "sha512-C4KVsjPcYKJOhr631AxR9XoG2rLF3QiTk5aMv36MXOjtWvm8axwNFAtKUPGsWUwLXXAMgYM1En7fsvndaXeXRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.1" + } + }, + "node_modules/workbox-precaching": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-7.4.1.tgz", + "integrity": "sha512-cdr/9qByww7yzEp7zg/qI4ukUrrNjQLgN+ONQRpjy/VqGQXwkgHwr00KksGJK8v0VifwDXBb8a4cWNZH71jn3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.1", + "workbox-routing": "7.4.1", + "workbox-strategies": "7.4.1" + } + }, + "node_modules/workbox-range-requests": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-7.4.1.tgz", + "integrity": "sha512-7i2oxAUE82gHdAJBCAQ04JzNOdRPqzuOzGfoUyJpFSmeqBNYGPrAH8GPoPjUQTfp+NycwrD2H68VtuF8qxv0vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.1" + } + }, + "node_modules/workbox-recipes": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-7.4.1.tgz", + "integrity": "sha512-gnbVfmV4/TtmQaM4x9AtuXhcdstJsep3XMVeztOrQVPT+R6+6DeBjGTCQ7fFCXm+4GEHUA5VEBTyi5+4gWGeog==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-cacheable-response": "7.4.1", + "workbox-core": "7.4.1", + "workbox-expiration": "7.4.1", + "workbox-precaching": "7.4.1", + "workbox-routing": "7.4.1", + "workbox-strategies": "7.4.1" + } + }, + "node_modules/workbox-routing": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-7.4.1.tgz", + "integrity": "sha512-yubJGErZOusuidAenaL5ypfhQOa7urxP/f8E0ws7FPb4039RiWXUWBAyUkmUoOL/BcQGen3h0J8872d51IYxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.1" + } + }, + "node_modules/workbox-strategies": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-7.4.1.tgz", + "integrity": "sha512-GZxpaw9NbmOelj7667uZ2kpk5BFpOGbO4X0qjwh5ls8XQ8C+Lha5LQchTiUzsTFSS+NlUpftYAyOVXvQUrcqOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.1" + } + }, + "node_modules/workbox-streams": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-7.4.1.tgz", + "integrity": "sha512-HWWtraKUbJknd9kgqGcpQ3G114HOPYvqs8HaJMDs2ebLNAimDkVDaWfAXE6Ybl+m8U6KsCE6pWyLYuigWmnAXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.1", + "workbox-routing": "7.4.1" + } + }, + "node_modules/workbox-sw": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-7.4.1.tgz", + "integrity": "sha512-fez5f2DUlDJWTFYkCWQpY10N8gtztd849NswCbVFk0QlcSM4HT5A8x4g4ii650yem4I8tHY0R7JZahwp3ltIPw==", + "dev": true, + "license": "MIT" + }, + "node_modules/workbox-window": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-7.4.1.tgz", + "integrity": "sha512-notZDH2u8VXaqyuD7xaqIfEFi6SRM4SUSd7ewe9PDsVqADuepxX2ZMY3uvuZGxzY5ZOsGC/vD3A/3smFtJt4/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/trusted-types": "^2.0.2", + "workbox-core": "7.4.1" + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/zimmerframe": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", + "integrity": "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/web/package.json b/web/package.json new file mode 100644 index 0000000..c9b57df --- /dev/null +++ b/web/package.json @@ -0,0 +1,33 @@ +{ + "name": "containarr-web", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite dev", + "build": "vite build", + "preview": "vite preview", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "test": "vitest run", + "test:watch": "vitest" + }, + "devDependencies": { + "@sveltejs/adapter-static": "^3.0.6", + "@sveltejs/kit": "^2.16.0", + "@sveltejs/vite-plugin-svelte": "^5.0.3", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/svelte": "^5.3.1", + "@vite-pwa/sveltekit": "^1.1.0", + "@vitest/coverage-v8": "^4.1.6", + "autoprefixer": "^10.4.20", + "jsdom": "^29.1.1", + "postcss": "^8.5.1", + "svelte": "^5.0.0", + "svelte-check": "^4.1.4", + "tailwindcss": "^3.4.17", + "typescript": "^5.7.3", + "vite": "^6.0.7", + "vitest": "^4.1.6", + "workbox-window": "^7.3.0" + } +} diff --git a/web/postcss.config.js b/web/postcss.config.js new file mode 100644 index 0000000..2aa7205 --- /dev/null +++ b/web/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/web/src/app.css b/web/src/app.css new file mode 100644 index 0000000..0506806 --- /dev/null +++ b/web/src/app.css @@ -0,0 +1,86 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + * { box-sizing: border-box; } + + html { + font-family: "Inter", system-ui, -apple-system, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + } + + body { background-color: #050f0c; } + + code, .font-mono { + font-family: "JetBrains Mono", "Fira Code", ui-monospace, monospace; + } + + ::-webkit-scrollbar { width: 6px; height: 6px; } + ::-webkit-scrollbar-track { background: transparent; } + ::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.1); border-radius: 3px; } + ::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.18); } + + ::selection { background: rgba(59,130,246,0.3); color: #e8edf8; } +} + +@layer components { + .dot-running { + width: 7px; height: 7px; + border-radius: 50%; + background: #34d399; + box-shadow: 0 0 7px rgba(52,211,153,0.6); + flex-shrink: 0; + } + .dot-exited { + width: 7px; height: 7px; + border-radius: 50%; + background: #f87171; + flex-shrink: 0; + } + .dot-other { + width: 7px; height: 7px; + border-radius: 50%; + background: #fbbf24; + flex-shrink: 0; + } + .dot-online { + width: 8px; height: 8px; + border-radius: 50%; + background: #34d399; + box-shadow: 0 0 8px rgba(52,211,153,0.55); + flex-shrink: 0; + } + .dot-offline { + width: 8px; height: 8px; + border-radius: 50%; + background: #1e2f48; + flex-shrink: 0; + } + + .glass { + background: rgba(5,15,12,0.85); + backdrop-filter: blur(14px); + -webkit-backdrop-filter: blur(14px); + border-bottom: 1px solid rgba(16,185,129,0.08); + } + + .card { + background: #071410; + border: 1px solid rgba(255,255,255,0.06); + border-radius: 0.75rem; + box-shadow: 0 4px 24px rgba(0,0,0,0.55), inset 0 1px 0 rgba(52,211,153,0.04); + } + + .nav-btn { + padding: 0.375rem; + border-radius: 0.5rem; + color: #3d6b5a; + transition: color 0.15s, background-color 0.15s; + } + .nav-btn:hover { + color: #a7f3d0; + background: rgba(16,185,129,0.08); + } +} diff --git a/web/src/app.html b/web/src/app.html new file mode 100644 index 0000000..9e12f17 --- /dev/null +++ b/web/src/app.html @@ -0,0 +1,15 @@ + + + + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/web/src/lib/LogModal.svelte b/web/src/lib/LogModal.svelte new file mode 100644 index 0000000..6749ecd --- /dev/null +++ b/web/src/lib/LogModal.svelte @@ -0,0 +1,121 @@ + + + + + + diff --git a/web/src/lib/api.ts b/web/src/lib/api.ts new file mode 100644 index 0000000..ea10c00 --- /dev/null +++ b/web/src/lib/api.ts @@ -0,0 +1,138 @@ +import { getToken, clearToken } from "./auth"; + +export interface ContainerPort { + host_port: number; + container_port: number; + protocol: string; + host_ip: string; +} + +export interface ContainerInfo { + id: string; + name: string; + image: string; + status: string; + state: string; + ports: ContainerPort[]; + created_at: number; + labels: Record; + compose_project: string; +} + +export interface ContainerEntry { + agent_id: string; + hostname: string; + alias: string; + ip_address: string; + container: ContainerInfo; +} + +export interface Agent { + id: string; + hostname: string; + alias: string; + ip_address: string; + arch: string; + os: string; + online: boolean; + last_seen_at: string; +} + +const BASE = "/api/v1"; + +function authHeaders(): Record { + const token = getToken(); + return token ? { Authorization: `Bearer ${token}` } : {}; +} + +async function apiFetch(input: string, init: RequestInit = {}): Promise { + const r = await fetch(input, { + ...init, + headers: { ...authHeaders(), ...(init.headers as Record ?? {}) }, + }); + if (r.status === 401) { + clearToken(); + location.href = "/login"; + throw new Error("Session expirée"); + } + return r; +} + +export async function fetchAgents(): Promise { + const r = await apiFetch(`${BASE}/agents`); + if (!r.ok) throw new Error(`agents: ${r.status}`); + return r.json(); +} + +export async function updateAgent(id: string, alias: string): Promise { + const r = await apiFetch(`${BASE}/agents/${id}`, { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ alias }), + }); + if (!r.ok) throw new Error(`update agent: ${r.status}`); + return r.json(); +} + +export async function fetchContainers(): Promise { + const ac = new AbortController(); + const t = setTimeout(() => ac.abort(), 8000); + try { + const r = await apiFetch(`${BASE}/containers`, { signal: ac.signal }); + if (!r.ok) throw new Error(`containers: ${r.status}`); + return r.json(); + } finally { + clearTimeout(t); + } +} + +export async function containerAction( + agentId: string, + containerId: string, + action: "start" | "stop" | "restart" | "remove" +): Promise<{ command_id: string }> { + const r = await apiFetch( + `${BASE}/agents/${agentId}/containers/${containerId}/action`, + { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ action }), + } + ); + if (!r.ok) throw new Error(`action failed: ${r.status}`); + return r.json(); +} + +export function connectLogs( + agentId: string, + containerId: string, + onLine: (line: { stream: string; line: string }) => void, + tail = 200, + follow = true, +): () => void { + const proto = location.protocol === "https:" ? "wss" : "ws"; + const token = getToken() ?? ""; + const ws = new WebSocket( + `${proto}://${location.host}/api/v1/agents/${agentId}/containers/${containerId}/logs?token=${encodeURIComponent(token)}&tail=${tail}&follow=${follow}` + ); + ws.onmessage = (e) => { + try { onLine(JSON.parse(e.data)); } catch {} + }; + return () => ws.close(); +} + +export function connectEvents( + onEvent: (evt: { type: string; agent_id?: string; payload: unknown }) => void +): () => void { + const proto = location.protocol === "https:" ? "wss" : "ws"; + const token = getToken() ?? ""; + const ws = new WebSocket( + `${proto}://${location.host}/api/v1/events?token=${encodeURIComponent(token)}` + ); + ws.onmessage = (e) => { + try { + onEvent(JSON.parse(e.data)); + } catch {} + }; + return () => ws.close(); +} diff --git a/web/src/lib/auth.ts b/web/src/lib/auth.ts new file mode 100644 index 0000000..04df89d --- /dev/null +++ b/web/src/lib/auth.ts @@ -0,0 +1,35 @@ +const KEY = "containarr_token"; + +export function getToken(): string | null { + return localStorage.getItem(KEY); +} + +export function setToken(token: string) { + localStorage.setItem(KEY, token); +} + +export function clearToken() { + localStorage.removeItem(KEY); +} + +export function isLoggedIn(): boolean { + const t = getToken(); + if (!t) return false; + try { + const payload = JSON.parse(atob(t.split(".")[1])); + return payload.exp * 1000 > Date.now(); + } catch { + return false; + } +} + +export async function login(username: string, password: string): Promise { + const r = await fetch("/api/v1/auth/login", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ username, password }), + }); + if (!r.ok) throw new Error("Identifiants invalides"); + const { token } = await r.json(); + setToken(token); +} diff --git a/web/src/routes/+layout.svelte b/web/src/routes/+layout.svelte new file mode 100644 index 0000000..78f40a1 --- /dev/null +++ b/web/src/routes/+layout.svelte @@ -0,0 +1,19 @@ + + +{@render children()} diff --git a/web/src/routes/+layout.ts b/web/src/routes/+layout.ts new file mode 100644 index 0000000..83addb7 --- /dev/null +++ b/web/src/routes/+layout.ts @@ -0,0 +1,2 @@ +export const ssr = false; +export const prerender = false; diff --git a/web/src/routes/+page.svelte b/web/src/routes/+page.svelte new file mode 100644 index 0000000..ce4db77 --- /dev/null +++ b/web/src/routes/+page.svelte @@ -0,0 +1,351 @@ + + + + Containarr + + +{#if logTarget} + (logTarget = null)} + /> +{/if} + + +{#if toast} +
+ + {toast.msg} +
+{/if} + +
+ + +
+
+ Containarr + Containarr +
+ +
+ {#if entries !== null} + + {entries.length} containers · {Object.keys(byAgent).length} hosts + + {/if} + + + + + + + + + + + +
+
+ +
+ + {#if entries === null} +
+
+ Chargement… +
+ + {:else if loadError} +
+ +

{loadError}

+
+ + {:else if Object.keys(byAgent).length === 0} +
+ + + + Aucun agent connecté +
+ + {:else} + {#each Object.entries(byAgent) as [_agentId, containers]} + {#if containers.length > 0} + {@const first = containers[0]} +
+ + +
+ +

+ {first.alias || first.hostname} +

+ {#if first.alias} + {first.hostname} + {/if} + {#if first.ip_address} + {first.ip_address} + {/if} + + {containers.length} container{containers.length !== 1 ? "s" : ""} + +
+ + + + + +
+ {#each containers as { agent_id, container } (container.id)} +
+
+
+ + {container.name} +
+ {container.state} +
+

{container.image}

+ {#if uniquePorts(container.ports).length > 0} +
+ {#each uniquePorts(container.ports) as port} + + {port.host_port}:{port.container_port} + + {/each} +
+ {/if} +
+ {@render ActionBtn({ label: "Logs", variant: "cyan", + loading: false, + onclick: () => openLogs(agent_id, container.id, container.name) })} + {#if container.state !== "running"} + {@render ActionBtn({ label: "Start", variant: "green", + loading: actionPending === container.id, + onclick: () => doAction(agent_id, container.id, "start") })} + {:else} + {@render ActionBtn({ label: "Stop", variant: "ghost", + loading: actionPending === container.id, + onclick: () => doAction(agent_id, container.id, "stop") })} + {@render ActionBtn({ label: "Restart", variant: "ghost", + loading: actionPending === container.id, + onclick: () => doAction(agent_id, container.id, "restart") })} + {/if} +
+
+ {/each} +
+ +
+ {/if} + {/each} + {/if} +
+
+ +{#snippet ActionBtn({ label, variant, loading, onclick }: { + label: string; + variant: "green" | "ghost" | "cyan"; + loading: boolean; + onclick: () => void; +})} + +{/snippet} diff --git a/web/src/routes/admin/+page.svelte b/web/src/routes/admin/+page.svelte new file mode 100644 index 0000000..5b1374d --- /dev/null +++ b/web/src/routes/admin/+page.svelte @@ -0,0 +1,206 @@ + + + + Admin — Containarr + + +{#if toast} +
+ + {toast.msg} +
+{/if} + +
+ + +
+
+ Containarr + Containarr + / + Administration +
+ + + + + Retour + +
+ +
+ + +
+

Sécurité

+
+

Changer le mot de passe

+
+ {#if pwError} +

{pwError}

+ {/if} + {#each [ + { id: "pw-c", label: "Mot de passe actuel", bind: "pwCurrent", val: pwCurrent, ac: "current-password" }, + { id: "pw-n", label: "Nouveau mot de passe", bind: "pwNew", val: pwNew, ac: "new-password" }, + { id: "pw-cf", label: "Confirmer", bind: "pwConfirm", val: pwConfirm, ac: "new-password" }, + ] as field} +
+ + { + const v = (e.target as HTMLInputElement).value; + if (field.id === "pw-c") pwCurrent = v; + else if (field.id === "pw-n") pwNew = v; + else pwConfirm = v; + }} + class="w-full bg-abyss-700 border border-white/[0.08] rounded-lg px-3 py-2 text-sm + text-slate-100 outline-none focus:border-emerald/60 focus:ring-1 focus:ring-emerald/30 + transition-all" /> +
+ {/each} + +
+
+
+ + +
+

Agents

+ + {#if loadError} +

{loadError}

+ {:else if agents.length === 0} +

Aucun agent enregistré.

+ {:else} +
+ + + + + + + + + + + + + {#each agents as agent (agent.id)} + + + + + + + + + {/each} + +
StatutHostnameIPArch / OSAlias
+
+ + + {agent.online ? "En ligne" : "Hors ligne"} + +
+
{agent.hostname}{agent.ip_address || "—"}{agent.arch || "—"} / {agent.os || "—"} + + + +
+
+ {/if} +
+ +
+
diff --git a/web/src/routes/login/+page.svelte b/web/src/routes/login/+page.svelte new file mode 100644 index 0000000..dee15ea --- /dev/null +++ b/web/src/routes/login/+page.svelte @@ -0,0 +1,94 @@ + + + + Connexion — Containarr + + +
+
+ + +
+ Containarr +
+

Containarr

+

Gestionnaire de containers Docker

+
+
+ + +
+ + {#if error} +
+ + + + {error} +
+ {/if} + +
+ + +
+ +
+ + +
+ + +
+ +
+
diff --git a/web/src/tests/LogModal.test.ts b/web/src/tests/LogModal.test.ts new file mode 100644 index 0000000..e34bc4a --- /dev/null +++ b/web/src/tests/LogModal.test.ts @@ -0,0 +1,147 @@ +/** + * Tests for LogModal.svelte + * + * Strategy: + * - Mount the component with @testing-library/svelte + * - Use a MockWebSocket to simulate incoming log messages + * - Verify DOM output and interaction (close button, Escape key, clear button) + */ +import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; +import { render, screen, fireEvent } from "@testing-library/svelte"; +import LogModal from "../lib/LogModal.svelte"; + +// ── WebSocket mock ──────────────────────────────────────────────────────────── + +class MockWebSocket { + static last: MockWebSocket | null = null; + url: string; + onmessage: ((e: { data: string }) => void) | null = null; + closed = false; + + constructor(url: string) { + this.url = url; + MockWebSocket.last = this; + } + + close() { this.closed = true; } + + /** Helper: push a log line */ + emit(data: unknown) { + this.onmessage?.({ data: JSON.stringify(data) }); + } +} + +// ── Setup / teardown ────────────────────────────────────────────────────────── + +beforeEach(() => { + MockWebSocket.last = null; + localStorage.setItem("containarr_token", "test-token"); + vi.stubGlobal("WebSocket", MockWebSocket); + + Object.defineProperty(globalThis, "location", { + value: { protocol: "http:", host: "localhost" }, + writable: true, + configurable: true, + }); +}); + +afterEach(() => { + vi.restoreAllMocks(); + localStorage.clear(); +}); + +// ── Default props ───────────────────────────────────────────────────────────── + +const defaultProps = { + agentId: "agent1", + containerId: "ctn1", + containerName: "my-container", + onClose: vi.fn(), +}; + +// ── Tests ───────────────────────────────────────────────────────────────────── + +describe("LogModal.svelte", () => { + it("renders the container name in the header", () => { + render(LogModal, { props: defaultProps }); + expect(screen.getByText("my-container")).toBeInTheDocument(); + }); + + it("shows the empty-state placeholder before any log arrives", () => { + render(LogModal, { props: defaultProps }); + expect(screen.getByText(/en attente des logs/i)).toBeInTheDocument(); + }); + + it("opens a WebSocket with the correct URL", () => { + render(LogModal, { props: defaultProps }); + expect(MockWebSocket.last).not.toBeNull(); + expect(MockWebSocket.last!.url).toContain("/api/v1/agents/agent1/containers/ctn1/logs"); + expect(MockWebSocket.last!.url).toContain("token=test-token"); + }); + + it("displays incoming log lines", async () => { + render(LogModal, { props: defaultProps }); + MockWebSocket.last!.emit({ stream: "stdout", line: "Server started" }); + + // Give Svelte a tick to update the DOM + await vi.waitFor(() => { + expect(screen.getByText("Server started")).toBeInTheDocument(); + }); + }); + + it("strips ANSI escape codes from log lines", async () => { + render(LogModal, { props: defaultProps }); + MockWebSocket.last!.emit({ stream: "stdout", line: "\x1b[32mGreen text\x1b[0m" }); + + await vi.waitFor(() => { + expect(screen.getByText("Green text")).toBeInTheDocument(); + }); + }); + + it("clears lines when the clear button is clicked", async () => { + render(LogModal, { props: defaultProps }); + MockWebSocket.last!.emit({ stream: "stdout", line: "some log" }); + + await vi.waitFor(() => screen.getByText("some log")); + + await fireEvent.click(screen.getByTitle("Effacer")); + + await vi.waitFor(() => { + expect(screen.queryByText("some log")).not.toBeInTheDocument(); + expect(screen.getByText(/en attente des logs/i)).toBeInTheDocument(); + }); + }); + + it("calls onClose when the close button is clicked", async () => { + const onClose = vi.fn(); + render(LogModal, { props: { ...defaultProps, onClose } }); + + await fireEvent.click(screen.getByTitle(/fermer/i)); + expect(onClose).toHaveBeenCalledOnce(); + }); + + it("calls onClose when the Escape key is pressed", async () => { + const onClose = vi.fn(); + render(LogModal, { props: { ...defaultProps, onClose } }); + + await fireEvent.keyDown(window, { key: "Escape" }); + expect(onClose).toHaveBeenCalledOnce(); + }); + + it("closes the WebSocket when the component is unmounted", async () => { + const { unmount } = render(LogModal, { props: defaultProps }); + const ws = MockWebSocket.last!; + expect(ws.closed).toBe(false); + unmount(); + expect(ws.closed).toBe(true); + }); + + it("ignores messages with an empty line field", async () => { + render(LogModal, { props: defaultProps }); + MockWebSocket.last!.emit({ stream: "stdout", line: "" }); + + // The placeholder should still be visible (no lines added) + await new Promise((r) => setTimeout(r, 50)); + expect(screen.getByText(/en attente des logs/i)).toBeInTheDocument(); + }); +}); diff --git a/web/src/tests/api.test.ts b/web/src/tests/api.test.ts new file mode 100644 index 0000000..f5d501e --- /dev/null +++ b/web/src/tests/api.test.ts @@ -0,0 +1,296 @@ +import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"; +import { + fetchAgents, + fetchContainers, + containerAction, + updateAgent, + connectLogs, + connectEvents, +} from "../lib/api"; + +// ── Helpers ────────────────────────────────────────────────────────────────── + +function mockFetch(status: number, body: unknown, ok = status >= 200 && status < 300) { + return vi.fn().mockResolvedValue({ + ok, + status, + json: async () => body, + }); +} + +/** Minimal WebSocket mock */ +class MockWebSocket { + static instances: MockWebSocket[] = []; + url: string; + onmessage: ((e: { data: string }) => void) | null = null; + onclose: (() => void) | null = null; + closed = false; + + constructor(url: string) { + this.url = url; + MockWebSocket.instances.push(this); + } + + close() { + this.closed = true; + } + + /** Helper: simulate incoming message */ + emit(data: unknown) { + this.onmessage?.({ data: JSON.stringify(data) }); + } +} + +// ── Setup / teardown ───────────────────────────────────────────────────────── + +beforeEach(() => { + localStorage.clear(); + MockWebSocket.instances = []; + vi.stubGlobal("WebSocket", MockWebSocket); + + // Default location stubs (jsdom provides location but let's make sure) + Object.defineProperty(globalThis, "location", { + value: { + protocol: "http:", + host: "localhost:5173", + href: "", + }, + writable: true, + configurable: true, + }); +}); + +afterEach(() => { + vi.restoreAllMocks(); +}); + +// ── fetchAgents ────────────────────────────────────────────────────────────── + +describe("fetchAgents", () => { + it("returns agents array on success", async () => { + const agents = [{ id: "a1", hostname: "host1", alias: "", ip_address: "1.2.3.4", arch: "amd64", os: "linux", online: true, last_seen_at: "2024-01-01" }]; + vi.stubGlobal("fetch", mockFetch(200, agents)); + + const result = await fetchAgents(); + expect(result).toEqual(agents); + }); + + it("throws on non-ok response", async () => { + vi.stubGlobal("fetch", mockFetch(500, {}, false)); + await expect(fetchAgents()).rejects.toThrow("agents: 500"); + }); + + it("includes Authorization header when token is present", async () => { + localStorage.setItem("containarr_token", "my-token"); + const fetchMock = mockFetch(200, []); + vi.stubGlobal("fetch", fetchMock); + + await fetchAgents(); + + const [, opts] = fetchMock.mock.calls[0]; + expect(opts.headers?.Authorization).toBe("Bearer my-token"); + }); + + it("redirects to /login and clears token on 401", async () => { + localStorage.setItem("containarr_token", "expired"); + vi.stubGlobal("fetch", mockFetch(401, {}, false)); + + await expect(fetchAgents()).rejects.toThrow("Session expirée"); + expect(localStorage.getItem("containarr_token")).toBeNull(); + }); +}); + +// ── updateAgent ────────────────────────────────────────────────────────────── + +describe("updateAgent", () => { + it("sends PATCH with alias and returns updated agent", async () => { + const updated = { id: "a1", hostname: "host1", alias: "new-alias", ip_address: "1.2.3.4", arch: "amd64", os: "linux", online: true, last_seen_at: "2024-01-01" }; + const fetchMock = mockFetch(200, updated); + vi.stubGlobal("fetch", fetchMock); + + const result = await updateAgent("a1", "new-alias"); + + expect(result).toEqual(updated); + const [url, opts] = fetchMock.mock.calls[0]; + expect(url).toBe("/api/v1/agents/a1"); + expect(opts.method).toBe("PATCH"); + expect(JSON.parse(opts.body)).toEqual({ alias: "new-alias" }); + }); + + it("throws on error", async () => { + vi.stubGlobal("fetch", mockFetch(404, {}, false)); + await expect(updateAgent("missing", "alias")).rejects.toThrow("update agent: 404"); + }); +}); + +// ── fetchContainers ────────────────────────────────────────────────────────── + +describe("fetchContainers", () => { + it("returns container entries on success", async () => { + const entries = [ + { + agent_id: "a1", + hostname: "host1", + alias: "", + ip_address: "1.2.3.4", + container: { + id: "c1", + name: "my-app", + image: "nginx:latest", + status: "running", + state: "running", + ports: [], + created_at: 0, + labels: {}, + compose_project: "", + }, + }, + ]; + vi.stubGlobal("fetch", mockFetch(200, entries)); + + const result = await fetchContainers(); + expect(result).toEqual(entries); + }); + + it("throws on non-ok status", async () => { + vi.stubGlobal("fetch", mockFetch(503, {}, false)); + await expect(fetchContainers()).rejects.toThrow("containers: 503"); + }); + + it("passes an AbortSignal to fetch", async () => { + const fetchMock = mockFetch(200, []); + vi.stubGlobal("fetch", fetchMock); + + await fetchContainers(); + + const [, opts] = fetchMock.mock.calls[0]; + expect(opts.signal).toBeInstanceOf(AbortSignal); + }); +}); + +// ── containerAction ────────────────────────────────────────────────────────── + +describe("containerAction", () => { + it.each(["start", "stop", "restart", "remove"] as const)( + "sends %s action and returns command_id", + async (action) => { + const fetchMock = mockFetch(200, { command_id: "cmd-42" }); + vi.stubGlobal("fetch", fetchMock); + + const result = await containerAction("agent1", "container1", action); + expect(result).toEqual({ command_id: "cmd-42" }); + + const [url, opts] = fetchMock.mock.calls[0]; + expect(url).toBe("/api/v1/agents/agent1/containers/container1/action"); + expect(opts.method).toBe("POST"); + expect(JSON.parse(opts.body)).toEqual({ action }); + } + ); + + it("throws on error", async () => { + vi.stubGlobal("fetch", mockFetch(500, {}, false)); + await expect(containerAction("a", "c", "start")).rejects.toThrow("action failed: 500"); + }); +}); + +// ── connectLogs ────────────────────────────────────────────────────────────── + +describe("connectLogs", () => { + it("creates a WebSocket with the correct URL", () => { + localStorage.setItem("containarr_token", "tok123"); + connectLogs("agent1", "container1", () => {}); + + expect(MockWebSocket.instances).toHaveLength(1); + const ws = MockWebSocket.instances[0]; + expect(ws.url).toContain("/api/v1/agents/agent1/containers/container1/logs"); + expect(ws.url).toContain("token=tok123"); + expect(ws.url).toContain("tail=200"); + expect(ws.url).toContain("follow=true"); + }); + + it("calls onLine with parsed message", () => { + const onLine = vi.fn(); + connectLogs("a", "c", onLine); + + const ws = MockWebSocket.instances[0]; + ws.emit({ stream: "stdout", line: "hello world" }); + + expect(onLine).toHaveBeenCalledOnce(); + expect(onLine).toHaveBeenCalledWith({ stream: "stdout", line: "hello world" }); + }); + + it("returns a close function that closes the WebSocket", () => { + const close = connectLogs("a", "c", () => {}); + const ws = MockWebSocket.instances[0]; + expect(ws.closed).toBe(false); + close(); + expect(ws.closed).toBe(true); + }); + + it("silently ignores invalid JSON messages", () => { + const onLine = vi.fn(); + connectLogs("a", "c", onLine); + + const ws = MockWebSocket.instances[0]; + ws.onmessage?.({ data: "not-json{{{{" }); + + expect(onLine).not.toHaveBeenCalled(); + }); + + it("uses wss when protocol is https", () => { + Object.defineProperty(globalThis, "location", { + value: { protocol: "https:", host: "myapp.io" }, + writable: true, + configurable: true, + }); + + connectLogs("a", "c", () => {}); + expect(MockWebSocket.instances[0].url).toMatch(/^wss:\/\//); + }); + + it("respects custom tail and follow parameters", () => { + connectLogs("a", "c", () => {}, 50, false); + const url = MockWebSocket.instances[0].url; + expect(url).toContain("tail=50"); + expect(url).toContain("follow=false"); + }); +}); + +// ── connectEvents ───────────────────────────────────────────────────────────── + +describe("connectEvents", () => { + it("creates a WebSocket pointing to /api/v1/events", () => { + localStorage.setItem("containarr_token", "evtTok"); + connectEvents(() => {}); + + expect(MockWebSocket.instances).toHaveLength(1); + const ws = MockWebSocket.instances[0]; + expect(ws.url).toContain("/api/v1/events"); + expect(ws.url).toContain("token=evtTok"); + }); + + it("calls onEvent with parsed message", () => { + const onEvent = vi.fn(); + connectEvents(onEvent); + + const ws = MockWebSocket.instances[0]; + ws.emit({ type: "containers.updated", payload: { count: 3 } }); + + expect(onEvent).toHaveBeenCalledOnce(); + expect(onEvent).toHaveBeenCalledWith({ type: "containers.updated", payload: { count: 3 } }); + }); + + it("returns a close function that closes the WebSocket", () => { + const close = connectEvents(() => {}); + const ws = MockWebSocket.instances[0]; + close(); + expect(ws.closed).toBe(true); + }); + + it("ignores malformed JSON", () => { + const onEvent = vi.fn(); + connectEvents(onEvent); + MockWebSocket.instances[0].onmessage?.({ data: "{{bad}}" }); + expect(onEvent).not.toHaveBeenCalled(); + }); +}); diff --git a/web/src/tests/auth.test.ts b/web/src/tests/auth.test.ts new file mode 100644 index 0000000..3674f56 --- /dev/null +++ b/web/src/tests/auth.test.ts @@ -0,0 +1,120 @@ +import { describe, it, expect, beforeEach, vi } from "vitest"; +import { getToken, setToken, clearToken, isLoggedIn, login } from "../lib/auth"; + +// localStorage is available via jsdom + +describe("auth.ts", () => { + const KEY = "containarr_token"; + + beforeEach(() => { + localStorage.clear(); + vi.restoreAllMocks(); + }); + + // --- getToken --- + describe("getToken", () => { + it("returns null when no token is stored", () => { + expect(getToken()).toBeNull(); + }); + + it("returns the stored token", () => { + localStorage.setItem(KEY, "my-token"); + expect(getToken()).toBe("my-token"); + }); + }); + + // --- setToken --- + describe("setToken", () => { + it("stores the token in localStorage", () => { + setToken("abc123"); + expect(localStorage.getItem(KEY)).toBe("abc123"); + }); + }); + + // --- clearToken --- + describe("clearToken", () => { + it("removes the token from localStorage", () => { + localStorage.setItem(KEY, "to-remove"); + clearToken(); + expect(localStorage.getItem(KEY)).toBeNull(); + }); + + it("does nothing if no token is stored", () => { + expect(() => clearToken()).not.toThrow(); + }); + }); + + // --- isLoggedIn --- + describe("isLoggedIn", () => { + it("returns false when no token is stored", () => { + expect(isLoggedIn()).toBe(false); + }); + + it("returns false for a malformed token", () => { + localStorage.setItem(KEY, "not.a.jwt"); + expect(isLoggedIn()).toBe(false); + }); + + function makeJWT(payload: Record): string { + const header = btoa(JSON.stringify({ alg: "HS256", typ: "JWT" })); + const body = btoa(JSON.stringify(payload)); + return `${header}.${body}.signature`; + } + + it("returns true for a non-expired token", () => { + const exp = Math.floor(Date.now() / 1000) + 3600; // +1h + const token = makeJWT({ sub: "user", exp }); + localStorage.setItem(KEY, token); + expect(isLoggedIn()).toBe(true); + }); + + it("returns false for an expired token", () => { + const exp = Math.floor(Date.now() / 1000) - 3600; // -1h + const token = makeJWT({ sub: "user", exp }); + localStorage.setItem(KEY, token); + expect(isLoggedIn()).toBe(false); + }); + }); + + // --- login --- + describe("login", () => { + it("stores the token on success", async () => { + const fakeToken = "fresh-jwt-token"; + vi.stubGlobal( + "fetch", + vi.fn().mockResolvedValue({ + ok: true, + json: async () => ({ token: fakeToken }), + }) + ); + + await login("admin", "password"); + expect(localStorage.getItem(KEY)).toBe(fakeToken); + }); + + it("throws on non-ok response", async () => { + vi.stubGlobal( + "fetch", + vi.fn().mockResolvedValue({ ok: false }) + ); + + await expect(login("admin", "wrong")).rejects.toThrow("Identifiants invalides"); + }); + + it("calls the correct endpoint with correct body", async () => { + const fetchMock = vi.fn().mockResolvedValue({ + ok: true, + json: async () => ({ token: "tok" }), + }); + vi.stubGlobal("fetch", fetchMock); + + await login("myuser", "mypass"); + + expect(fetchMock).toHaveBeenCalledOnce(); + const [url, opts] = fetchMock.mock.calls[0]; + expect(url).toBe("/api/v1/auth/login"); + expect(opts.method).toBe("POST"); + expect(JSON.parse(opts.body)).toEqual({ username: "myuser", password: "mypass" }); + }); + }); +}); diff --git a/web/src/tests/setup.ts b/web/src/tests/setup.ts new file mode 100644 index 0000000..d0de870 --- /dev/null +++ b/web/src/tests/setup.ts @@ -0,0 +1 @@ +import "@testing-library/jest-dom"; diff --git a/web/src/tests/stripAnsi.test.ts b/web/src/tests/stripAnsi.test.ts new file mode 100644 index 0000000..695bf20 --- /dev/null +++ b/web/src/tests/stripAnsi.test.ts @@ -0,0 +1,54 @@ +/** + * Tests for the stripAnsi logic used in LogModal.svelte. + * + * The function is defined inline in the component; we replicate it here + * identically so it can be unit-tested without mounting the full component. + */ +import { describe, it, expect } from "vitest"; + +// Replicated directly from LogModal.svelte (src/lib/LogModal.svelte) +function stripAnsi(str: string): string { + // eslint-disable-next-line no-control-regex + return str.replace(/\x1b\[[0-9;]*[mGKHF]/g, ""); +} + +describe("stripAnsi", () => { + it("passes through plain text unchanged", () => { + expect(stripAnsi("hello world")).toBe("hello world"); + }); + + it("strips a simple SGR reset sequence (\\x1b[0m)", () => { + expect(stripAnsi("\x1b[0mhello")).toBe("hello"); + }); + + it("strips bold / colour sequences", () => { + expect(stripAnsi("\x1b[1;32mGreen bold\x1b[0m")).toBe("Green bold"); + }); + + it("strips cursor-move sequences (G, K, H, F)", () => { + expect(stripAnsi("\x1b[2K\x1b[1Gsome line")).toBe("some line"); + }); + + it("strips multiple sequences in a single string", () => { + const raw = "\x1b[31mERROR\x1b[0m: \x1b[1mfoo\x1b[0m"; + expect(stripAnsi(raw)).toBe("ERROR: foo"); + }); + + it("handles multi-param sequences like \\x1b[38;5;200m", () => { + expect(stripAnsi("\x1b[38;5;200mtext\x1b[0m")).toBe("text"); + }); + + it("does not strip incomplete escape sequences (no letter terminator)", () => { + // "\x1b[123" is incomplete — no terminating letter, should stay + const partial = "\x1b[123"; + expect(stripAnsi(partial)).toBe(partial); + }); + + it("returns empty string unchanged", () => { + expect(stripAnsi("")).toBe(""); + }); + + it("preserves newlines", () => { + expect(stripAnsi("\x1b[32mline1\x1b[0m\nline2")).toBe("line1\nline2"); + }); +}); diff --git a/web/static/icon-192.png b/web/static/icon-192.png new file mode 100644 index 0000000000000000000000000000000000000000..3c205f5a86a47f2175d48327203406c32dfc0902 GIT binary patch literal 8219 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

ppf?@xy=LZ`;VaE|## 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 JPDHLkV1iCr-){f_ literal 0 HcmV?d00001 diff --git a/web/svelte.config.js b/web/svelte.config.js new file mode 100644 index 0000000..3c4ccba --- /dev/null +++ b/web/svelte.config.js @@ -0,0 +1,14 @@ +import adapter from "@sveltejs/adapter-static"; +import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + preprocess: vitePreprocess(), + kit: { + adapter: adapter({ + fallback: "index.html", // SPA mode for PWA + }), + }, +}; + +export default config; diff --git a/web/tailwind.config.js b/web/tailwind.config.js new file mode 100644 index 0000000..7b6efce --- /dev/null +++ b/web/tailwind.config.js @@ -0,0 +1,46 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: ["./src/**/*.{html,js,svelte,ts}"], + theme: { + extend: { + colors: { + abyss: { + 950: "#03070f", + 900: "#060c18", + 800: "#0a1220", + 700: "#0f1b2e", + 600: "#162238", + 500: "#1e2f48", + }, + emerald: { + DEFAULT: "#10b981", + bright: "#34d399", + dim: "#0a3d26", + }, + signal: { + green: "#34d399", + red: "#f87171", + yellow: "#fbbf24", + cyan: "#2dd4bf", + }, + }, + fontFamily: { + mono: ["JetBrains Mono", "Fira Code", "ui-monospace", "monospace"], + sans: ["Inter", "system-ui", "-apple-system", "sans-serif"], + }, + boxShadow: { + "glow-green": "0 0 10px rgba(52,211,153,0.45)", + "glow-emerald": "0 0 14px rgba(16,185,129,0.4)", + card: "0 4px 24px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.04)", + }, + backgroundImage: { + "grid-faint": + "linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px)", + }, + backgroundSize: { + grid: "32px 32px", + }, + }, + }, + plugins: [], +}; diff --git a/web/tsconfig.json b/web/tsconfig.json new file mode 100644 index 0000000..5c56cee --- /dev/null +++ b/web/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true + } +} diff --git a/web/vite.config.ts b/web/vite.config.ts new file mode 100644 index 0000000..6788117 --- /dev/null +++ b/web/vite.config.ts @@ -0,0 +1,47 @@ +import { sveltekit } from "@sveltejs/kit/vite"; +import { SvelteKitPWA } from "@vite-pwa/sveltekit"; +import { svelteTesting } from "@testing-library/svelte/vite"; +import { defineConfig } from "vite"; + +export default defineConfig({ + test: { + environment: "jsdom", + globals: true, + setupFiles: ["./src/tests/setup.ts"], + include: ["src/**/*.{test,spec}.{js,ts}"], + }, + plugins: [ + sveltekit(), + svelteTesting(), + SvelteKitPWA({ + registerType: "autoUpdate", + manifest: { + name: "Containarr", + short_name: "Containarr", + description: "Multi-VM Docker container manager", + theme_color: "#0f172a", + background_color: "#0f172a", + display: "standalone", + start_url: "/", + icons: [ + { src: "/icon-192.png", sizes: "192x192", type: "image/png" }, + { src: "/icon-512.png", sizes: "512x512", type: "image/png" }, + ], + }, + workbox: { + globPatterns: ["**/*.{js,css,html,svg,png,ico,woff2}"], + }, + devOptions: { + enabled: false, + }, + }), + ], + server: { + proxy: { + "/api": { + target: "http://127.0.0.1:8080", + ws: true, + }, + }, + }, +});