diff --git a/.codescene/code-health-rules.json b/.codescene/code-health-rules.json index 8638125f5..67281a1e5 100644 --- a/.codescene/code-health-rules.json +++ b/.codescene/code-health-rules.json @@ -1,6 +1,56 @@ { "usage": "Repository overrides for CodeScene code health scoring. Test code is relaxed for inherently similar test structure, and one repo-wide false positive (String Heavy Function Arguments) is disabled with its rationale below. Production code keeps every other rule at default weight.", "rule_sets": [ + { + "matching_content_path": "src/channels/web/static/solid/**", + "matching_content_path_doc": "Generated, minified SolidJS build output embedded into the binary (refreshed by `make frontend-build`). Not hand-maintained source; code-health findings here are false positives against the bundler's output. The corresponding sources under web-src/ are analysed at full weight.", + "rules": [ + { + "name": "Complex Method", + "weight": 0.0 + }, + { + "name": "Complex Conditional", + "weight": 0.0 + }, + { + "name": "Bumpy Road Ahead", + "weight": 0.0 + }, + { + "name": "Overall Code Complexity", + "weight": 0.0 + }, + { + "name": "Deep, Nested Complexity", + "weight": 0.0 + }, + { + "name": "Excess Number of Function Arguments", + "weight": 0.0 + }, + { + "name": "Lines of Code in a Single File", + "weight": 0.0 + }, + { + "name": "Code Duplication", + "weight": 0.0 + }, + { + "name": "Large Method", + "weight": 0.0 + }, + { + "name": "Primitive Obsession", + "weight": 0.0 + }, + { + "name": "String Heavy Function Arguments", + "weight": 0.0 + } + ] + }, { "matching_content_path": "tests/**", "matching_content_path_doc": "Integration test crates: parameterized cases, fixture staging, and assertion tables are deliberately repetitive.", @@ -161,6 +211,54 @@ } ] }, + { + "matching_content_path": "web-src/axinite/tests/**", + "matching_content_path_doc": "Browser workspace test suites: the same test-shape leniency as tests/** (parameterized cases, fixture staging, and assertion tables are deliberately repetitive).", + "rules": [ + { + "name": "Code Duplication", + "weight": 0.0 + }, + { + "name": "Large Assertion Blocks", + "weight": 0.0 + }, + { + "name": "Duplicated Assertion Blocks", + "weight": 0.0 + }, + { + "name": "Large Method", + "weight": 0.0 + }, + { + "name": "String Heavy Function Arguments", + "weight": 0.0 + } + ] + }, + { + "matching_content_path": "web-src/mock-backend/**", + "matching_content_path_doc": "Daemon-free stub harness, not production code: it mirrors the gateway wire contract with deterministic in-memory fixtures, so fixture volume (file length) and contract-shaped response builders (duplication) are down-weighted, and stringly wire identifiers (primitive obsession, string-heavy arguments) are disabled outright \u2014 the public mock API deliberately takes the same string ids the wire carries. Structural rules (Bumpy Road, Complex Method) stay at default weight and were refactored, not suppressed. Note CodeScene applies one rule set per file, so this set restates the repo-wide String Heavy disable.", + "rules": [ + { + "name": "Code Duplication", + "weight": 0.3 + }, + { + "name": "Lines of Code in a Single File", + "weight": 0.3 + }, + { + "name": "Primitive Obsession", + "weight": 0.0 + }, + { + "name": "String Heavy Function Arguments", + "weight": 0.0 + } + ] + }, { "matching_content_path": "**", "matching_content_path_doc": "String Heavy Function Arguments is disabled repo-wide: after the refactor rounds extracted every genuine parameter clump into structs and newtypes, the remaining flagged parameters are paths, URLs, user-supplied text, secret names, shell command tokens, and SQL fragments, which are idiomatically &str in Rust. The smell fires as a systematic false positive here. This general rule set is listed last so the more specific test-scoped sets above take precedence.", diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml new file mode 100644 index 000000000..e1c4cc957 --- /dev/null +++ b/.github/workflows/frontend.yml @@ -0,0 +1,54 @@ +--- +name: Frontend +"on": + pull_request: + branches: + - main + paths: + - "web-src/**" + - "src/channels/web/static/solid/**" + - "Makefile" + - ".github/workflows/frontend.yml" + push: + branches: + - main + paths: + - "web-src/**" + - "src/channels/web/static/solid/**" + - "Makefile" + - ".github/workflows/frontend.yml" + +jobs: + frontend: + name: Frontend verification + runs-on: ubicloud-standard-8 + env: + # playwright.config.ts pins the hermetic browsers path at test time; + # the install step must target the same location. + PLAYWRIGHT_BROWSERS_PATH: "0" + steps: + - name: Checkout repository + uses: actions/checkout@v6 + - name: Install Bun + uses: oven-sh/setup-bun@v2 + with: + bun-version: latest + - name: Install uv + # `semantic` and `verify:full` fetch semgrep and moz-fluent-linter + # through uvx. + uses: astral-sh/setup-uv@v5 + - name: Install dependencies + run: make frontend-install + - name: Install Playwright Chromium + working-directory: web-src + run: bunx playwright install --with-deps chromium + - name: Full verification chain + # Tailwind compile check, Biome lint, TypeScript, vitest unit and + # accessibility suites, Fluent variable/coverage checks, the + # semantic-CSS rules, the workspace Playwright spec, and + # moz-fluent-lint. + run: make frontend-full + - name: Embedded asset staleness gate + # Rebuild the SPA and fail when src/channels/web/static/solid does + # not match the web-src sources. + run: make frontend-verify diff --git a/Makefile b/Makefile index be5201985..f41bb8f9e 100644 --- a/Makefile +++ b/Makefile @@ -66,10 +66,47 @@ AUDIT_FLAGS ?= \ --ignore RUSTSEC-2024-0370 \ --ignore RUSTSEC-2025-0134 -.PHONY: all install install-with-overrides sync-local-wasm-overrides build-github-tool-wasm fmt check-fmt typecheck lint lint-clippy lint-whitaker markdownlint spelling spelling-phrase-check spelling-config spelling-config-write spelling-helper-test nixie audit rust-audit test test-cargo test-matrix test-matrix-cargo test-workflow-contracts clean +.PHONY: all install install-with-overrides sync-local-wasm-overrides build-github-tool-wasm fmt check-fmt typecheck lint lint-clippy lint-whitaker markdownlint spelling spelling-phrase-check spelling-config spelling-config-write spelling-helper-test nixie audit rust-audit test test-cargo test-matrix test-matrix-cargo test-workflow-contracts clean frontend-install frontend-build frontend-verify frontend-check frontend-test frontend-full frontend-stub all: check-fmt lint test spelling +BUN ?= $(shell command -v bun 2>/dev/null || printf '%s' "$$HOME/.bun/bin/bun") +FRONTEND_DIR := web-src +FRONTEND_DIST := $(FRONTEND_DIR)/dist +FRONTEND_EMBED_DIR := src/channels/web/static/solid + +frontend-install: + cd $(FRONTEND_DIR) && $(BUN) install --frozen-lockfile + +# Build the SolidJS app and refresh the embedded copy served by the gateway. +frontend-build: frontend-install + cd $(FRONTEND_DIR) && $(BUN) run build + rsync -a --delete $(FRONTEND_DIST)/ $(FRONTEND_EMBED_DIR)/ + +# Fail when the committed embedded assets are stale relative to web-src. +frontend-verify: frontend-build + git diff --exit-code -- $(FRONTEND_EMBED_DIR) || { echo "error: $(FRONTEND_EMBED_DIR) is stale; commit the output of 'make frontend-build'" >&2; exit 1; } + +# Static checks and unit suites for the browser workspace. `semantic` +# covers the classlist, semgrep, and stylelint rules and fetches semgrep +# through uvx on first use. +frontend-check: frontend-install + cd $(FRONTEND_DIR) && $(BUN) run check:fmt && $(BUN) run lint && $(BUN) run check:types && $(BUN) run semantic + +frontend-test: frontend-check + cd $(FRONTEND_DIR) && $(BUN) run test && $(BUN) run test:a11y && $(BUN) run lint:ftl-vars + +# The mockup's full verification chain: Tailwind compile check, lint, +# typecheck, unit + a11y + Fluent + semantic suites, the workspace +# Playwright spec (browsers must be installed), and moz-fluent-lint. +frontend-full: frontend-install + cd $(FRONTEND_DIR) && $(BUN) run verify:full + +# Daemon-free stub runtime: Bun mock API (HTTP + SSE + /api/features) plus a +# preview server for the built SPA on http://127.0.0.1:2020. +frontend-stub: + cd $(FRONTEND_DIR) && $(BUN) run dev + install: ./scripts/build-wasm-extensions.sh $(CARGO) install --path . diff --git a/README.md b/README.md index 52337618a..a55c1c93b 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,8 @@ ______________________________________________________________________ and tools. - [Building channels](docs/BUILDING_CHANNELS.md) — rebuilding bundled channel artefacts. +- [SolidJS front-end](docs/solidjs-frontend.md) — the browser UI, its + daemon-free stub runtime (`make frontend-stub`), and feature flags. - [Contributing](CONTRIBUTING.md) — development workflow and review tracks. - [Changelog](CHANGELOG.md) — release history. diff --git a/docs/execplans/adopt-solidjs-ui-followups.md b/docs/execplans/adopt-solidjs-ui-followups.md new file mode 100644 index 000000000..471b8cb18 --- /dev/null +++ b/docs/execplans/adopt-solidjs-ui-followups.md @@ -0,0 +1,372 @@ +# SolidJS adoption follow-ups: flag persistence, UI parity, e2e migration + +This ExecPlan (execution plan) is a living document. The sections +`Constraints`, `Tolerances`, `Risks`, `Progress`, `Surprises & Discoveries`, +`Decision Log`, and `Outcomes & Retrospective` must be kept up to date as work +proceeds. It continues `docs/execplans/adopt-solidjs-ui.md` (COMPLETE), which +established the SolidJS SPA as the default gateway UI. + +Status: COMPLETE + +## Purpose / big picture + +Three follow-up streams complete the SolidJS adoption: + +1. **RFC 0009 persistence**: deployment-scoped feature-flag overrides stored + in the database and served through `GET /api/features`, beneath the + existing `FEATURE_FLAG_` environment-variable resolution, updated at + runtime through the settings API without a restart. +2. **UI parity** (docs/solidjs-pwa-gap-analysis.md): logs as a top-level + route; gateway restart affordance; TEE attestation surface; pairing + approval for WASM channels; chat media (image attach, generated images), + auth cards, and job-start cards; jobs detail fidelity (tabs, transitions, + live activity, done signal, file tree). +3. **e2e migration**: the Python Playwright suite in `tests/e2e/` drives the + SolidJS DOM against the real daemon, and the `AXINITE_WEB_UI=legacy` pin + is removed from its conftest. + +Observable outcomes: an operator can toggle a flag for a deployment via +`PUT /api/settings/feature_flag:` and see `GET /api/features` change +immediately; every legacy browser affordance listed above exists in the +SolidJS UI and is exercisable against the stub; `pytest tests/e2e/` passes +against the SolidJS UI. + +## Constraints + +- The daemon's wire contracts are authoritative; the SPA and mock adapt to + them. New daemon surface is limited to the RFC 0009 flag layer. +- Both database backends (postgres, libsql) must gain the persistence layer; + libsql migrations must not rebuild the `settings` table. +- The mock backend stays contract-focused: fixtures for the new routes and + events, deterministic, no durable state. +- `make all`, frontend gates, and `make frontend-verify` stay green at every + commit; commit after each milestone. +- Do not delete regression tests; rewrite them. The legacy shell and its + assets stay in place (their removal is RFC 0018 Stage 5, out of scope + here). +- en-GB-oxendict prose; UI strings localized in all ten locales. + +## Tolerances (exception triggers) + +- If the TEE external-host contract (`api.`) proves unverifiable + beyond what `app.js` encodes, implement to the `app.js`-observed contract + and stop there; do not invent server behaviour. +- If a Python e2e scenario cannot be expressed without a UI affordance this + plan does not build (for example the legacy "Always" approval action if + the daemon rejects it), record the decision and adapt the scenario rather + than growing scope. +- Dependencies: no new Rust crates; no new JS runtime dependencies. Escalate + otherwise. +- Iterations: a gate failing after 4 fix attempts stops the milestone. + +## Risks + +- Risk: e2e scenarios depend on injectable JS hooks (`showApproval`, + `addMessage`, `connectSSE`, …) that a compiled SPA does not expose. + Severity: high. Likelihood: certain. + Mitigation: expose a deliberate, minimal test-hook object + (`window.__axinite`) from the SPA — chat-stream close/reconnect and an + `emitChatEvent` injector — always mounted (tiny, no security surface + beyond what the browser console already allows) and documented. +- Risk: restart completion detection relies on SSE reconnection heuristics. + Mitigation: mirror the legacy heuristic (tool_completed name `restart` or + response containing "restart initiated", cleared on stream re-open); unit + test the state machine with a fake EventSource. +- Risk: deployment-scoping is new infrastructure; requiring an + `X-Deployment-Id` header on `GET /api/features` (as RFC 0009 writes it) + would break the existing SPA fetch. + Mitigation: resolve to a `"default"` deployment when the header is absent + on reads; writes require the header per RFC. Recorded in the Decision Log. +- Risk: the jobs detail rework is the largest component change and could + destabilize existing behaviour tests. + Mitigation: keep the existing list/summary intact; build detail tabs as + new components with their own tests before swapping in. + +## Progress + +- [x] (2026-07-19 13:10Z) Recon: e2e scenario inventory, settings/migration + conventions, restart/TEE/media/pairing contracts, mock gaps. +- [x] (2026-07-19 13:20Z) ExecPlan drafted. +- [x] (2026-07-19 14:45Z) F1: RFC 0009 flag persistence (Rust): + `feature_flag_overrides` table + (postgres V18 + libsql incremental), `SettingsStore` deployment-flag + methods, `FeatureFlagRegistry` in `GatewayState`, settings-handler + interception, `/api/features` layering, tests. +- [x] (2026-07-19 14:05Z) F2: logs top-level route (SPA): `/logs` route + + `route_logs` flag + (registry, Rust defaults, mock), filters (level, target, text), + pause/resume, clear, auto-scroll; gateway serves `/logs` shell; web-src + e2e updated. +- [x] (2026-07-19 14:30Z) F3: stub surface extensions (mock backend): + pairing routes and + `pairing` activation fixture; `/api/chat/auth-token` + `auth-cancel`; + `job_started` + `image_generated` emissions; `/restart` command fixture; + images accepted on send; contract tests. +- [x] (2026-07-19 15:10Z) F4: chat media + auth cards + job cards (SPA): + image staging + (attach/paste, caps, previews), `images[]` on send, generated-image + rendering, `auth_required` dispatch (OAuth card vs configure modal), + `auth_completed` dismissal + toast, `job_started` card; `ChatSseEvent` + union extended to the full daemon event set; tests. +- [x] (2026-07-19 15:40Z) F5: restart + TEE + pairing surfaces (SPA): + restart button/modal/ + loader driven by `restart_enabled` and the `/restart` chat command; TEE + shield + popover behind `surface_tee_attestation` (external-host client + per the legacy contract); pairing rows + approve + stepper states on the + extensions route with 10 s polling; tests. +- [x] (2026-07-19 16:10Z) F6: jobs detail fidelity (SPA): + Overview/Activity/Files tabs, + transitions timeline, `browse_url`, mode/kind, restart/prompt gating, + persisted+live activity merge, done signal, recursive file tree; tests. +- [x] (2026-07-19 17:10Z) F7: Python e2e migration: `?token=` boot + + `data-testid` contract in + the SPA (`auth-screen`, `sse-status`, message roles), `window.__axinite` + hooks, rewrite `helpers.py` SEL + all seven scenarios to the SolidJS DOM, + drop `AXINITE_WEB_UI=legacy` from conftest, run the suite against the + real daemon. +- [x] (2026-07-19 18:20Z) F8: validation closure. Browser validation via + Playwright MCP against the stub: logs route controls and stream, + generated-image and job-start cards, the three-way approval card, jobs + detail tabs with the expandable file tree, the awaiting-pairing stepper + and pending pairing request, zero console errors, and no horizontal + overflow at 375 px. css-view on logs/jobs/extensions/chat (88 to 376 + nodes per route): no element beyond the viewport. Full gates green via + scrutineer: check-fmt, lint (clippy plus whitaker), nextest (4267 + passed), markdownlint/spelling (after renaming `_fulfil` and + `unparsable`), nixie, frontend-test (126 tests), frontend-verify, the + legacy Node test, and the workspace Playwright spec; the migrated + Python suite passes 35 with one skip and the extensions scenario + re-passed after the rename. CodeRabbit reviewed the 189-file diff + against main: zero findings, no rate limiting. + +## Surprises & discoveries + +- Observation: gateway restart is not an HTTP endpoint — the legacy UI + sends the `/restart` slash command through `POST /api/chat/send` and + treats SSE reconnection as completion; `restart_enabled` comes from + `AXINITE_IN_DOCKER`. + Evidence: `app.js:145-248`, `src/tools/builtin/restart.rs`. + Impact: the SolidJS restart affordance replicates the command flow; no + daemon change needed. +- Observation: TEE attestation is served by a separate host + (`https://api./instances/{name}/attestation` and + `/attestation/report`), derived from the browser hostname and inert on + localhost. + Evidence: `app.js:3583-3687`. + Impact: the SPA client mirrors that contract; the stub does not model it + (unit tests mock `fetch`). +- Observation: the mock backend lacks pairing, chat auth-token/cancel, + `job_started`, `image_generated`, image acceptance, and any `/restart` + behaviour — all needed before the SPA parity work can be exercised. + Evidence: recon of `web-src/mock-backend/src/server.ts`, `state.ts`. + Impact: F3 lands before F4–F6. +- Observation: `tests/e2e/test_extensions.py` mocks every API via + `page.route()` interception and never touches the real daemon; the skills + scenario deliberately hits the live ClawHub registry with self-skips. + Impact: extension scenarios can be migrated purely against the SolidJS + DOM; skills keeps its skip guards. + +## Decision log + +- Decision: store deployment-scoped flags in a new + `feature_flag_overrides (deployment_id, flag_name, enabled, updated_at)` + table (PK `(deployment_id, flag_name)`) rather than adding a nullable + `deployment_id` to `settings`. + Rationale: libsql cannot ALTER the composite-keyed `settings` table + without a rebuild; a dedicated table keeps the `(user_id, key)` settings + contract untouched, satisfies RFC 0009's intent (deployment-scoped rows + not keyed by user), and is a plain `CREATE TABLE` on both backends. The + settings API surface (`feature_flag:` key prefix, `X-Deployment-Id` + header) is unchanged from the RFC. + Date/Author: 2026-07-19, Claude. +- Decision: `GET /api/features` resolves the deployment from an optional + `X-Deployment-Id` header, defaulting to `"default"`; writes via + `PUT /api/settings/feature_flag:` require the header (400 without). + Rationale: the RFC requires the header on both; requiring it on reads + would break the existing SPA boot fetch, and this single-instance product + has no deployment identity source yet. Reads defaulting keeps the + contract additive; the strict write path preserves the RFC's persistence + semantics. + Date/Author: 2026-07-19, Claude. +- Decision: precedence is environment variable > deployment override > + compiled default, per RFC 0009 §Precedence (subsystem-availability + defaults are not implemented — no current flag needs one). + Date/Author: 2026-07-19, Claude. +- Decision: expose a minimal `window.__axinite` test-hook object from the + SPA (chat stream close/reconnect, `emitChatEvent`, ready marker) instead + of recreating the legacy globals the Python e2e suite pokes. + Rationale: the compiled SPA has no reachable globals; a deliberate, + documented hook surface keeps scenarios deterministic without shipping + the legacy's implicit global soup. It adds no capability an open console + does not already have. + Date/Author: 2026-07-19, Claude. +- Decision: logs become a `/logs` route gated by a new `route_logs` flag; + the logs dialog is retired and the topbar button becomes a nav entry. + The `panel_logs` flag continues to gate the log stream surface itself so + existing deployments' flag semantics survive. + Date/Author: 2026-07-19, Claude. + +## Context and orientation + +Established by the prior plan: `web-src/` SolidJS workspace; embedded assets +under `src/channels/web/static/solid/`; `handlers/ui_assets.rs` (variant +serving), `handlers/features.rs` (env-var flags); Bun mock backend with +contract tests; Make targets `frontend-*`. Daemon contracts for the parity +surfaces (verified by recon, with exact shapes): + +- Restart: `restart_enabled` in `GatewayStatusResponse`; `/restart` chat + command; completion via `tool_completed {name:"restart", success}` or a + `response` containing "restart initiated"; loader cleared on SSE re-open. +- TEE: external `GET {api-base}/instances/{name}/attestation` + (`image_digest`), `GET {api-base}/attestation/report` + (`tls_certificate_fingerprint`, `report_data`, `vm_config`), copy-report. +- Media: `SendMessageRequest.images: [{media_type, data(base64)}]`, 5 MB + and 5-image caps; SSE `image_generated {data_url, path?, thread_id?}`. +- Auth cards: SSE `auth_required {extension_name, instructions?, auth_url?, + setup_url?}` (auth_url → token/OAuth card, else configure modal); + `auth_completed {extension_name, success, message}`; POST + `/api/chat/auth-token {extension_name, token}` and `/api/chat/auth-cancel + {extension_name}`. +- Jobs: SSE `job_started {job_id, title, browse_url}`; + `JobDetailResponse` carries `project_dir, browse_url, job_mode, + transitions, can_restart, can_prompt, job_kind`. +- Pairing: `GET /api/pairing/{channel}` → + `{channel, requests:[{code, sender_id, meta?, created_at}]}`; + `POST /api/pairing/{channel}/approve {code}` → `ActionResponse`; 429 on + rate-limited approvals; WASM channel `activation_status` in + {installed, configured, pairing, active, failed} drives the stepper. +- Settings: `(user_id, key)`-keyed `settings` table (postgres `V8`, + libsql base schema); `SettingsStore`/`NativeSettingsStore` traits in + `src/db/traits/settings.rs`; the 5-step recipe in `src/db/CLAUDE.md` + (trait → forwarders → postgres → libsql → migration); postgres + migrations are refinery `V__*.sql`, libsql uses + `INCREMENTAL_MIGRATIONS` tuples; `NullDatabase` needs stub impls; + `X-Confirm-Action` in `handlers/skills.rs` is the header-validation + precedent. + +Python e2e (tests/e2e/): boots the daemon with `GATEWAY_AUTH_TOKEN`, boots +a mock OpenAI-compatible LLM with canned regex responses (including a +deliberate XSS payload for "html test"), navigates to `/?token=…`, waits +for `#auth-screen` to hide, and asserts against ~80 legacy selectors and +several injectable globals. `AXINITE_WEB_UI=legacy` is pinned in +`conftest.py:105`. + +## Plan of work + +F1 (Rust, independent): migration `V18__feature_flag_overrides.sql` and a +libsql incremental creating the table; `SettingsStore` + +`NativeSettingsStore` gain `list_deployment_flags(deployment)`, +`set_deployment_flag(deployment, name, enabled)`; forwarders, postgres +(via `history/store/settings.rs`), libsql, `NullDatabase` impls; +`FeatureFlagRegistry` (deployment → name → bool) in `GatewayState` +(struct + `GatewayChannel::new()` + `rebuild_state()`), hydrated from the +store at startup; `settings_set_handler` intercepts `feature_flag:` keys +(validate name `[a-z0-9_]+`, require `X-Deployment-Id`, coerce value to +bool, persist, update registry); `features_handler` resolves env > +registry(deployment) > default. Red tests first: registry precedence, +handler 400 without header, immediate visibility of a write, libsql +round-trip via `LibSqlBackend::new_memory()`. + +F2 (SPA): `route_logs` flag added to `registry.ts`, Rust `FLAG_DEFAULTS`, +mock defaults; `/logs` route in the router + `SOLID_APP_ROUTES`; new +`logs-preview.tsx` route component (stream via existing `connectLogEvents`, +level filter for display, target substring filter, pause/resume, clear, +auto-scroll toggle, level set via existing `/api/logs/level`); retire +`logs-dialog.tsx`; shell nav gains Logs; ten-locale strings; behaviour +tests; update `app-shell.pw.ts`. + +F3 (mock): pairing GET/approve routes + a `pairing`-status WASM channel +fixture; `/api/chat/auth-token` + `/api/chat/auth-cancel`; `sendMessage` +recognizes `/restart` (emits `tool_started`/`tool_completed` named +`restart` and a "Restart initiated" response), a prompt containing +"image" (emits `image_generated` with an inline data URL), and "job" +(emits `job_started`); accepts and echoes `images[]` count in the reply; +`auth_required` fixture variant with `auth_url`; contract tests for each. + +F4 (SPA chat): extend `ChatSseEvent` to the daemon's full tagged set; +image staging + previews + caps + paste; send includes `images`; +generated-image cards; auth card + configure-modal dispatch and +`auth_completed` handling with toasts; `job_started` card linking to the +job; behaviour tests for each path (fake stream via the test hook). + +F5 (SPA shell/extensions): restart button + confirm modal + loader with +the legacy completion heuristics; TEE shield/popover client (hostname +derivation, localhost-inert, report cache, copy) behind +`surface_tee_attestation`; pairing rows + approve + 10 s poll + stepper +(`installed/configured/pairing/active/failed`) on extensions; tests. + +F6 (SPA jobs): tabbed detail; overview (transitions, browse_url, mode, +kind, metadata, gated restart/cancel/prompt); activity merging persisted +events with live `job_*` SSE bucketed by job id; done-signal checkbox on +prompt; recursive file tree; tests. + +F7 (e2e): SPA testability contract — `?token=` boot (AuthGate reads and +stores the query token, strips it from the URL), `data-testid`/ids: +`auth-screen`, `sse-status` (text "Connected"/"Disconnected"), message +roles, approval/auth-card/toast/testids as needed; `window.__axinite` +hooks; then rewrite `tests/e2e/helpers.py` SEL and all seven scenarios; +remove the legacy pin; run the suite (build with libsql features) and fix +fallout. Scenarios that asserted legacy-only mechanics are rewritten to +the SolidJS equivalents (documented per scenario in the commit). + +F8: validation closure as in the Progress list. + +Sequencing: F1 ∥ (F2 → F3 → F4/F5/F6) → F7 → F8. F4–F6 are delegated as +bounded implementation tasks with tests where practical. + +## Concrete steps + +Per milestone: red tests → implement → `make frontend-test` (web-src) or +targeted `cargo nextest` (Rust) → `make frontend-build` when the SPA +changed → commit. Final: `make all`, `make frontend-verify`, +`pytest tests/e2e/ -v` (with `cargo build --no-default-features --features +libsql` first), Playwright MCP + css-view spot checks, CodeRabbit CLI. + +## Validation and acceptance + +- F1: `PUT /api/settings/feature_flag:panel_logs` with + `X-Deployment-Id: production` and value `"false"` makes + `GET /api/features` (same header) return `panel_logs: false` without + restart; without the header the PUT returns 400; env var still wins. +- F2–F6: each surface has behaviour tests, and the stub exercises it + (`make frontend-stub` + documented interaction). +- F7: `pytest tests/e2e/ -v` passes against the SolidJS UI with no + `AXINITE_WEB_UI` pin (skills scenario may self-skip offline). +- Gates: `make all`, frontend gates, `frontend-verify`, CodeRabbit clean + or findings addressed. + +## Idempotence and recovery + +Milestone commits allow `git revert`. Migrations are additive +(`CREATE TABLE IF NOT EXISTS`). The mock stays stateless per process. + +## Outcomes & retrospective + +All three streams delivered. Operators can persist deployment-scoped flag +overrides through the settings API and see them at `GET /api/features` +without a restart, layered beneath environment variables exactly as RFC +0009 orders precedence. The SolidJS UI now carries the legacy shell's +operator surfaces: logs as a route, restart with honest completion +detection, the TEE shield, pairing approval with the activation stepper, +chat media and auth/job cards, and a jobs detail view with live activity. +The Python e2e suite drives the SolidJS DOM against the real daemon with +the legacy pin removed. + +Notable catches along the way: the e2e mock LLM had been silently 404-ing +every completion call (the daemon posts to `{base}/chat/completions`, the +mock only served `/v1/...`) — the legacy UI never surfaced the hang; and +the restart controller was hardened beyond the legacy heuristic to require +an observed down/up cycle before declaring success. + +Lessons: agent parallelism inside one worktree needs strict file +ownership — a concurrent lint run mid-implementation produced a confusing +half-state twice; the whitaker module-size cap and the en-GB spelling gate +are the two gates external contributions most reliably trip, so budget a +cleanup pass after any large import; and test suites that poke framework +internals (the legacy globals) migrate far more cleanly once the app +exposes a small, documented hook surface instead. + +Remaining follow-up (unchanged in scope): RFC 0018 Stage 5 — removing the +legacy shell, its assets, and `tests/web_static_app.test.mjs` once the +rollback window closes — plus the RFC 0009 open questions (subsystem +defaults, flag-change SSE events). diff --git a/docs/execplans/adopt-solidjs-ui.md b/docs/execplans/adopt-solidjs-ui.md new file mode 100644 index 000000000..2fa5b244e --- /dev/null +++ b/docs/execplans/adopt-solidjs-ui.md @@ -0,0 +1,405 @@ +# Adopt the SolidJS front-end as the default Axinite browser UI + +This ExecPlan (execution plan) is a living document. The sections +`Constraints`, `Tolerances`, `Risks`, `Progress`, `Surprises & Discoveries`, +`Decision Log`, and `Outcomes & Retrospective` must be kept up to date as work +proceeds. + +Status: COMPLETE + +## Purpose / big picture + +Axinite currently serves a handwritten browser UI (one large `app.js`, plus +`index.html` and `style.css`) compiled into the Rust binary from +`src/channels/web/static/`. The sibling repository `axinite-mockup` proves out +a SolidJS single-page application (SPA) with typed API modules, TanStack +Router/Query, feature flags, localization, and a Bun mock backend that mirrors +the browser contract over JSON and Server-Sent Events (SSE). + +After this change: + +- The SolidJS SPA is the default browser UI served by the Axinite gateway. A + developer who builds and runs the daemon and opens the gateway URL sees the + SolidJS app, not the legacy shell. +- A developer can run the SolidJS UI **without the daemon** via one command + (`make frontend-stub`, wrapping the Bun mock backend + preview server), with + deterministic HTTP fixtures, deterministic SSE events, and runtime feature + flags served over `GET /api/features`. +- The legacy shell remains embedded solely as an operator rollback path, + selected by an explicit gateway-side switch, and is documented as + transitional. + +This implements Stages 1–3 (and part of 5) of RFC 0018 +(`docs/rfcs/0018-solidjs-front-end-adoption.md`), using the mock backend as the +daemon-free stub runtime, and a minimal environment-variable subset of RFC 0009 +(`docs/rfcs/0009-feature-flags-frontend.md`) for `GET /api/features`. + +## Constraints + +- The Rust gateway remains the authoritative production server; the browser + stays same-origin on `/api/*` (RFC 0018 §1). No separate front-end service. +- Production packaging must keep working: one binary, assets embedded at + compile time (preserves Dockerfile/wix behaviour, which copy no extra asset + directories). `cargo build` must succeed **without** Bun/Node installed. +- The Bun mock backend must not become a second daemon: contract-focused + fixtures only; no auth logic beyond ignoring tokens, no durable state. +- Do not adopt extra state libraries (Zustand, XState, Dexie) — RFC 0018 §7. +- No network calls to external services in tests; fixtures deterministic. +- Do not delete existing regression tests that assert legacy behaviour; + update them to assert SolidJS behaviour instead. +- Commit gates: `make all` (check-fmt, lint, test, spelling) must pass, plus + the new front-end gates added by this plan. +- British English (en-GB-oxendict) in prose and documentation. + +## Tolerances (exception triggers) + +- Scope: if replacing a Python e2e scenario requires building a **new** UI + surface larger than ~300 lines (for example, a full pairing flow or TEE + panel), stop and record the decision rather than building it; mark the + scenario as legacy-gated with documented rationale. +- Dependencies: no new Rust crate dependencies are expected for asset + embedding; if one becomes necessary (for example, `include_dir`), record it + in the Decision Log. No new JS runtime dependencies beyond what + `axinite-mockup` already uses. +- Iterations: if a gate still fails after 4 fix attempts, stop and escalate. +- Interface: changes to daemon route semantics (beyond adding + `GET /api/features`) require escalation; the SPA adapts to the daemon, not + the reverse. + +## Risks + +- Risk: the Python e2e suite (`tests/e2e/scenarios/`) asserts legacy DOM + selectors and flows (chat, extensions, skills, tool approval, SSE reconnect, + HTML injection) that the SolidJS UI does not yet cover with equivalent + affordances. + Severity: high. Likelihood: high. + Mitigation: milestone 8 audits each scenario; update selectors where the + SolidJS UI has the surface; where it does not, keep the scenario running + against the legacy fallback UI (explicit env-var opt-in) with a tracking + note, rather than deleting it. +- Risk: the SPA lacks gateway auth (gap analysis G1), so serving it as + default against a token-protected daemon would ship a broken UI. + Severity: high. Likelihood: certain until fixed. + Mitigation: milestone 4 ports the legacy token boot flow (sessionStorage + token, `Authorization: Bearer`, `?token=` for SSE) into the typed client. +- Risk: hashed Vite asset filenames complicate compile-time embedding. + Mitigation: configure stable output filenames so `include_str!` continues to + work with a fixed, small file set. +- Risk: checked-in build artefacts drift from source. + Mitigation: `make frontend-build` regenerates; a freshness check compares a + rebuild against the committed artefacts in CI/gates. +- Risk: contract drift between mock backend and daemon (gap analysis §13). + Mitigation: milestone 3 fixes the known breaks (`LogEntry.target`, job + prompt `{content, done?}`, extension install `{name, url?, kind?}`) in the + shared `contracts.ts`, which the mock backend imports directly. + +## Progress + +- [x] (2026-07-19 10:20Z) Recon complete: legacy web channel, mockup repo, + build/CI wiring (three parallel reconnaissance passes). +- [x] (2026-07-19 10:30Z) ExecPlan drafted. +- [x] (2026-07-19 10:55Z) M1: imported `axinite-mockup` as `web-src/`; + isolated build + checks pass (commit 8fa5d2b9). +- [x] (2026-07-19 11:00Z) M2: root base path, stable asset filenames, PWA + and GitHub Pages scaffolding removed, preview-server SPA fallback + (commit 056d90cc). +- [x] (2026-07-19 11:05Z) M3: contract fixes (`LogEntry.target`, + `JobPromptRequest { content, done? }`, full extension install shape) in + `contracts.ts` + mock backend, pinned by + `api-contract-alignment.test.ts` (commit 6fc69902). +- [x] (2026-07-19 11:10Z) M4: auth boot flow — token module, bearer + injection, SSE query token, `AuthGate` with anonymous-probe bypass for + the stub; localized in all ten locales (commit bae63224). +- [x] (2026-07-19 11:20Z) M5: gateway serves the built SPA by default + (embedded `src/channels/web/static/solid/`); legacy behind + `AXINITE_WEB_UI=legacy`; Make targets `frontend-build`/`frontend-verify` + etc.; five Rust serving tests (commit 617fb6c0). +- [x] (2026-07-19 11:25Z) M6: env-var-driven `GET /api/features` in the + gateway with compiled defaults mirroring the SPA registry + (commit 44750c29). +- [x] (2026-07-19 11:30Z) M7: stub flags flattened to the RFC 0009 map + with `FEATURE_FLAG_*` overrides; `MOCK_FAILURES` failure fixtures; + in-process contract tests for HTTP shapes and SSE ordering + (commit d0d43217). +- [x] (2026-07-19 11:45Z) Browser validation via Playwright MCP against the + stub: initial load from HTTP fixtures, SSE-driven chat turn, flag toggle + hiding the Skills nav entry, `MOCK_FAILURES` error state, clean console on + the happy path. Three defects found and fixed with regression tests + (nav flag gating, jobs error notice, SSE error-event JSON parsing) + (commit 96466513). +- [x] (2026-07-19 11:50Z) css-view layout validation on all six routes (84 to + 284 nodes per route): no element extends past the viewport except the + decorative `position: fixed` watermark; `scrollWidth == innerWidth` at + 1280, 768, and 375 px; the jobs table scrolls inside its own + `overflow-x: auto` wrap. +- [x] (2026-07-19 12:00Z) M8: docs (`docs/solidjs-frontend.md`, transitional + banner in `docs/front-end-architecture.md`, README pointer, web module + CLAUDE.md route tables); `tests/web_static_app.test.mjs` still passes + (targets the retained legacy assets); Python e2e conftest pins + `AXINITE_WEB_UI=legacy` with rationale. +- [x] (2026-07-19 12:40Z) M9: gates green — `make check-fmt`, `make lint` + (clippy plus whitaker after splitting `ui_assets.rs` out of + `static_files.rs`), `make typecheck`, `make markdownlint`, `make nixie`, + `make frontend-test` (45 unit + 2 a11y), `make frontend-verify`, full + `cargo nextest --workspace` (4252 passed), web-channel subset re-run after + the module split (148 passed), `node --test tests/web_static_app.test.mjs` + (8 passed). CodeRabbit CLI reviewed the branch diff (121 files): zero + findings, no rate limiting (commit 586983fe). + +## Surprises & discoveries + +- Observation: the repo already contains RFC 0018 — a full staged adoption + plan for exactly this migration — plus RFC 0009 (feature flags) and + `docs/solidjs-pwa-gap-analysis.md` (a precise inventory of contract breaks). + Evidence: `docs/rfcs/0018-solidjs-front-end-adoption.md`. + Impact: this plan follows RFC 0018's stages and treats the gap analysis as + the contract-fix backlog. +- Observation: `GET /api/jobs/{id}/events` is documented as SSE in module + docs but is actually paginated JSON; live job events arrive on the global + `/api/chat/events` stream. + Evidence: `src/channels/web/handlers/job_control/events.rs`. + Impact: the stub and SPA must not invent a per-job SSE stream. +- Observation: the legacy UI never opens the WebSocket endpoint; it is + SSE-only. Evidence: no `new WebSocket` in `app.js`. + Impact: the SPA can standardize on JSON + SSE (RFC 0018 open question + resolved for this migration). +- Observation: `tests/web_static_app.test.mjs` string-slices function bodies + out of `app.js` — it breaks the moment the legacy file is no longer the + primary UI source. Impact: migrate its assertions to the SolidJS skills + module (milestone 8). + +## Decision log + +- Decision: proceed with implementation without a separate approval pause. + Rationale: the commissioning task explicitly instructs end-to-end delivery + and validation and the session is autonomous; that instruction is treated as + standing approval per the execplans skill's standing-instruction clause. + Date/Author: 2026-07-19, Claude. +- Decision: place the browser workspace at `web-src/`. + Rationale: matches the existing repo convention that `*-src/` directories + are standalone source trees outside the Cargo workspace (`channels-src/`, + `tools-src/`), which is exactly the relationship the SPA has to the binary. + Date/Author: 2026-07-19, Claude. +- Decision: commit built SPA artefacts (small, stable-named set under + `web-src/dist/`) and embed them with `include_str!`/`include_bytes!`, rather + than building JS from `build.rs` or serving from disk. + Rationale: keeps `cargo build` hermetic (no Bun requirement for Rust-only + contributors, Docker, wix, release builds), preserves the one-binary + operational model RFC 0009 explicitly prizes, and mirrors the current + checked-in-asset model. Drift is controlled by a freshness gate. + Date/Author: 2026-07-19, Claude. +- Decision: gateway-side entrypoint selection via `AXINITE_WEB_UI=legacy` + environment variable rather than full `FeatureFlagRegistry` routing. + Rationale: RFC 0009's deployment-scoped settings persistence is a large + Rust work-item; an env-var switch satisfies RFC 0018's rollback requirement + now and can be upgraded to registry-backed routing when RFC 0009 lands. The + new UI is the default either way. + Date/Author: 2026-07-19, Claude. +- Decision: keep the Python e2e suite (`tests/e2e/`) on the legacy shell by + pinning `AXINITE_WEB_UI=legacy` in its conftest, rather than rewriting the + scenarios against the SolidJS DOM in this change. + Rationale: the suite binds to roughly eighty legacy selectors (tab bar, + approval overlay, configure modal, `?token=` boot) whose SolidJS + equivalents either differ structurally or do not exist yet; rewriting + exceeds the per-scenario tolerance in this plan. The SolidJS UI has its + own browser-level coverage (the `web-src` Playwright suite plus the + Playwright MCP validation recorded above). Migrating the Python scenarios + route-by-route is explicit follow-up work aligned with RFC 0018 Stage 4. + Date/Author: 2026-07-19, Claude. +- Decision: implement `GET /api/features` now, minimally, resolving only + `FEATURE_FLAG_` environment variables over compiled + defaults (no settings-table persistence, no deployment header requirement). + Rationale: the SPA's flag provider already consumes this endpoint; leaving + it 404 would silently mask integration risk (gap G2). The env-var layer is + the top of RFC 0009's precedence chain, so this is forward-compatible. + Date/Author: 2026-07-19, Claude. + +## Outcomes & retrospective + +Delivered against the original purpose: the SolidJS SPA is the default +gateway UI (embedded, one-binary model preserved); the legacy shell survives +only behind `AXINITE_WEB_UI=legacy`; `make frontend-stub` runs the UI without +the daemon with deterministic HTTP fixtures, SSE streams, runtime flag +overrides, and failure fixtures; contract, unit, behaviour, a11y, browser +(Playwright MCP), and layout (css-view) validation all passed, and CodeRabbit +reported zero findings. + +What browser validation earned beyond the test suites: three real defects +(nav ignoring route flags, silent list-failure state, SSE error-event JSON +crash) that no existing suite covered — each now has a regression test. + +Remaining follow-up work: + +- Rewrite the Python e2e scenarios (`tests/e2e/`) against the SolidJS DOM + route-by-route (RFC 0018 Stage 4) and then retire the legacy shell and its + assets (Stage 5), including `tests/web_static_app.test.mjs`. +- Implement the RFC 0009 settings-table/deployment-scoped flag layer beneath + the env-var resolution in `handlers/features.rs`. +- Close the remaining UI parity gaps catalogued in + `docs/solidjs-pwa-gap-analysis.md` (logs as a route, restart/TEE/pairing + surfaces, chat media, jobs detail fidelity). + +Lessons: the mockup's own e2e spec was stale against its components (chat and +memory headings), so imported suites need verification before trust; the +`typos` en-GB-oxendict gate interacts noisily with vendored front-end code +and needed a deliberate exclusion policy (generated artefacts, translations, +CSS syntax, vendored docs) rather than word-by-word fixes; and gate runs and +editing must not overlap in one worktree — a scrutineer pass ran while the +module split landed, which muddied its report. + +## Context and orientation + +Key current-state facts (verified by code inspection): + +- Legacy assets: `src/channels/web/static/{index.html,style.css,app.js,favicon.ico}` + embedded via `include_str!`/`include_bytes!` in + `src/channels/web/handlers/static_files.rs` (`public_routes()` maps `/`, + `/style.css`, `/app.js`, `/favicon.ico`). +- Auth: bearer token, constant-time compare (`src/channels/web/auth.rs`); + `?token=` query fallback only for GET `/api/chat/events`, + `/api/logs/events`, `/api/chat/ws`. +- SSE: `/api/chat/events` (event types `response, thinking, tool_started, + tool_completed, tool_result, stream_chunk, status, job_started, job_message, + job_tool_use, job_tool_result, job_status, job_result, approval_needed, + auth_required, auth_completed, extension_status, image_generated, error, + heartbeat`; Axum keep-alive every 30 s) and `/api/logs/events` + (`event: log`, replays recent entries then streams). +- HTTP surface consumed by the legacy UI: chat (send/threads/history/new + thread/approval/auth-token/auth-cancel), memory (tree/list/read/write/ + search), jobs (list/summary/detail/cancel/restart/prompt/events(JSON)/ + files), routines (list/summary/detail/runs/trigger/toggle/delete), skills + (list/search/install/delete), extensions (list/tools/registry/install/ + activate/remove/setup), settings, logs level, gateway status, pairing, + project file browser. Full inventory: recon notes in + `docs/solidjs-pwa-gap-analysis.md` §2 and `src/channels/web/CLAUDE.md`. +- Mockup (`/home/leynos/Projects/axinite-mockup`): Bun workspace; SolidJS + + TanStack Router/Query + Kobalte + Tailwind/DaisyUI + i18next/Fluent; typed + API modules in `axinite/src/lib/api/` with all contracts in `contracts.ts`; + feature-flag registry of 13 flags in `axinite/src/lib/feature-flags/` with + `/api/features` fetch, localStorage overrides, and a `?debug-flags=1` debug + panel; Bun mock backend (`mock-backend/src/server.ts`, port 8787) plus + preview server (port 2020) proxying `/api/*`; `scripts/dev.ts` orchestrates + mock API + `vite build --watch` + preview; vitest unit + a11y suites; + Playwright e2e (`axinite/tests/e2e/app-shell.pw.ts`). +- Known contract breaks to fix (gap analysis): missing auth (G1), + `/api/features` absent from gateway (G2), `LogEntry.source` vs daemon + `target` (G3), job prompt `{prompt}` vs `{content, done?}` (G4), extension + install narrowed to `{name}` (§11.1). +- Existing tests affected: `tests/web_static_app.test.mjs` (string-slices + `app.js`), Python Playwright e2e in `tests/e2e/scenarios/` (drives legacy + DOM against the real binary). + +## Plan of work + +M1 — Import the workspace. Copy `axinite-mockup` (from +`/home/leynos/Projects/axinite-mockup`, excluding `.git`, GitHub Pages +scaffolding, and mockup-repo docs that do not transfer) into `web-src/`. +Keep bun, biome, vitest, playwright configs. Run `bun install`, +`bun run check:types`, `bun run lint`, `bun run test` inside `web-src/` and +fix breakage caused by the move only. + +M2 — Serving shape. Change the Vite base path to `/` (drop the +`/axinite-mockup` GH Pages base and the per-route MPA HTML duplication if it +exists only for Pages fallback), and configure stable build filenames +(`dist/index.html`, `dist/assets/app.js`, `dist/assets/app.css`, plus the +favicon) so compile-time embedding stays a fixed file list. + +M3 — Contract fixes in `web-src/axinite/src/lib/api/contracts.ts` and the +mock backend: `LogEntry.target`, `JobPromptRequest { content, done? }`, +`InstallExtensionRequest { name, url?, kind? }`. Update the components and +fixtures that consume them, with unit tests. + +M4 — Auth. Add a token boot flow to the SPA: an unauthenticated state that +prompts for the gateway token, stores it in `sessionStorage`, sends +`Authorization: Bearer` on `fetch`, and appends `?token=` to the two SSE +URLs. The mock backend ignores tokens (stub stays auth-free); a +`VITE_`-independent runtime check keeps the stub flow tokenless via a stub +flag (`/api/features` fixture) or by the mock accepting all requests. + +M5 — Gateway integration. Add `make frontend-build` (bun build into +`web-src/dist/`, committed). Rewrite `static_files.rs` handlers to embed the +SolidJS artefacts as the default for `/`, `/assets/app.js`, +`/assets/app.css`, `/favicon.ico`; keep the legacy `index.html`/`app.js`/ +`style.css` handlers reachable only when `AXINITE_WEB_UI=legacy` is set at +startup. Add a freshness check (`make frontend-verify`) comparing a rebuild +with the committed dist. Update `src/channels/web/CLAUDE.md`, +`docs/front-end-architecture.md`. + +M6 — `GET /api/features` in the gateway: env-var-driven map, compiled +defaults for the 13 SPA flags, unit tests. Wire into `protected_routes()`. + +M7 — Stub runtime. `make frontend-stub` at repo root runs the Bun dev +orchestration (mock API + build watch + preview proxy on a documented port). +Mock backend gains env-driven flag overrides (`FEATURE_FLAG_`), and its +fixtures are checked for determinism. Add contract tests: vitest suites that +start the mock server and assert each stubbed HTTP route's response shape and +the SSE routes' event order, headers, and heartbeat framing. + +M8 — Regression migration. Replace `tests/web_static_app.test.mjs` string +slicing with tests against the SolidJS skills module. Audit each Python e2e +scenario: update selectors/flows to the SolidJS UI where the surface exists; +gate any legacy-only scenario on `AXINITE_WEB_UI=legacy` with a documented +parity note. Update docs (README/docs) with stub usage, stubbed routes, flag +overrides, and remaining-legacy rationale. + +M9 — Validation. Playwright MCP smoke against the stub (load, HTTP-populated +state, SSE-driven update, flag toggle, failure fixture, console cleanliness); +css-view layout checks at representative viewports; `make all`; wasm/github +tool gates untouched; scrutineer runs the full gate set; CodeRabbit CLI on +the final diff (15-minute retry once on rate limit). + +## Concrete steps + +Representative commands (run from repo root unless stated): + + cd web-src && bun install && bun run check:types && bun run lint \ + && bun run test + make frontend-build # bun vite build -> web-src/dist (committed) + make frontend-stub # daemon-free stub: mock API + preview on :2020 + make frontend-verify # rebuild and diff against committed dist + make all # Rust gates: check-fmt, lint, test, spelling + +Expected: all commands exit 0; `make frontend-stub` prints the preview URL; +opening it shows the SolidJS shell populated from mock fixtures. + +## Validation and acceptance + +- Unit: vitest suites in `web-src` pass, including new tests for contract + shapes, flag resolution, and SSE client parsing. +- Contract: new vitest integration tests boot the mock backend on an + ephemeral port and assert route shapes and SSE event sequences + deterministically. +- Rust: `cargo nextest run --workspace` passes; new `/api/features` and + entrypoint-selection tests pass; `make all` green. +- Browser: Playwright MCP scenario list in M9 all observed manually via MCP + against `make frontend-stub`; no fatal console errors. +- Red-Green-Refactor: each code milestone adds its failing test first (for + example, the contract tests for `LogEntry.target` fail against the unfixed + mock, then pass); where a change is pure asset wiring, the observable + substitute is the gateway integration test asserting the served + `index.html` contains the SolidJS mount point. +- CodeRabbit CLI reviewed the final diff (or the rate-limit protocol was + followed and recorded). + +## Idempotence and recovery + +Every milestone is committed separately; `git revert` of a milestone commit +restores the previous state. `make frontend-build` is idempotent. The legacy +UI remains embedded and selectable via `AXINITE_WEB_UI=legacy` until a future +cleanup removes it (RFC 0018 Stage 5 completion). + +## Interfaces and dependencies + +- `web-src/axinite/src/lib/api/client.ts`: gains + `setGatewayToken(token: string)`, bearer-header injection, and + `createEventStream(url)` token-query support. +- `src/channels/web/handlers/static_files.rs`: serves SolidJS artefacts by + default; exposes `fn ui_variant() -> UiVariant` (Legacy | Solid) resolved + from `AXINITE_WEB_UI` once at startup. +- `src/channels/web/handlers/features.rs` (new): + `GET /api/features -> JSON object {flag_name: bool}` resolving + `FEATURE_FLAG_` env vars over compiled defaults. +- `mock-backend/src/state.ts`: flag defaults overridable via + `FEATURE_FLAG_` env vars at stub start. + +No new Rust dependencies. No new JS dependencies beyond the mockup's own. diff --git a/docs/front-end-architecture.md b/docs/front-end-architecture.md index 14e6010a2..966bc4bb3 100644 --- a/docs/front-end-architecture.md +++ b/docs/front-end-architecture.md @@ -1,9 +1,17 @@ # Axinite front-end architecture +> **Transitional note (RFC 0018 adoption).** The default browser UI is now +> the SolidJS single-page application authored in `web-src/` and served from +> embedded `src/channels/web/static/solid/` assets. This document describes +> the **legacy** handwritten shell, which remains embedded solely as an +> operator rollback path selected with `AXINITE_WEB_UI=legacy`. For the +> current front-end, its stub runtime, and its commands, see +> `docs/solidjs-frontend.md`. + ## Front matter -- **Status:** Draft design reference for the currently implemented web front - end. +- **Status:** Legacy reference for the fallback web front end (superseded by + `docs/solidjs-frontend.md` for the default UI). - **Scope:** The browser-facing web gateway, including static asset delivery, client-side interface generation, backend integration, and runtime communication paths. diff --git a/docs/rfcs/0009-feature-flags-frontend.md b/docs/rfcs/0009-feature-flags-frontend.md index 49b68b105..9046feabf 100644 --- a/docs/rfcs/0009-feature-flags-frontend.md +++ b/docs/rfcs/0009-feature-flags-frontend.md @@ -3,8 +3,9 @@ ## Preamble - **RFC number:** 0009 -- **Status:** Proposed +- **Status:** Implemented (with noted deviations) - **Created:** 2026-03-14 +- **Implemented:** 2026-07-19 (see §Implementation notes) ## Summary @@ -487,6 +488,44 @@ _Table 2: Comparison of alternatives._ reconnect, or is a single fetch at boot sufficient given that operator overrides update the registry immediately? +## Implementation notes and deviations + +The mechanism shipped with the SolidJS front-end adoption +(`docs/execplans/adopt-solidjs-ui-followups.md`). The delivered behaviour +follows this RFC with the following deliberate deviations: + +- **Storage**: deployment-scoped overrides live in a dedicated + `feature_flag_overrides` table (primary key `(deployment_id, flag_name)`; + Postgres migration `V18`, libsql incremental 18) rather than a + deployment-aware extension of the `settings` table. This keeps the + `(user_id, key)` settings contract untouched and avoids a libsql + table rebuild; the API surface (`feature_flag:` key prefix, + `X-Deployment-Id` header) is unchanged. +- **Reads default the deployment**: `GET /api/features` treats + `X-Deployment-Id` as optional and resolves to the `"default"` + deployment when absent, because the browser boot fetch has no + deployment identity source. Writes require the header, as specified. +- **Settings API access to flag keys**: `GET` and `DELETE` of + `feature_flag:` keys through `/api/settings` return 400, directing + callers to `GET /api/features`; flag rows never enter the user-scoped + settings table. +- **Subsystem-availability defaults** are implemented as a + disable-only layer: when a flag's backing subsystem is absent from + `GatewayState` (jobs and routines runtimes, extension manager, skill + registry, log broadcaster), the flag defaults to `false`; presence of + a subsystem falls through to the compiled default rather than + enabling a flag early. Environment variables and operator overrides + still take precedence. +- **Gateway version correlation** (open question 1) is provided as an + `X-Axinite-Version` response header on `GET /api/features`, + preserving the flat boolean-map body. +- **Flag-change SSE event** (open question 2) remains deferred; it is + tracked as roadmap task 4.5.7. +- **Front-end consumption** (§5) is realized in the SolidJS workspace + (`web-src/axinite/src/lib/feature-flags/`) rather than the retired + legacy `app.js`; the browser additionally supports localStorage + overrides for preview and testing. + ## Recommendation Adopt the proposed design: a dedicated `GET /api/features` endpoint backed by a diff --git a/docs/roadmap.md b/docs/roadmap.md index d8e3bd911..a3e26e153 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -1160,6 +1160,16 @@ out behind flags. access to the mutable registry, endpoint response shape (boolean map), and no hot-path database hits, and prove that invalid flag names are discarded with warnings. +- [ ] 4.5.7. Emit a `feature_flags_changed` Server-Sent Events (SSE) event + when a deployment-scoped override changes. Requires 4.5.3. + - See + [RFC 0009 §Open questions](./rfcs/0009-feature-flags-frontend.md#open-questions). + - Success: applying a `feature_flag:` override through the settings API + broadcasts a `feature_flags_changed` event (carrying the deployment + identifier) on the chat SSE stream, the SolidJS front end invalidates its + cached flag map and re-fetches `GET /api/features` on receipt, connected + browsers reflect the new flag state without a page reload, and the mock + backend mirrors the event for stub-driven tests. ## 5. Add model, reasoning, and citation control diff --git a/docs/solidjs-frontend.md b/docs/solidjs-frontend.md new file mode 100644 index 000000000..d9787617e --- /dev/null +++ b/docs/solidjs-frontend.md @@ -0,0 +1,217 @@ +# SolidJS front-end: development, stub runtime, and serving + +## Front matter + +This document describes the SolidJS browser front-end adopted from the +`axinite-mockup` repository (RFC 0018), how the gateway serves it, and how to +develop and validate it without running the full Axinite daemon. It supersedes +the legacy description in `docs/front-end-architecture.md`, which is retained +for the legacy fallback shell. + +## 1. Overview + +The default browser UI is a SolidJS single-page application (SPA) authored in +`web-src/`. It uses Vite for builds, TanStack Router for the route tree, +TanStack Query plus Solid signals for state, Kobalte for accessible +primitives, Tailwind/daisyUI semantic classes for styling, and +i18next/Fluent bundles for ten locales. + +The build output is copied into `src/channels/web/static/solid/` and embedded +into the gateway binary with `include_str!`/`include_bytes!`, exactly like the +legacy assets were. Operators still deploy one binary; no Node or Bun +toolchain is needed to build or run the Rust gateway. + +The legacy handwritten shell (`src/channels/web/static/{index.html,style.css, +app.js}`) remains embedded purely as a rollback path. Setting +`AXINITE_WEB_UI=legacy` before starting the gateway serves the legacy shell +instead of the SPA. The Python end-to-end suite in `tests/e2e/` drives the +SolidJS UI against the real daemon. + +## 2. Commands + +All commands run from the repository root. + +| Command | Purpose | +| ----------------------- | -------------------------------------------------------------- | +| `make frontend-install` | `bun install --frozen-lockfile` in `web-src/`. | +| `make frontend-build` | Vite build, then refresh `src/channels/web/static/solid/`. | +| `make frontend-verify` | Rebuild and fail if the embedded copy is stale. | +| `make frontend-check` | Biome format/lint, TypeScript, semantic-CSS rules. | +| `make frontend-test` | `frontend-check` plus unit, a11y, and Fluent suites. | +| `make frontend-full` | The complete `verify:full` verification chain. | +| `make frontend-stub` | Daemon-free stub runtime (see below). | + +The semantic-CSS rules cover the classlist, semgrep, and stylelint +checks; `verify:full` adds the Tailwind compile check, the workspace +Playwright spec, and `moz-fluent-lint` on top of every other suite. +Inside `web-src/`, the underlying Bun scripts are available directly +(`bun run test`, `bun run test:e2e`, `bun run build`, and so on). The +`semantic` and `verify:full` scripts fetch semgrep and moz-fluent-linter +through `uvx` on first use. Continuous integration runs `make +frontend-full` and the `make frontend-verify` staleness gate for any pull +request touching `web-src/` or the embedded assets +(`.github/workflows/frontend.yml`); Playwright's Chromium must be +installed for the workspace spec (`bunx playwright install chromium`). + +After changing anything in `web-src/` that affects the built app, run +`make frontend-build` and commit the refreshed +`src/channels/web/static/solid/` output together with the source change. +`make frontend-verify` is the staleness gate. + +## 3. The stub runtime + +`make frontend-stub` starts the front-end without the Axinite daemon: + +- a Bun mock API (`web-src/mock-backend/src/server.ts`) on port 8787 + (override with `MOCK_API_PORT`), +- a Vite build watcher, and +- a preview server on (override with `PREVIEW_PORT`) + that serves the built SPA, falls back to the app shell for extension-less + routes, and proxies `/api/*` to the mock API. + +The mock backend is a contract harness, not a second daemon: it holds +deterministic in-memory fixtures, ignores authentication, and persists +nothing. + +### 3.1 Stubbed HTTP routes + +The mock implements the routes the SPA consumes, with gateway-shaped +payloads (`web-src/axinite/src/lib/api/contracts.ts` documents the shapes): + +- `GET /api/gateway/status`, `GET /api/features` +- Chat: `GET /api/chat/threads`, `POST /api/chat/thread/new`, + `GET /api/chat/history`, `POST /api/chat/send` (returns 202), + `POST /api/chat/approval`, `POST /api/chat/auth-token`, + `POST /api/chat/auth-cancel` +- Pairing: `GET /api/pairing/{channel}`, + `POST /api/pairing/{channel}/approve` (a deterministic pending request + `PAIR-1234` exists on the `whatsapp` channel; approving the code + `rate-limited` returns the daemon's plain-text 429) +- Memory: `GET /api/memory/tree`, `GET /api/memory/read`, + `POST /api/memory/search`, `POST /api/memory/write` +- Jobs: `GET /api/jobs`, `GET /api/jobs/summary`, `GET /api/jobs/{id}`, + `GET /api/jobs/{id}/events` (paginated JSON, mirroring the daemon — + not SSE), `GET /api/jobs/{id}/files/list`, `GET /api/jobs/{id}/files/read`, + `POST /api/jobs/{id}/cancel|restart|prompt` +- Routines: `GET /api/routines`, `GET /api/routines/summary`, + `GET /api/routines/{id}`, `GET /api/routines/{id}/runs`, + `POST /api/routines/{id}/trigger|toggle`, `DELETE /api/routines/{id}` +- Extensions: `GET /api/extensions`, `GET /api/extensions/tools`, + `GET /api/extensions/registry`, `POST /api/extensions/install`, + `POST /api/extensions/{name}/activate|remove`, + `GET/POST /api/extensions/{name}/setup` +- Skills: `GET /api/skills`, `POST /api/skills/search`, + `POST /api/skills/install`, `DELETE /api/skills/{name}` +- Logs: `GET /api/logs/level`, `POST /api/logs/level` + +Unknown routes return 404 with a JSON error body. + +### 3.2 Stubbed SSE routes + +- `GET /api/chat/events` — `text/event-stream`; frames use + `event: ` matching the payload's `type` field (the daemon's + `SseEvent` tagging). Sending a chat message produces a deterministic + lifecycle: `thinking`, then `tool_started`/`tool_completed`/`tool_result` + on fixed short delays, then `response`. Heartbeat `event: heartbeat` + frames are emitted every 15 seconds. Deterministic extras: the exact + message `/restart` emits a `restart`-named tool sequence and a + "Restart initiated" response; a prompt containing "image" additionally + emits `image_generated` with an inline data URL; a prompt containing + "job" emits `job_started`; attached `images[]` are acknowledged in the + response text; a successful `POST /api/chat/auth-token` publishes + `auth_completed`. +- `GET /api/logs/events` — replays the fixture log history as + `event: log` frames (entries carry `level`, `target`, `message`, + `timestamp`, matching `log_layer.rs`), then streams new entries; comment + keep-alives (`: keep-alive`) every 15 seconds. + +Neither route implements `Last-Event-ID` or `retry:` hints; the real daemon +does not either — browsers rely on plain `EventSource` auto-reconnect and +history replay on reconnect. + +### 3.3 Failure fixtures + +Set `MOCK_FAILURES` to a comma-separated list of request paths to make the +stub return a deterministic HTTP 500 for those routes: + +```sh +MOCK_FAILURES=/api/jobs make frontend-stub +``` + +The jobs route renders a visible, localized error notice in this state; use +the same mechanism to exercise other error paths. + +### 3.4 Feature flags in the stub + +The stub serves `GET /api/features` as a flat `{"flag_name": bool}` map +(RFC 0009 shape) with the same compiled defaults as the gateway. Overrides, +in increasing precedence: + +1. Environment at stub start: `FEATURE_FLAG_=true|false` + (for example `FEATURE_FLAG_ROUTE_SKILLS=false make frontend-stub`). +2. Browser-local override: open the app with `?debug-flags=1` and use the + "Feature flags" maintainer panel to force any flag on or off; overrides + persist in `localStorage` under `axinite.feature-flag-overrides`, which + Playwright can also seed directly. + +The resolution order in the SPA is local override, then server value, then +registry default (`web-src/axinite/src/lib/feature-flags/`). + +## 4. Serving from the gateway + +`src/channels/web/handlers/static_files.rs` embeds the built artefacts and +serves, by default (`UiVariant::Solid`): + +- the app shell at `/` and every client route (`/chat`, `/memory`, `/jobs`, + `/routines`, `/extensions`, `/skills`, `/logs`), +- `/assets/app.js`, `/assets/index.css`, `/assets/axinite32.ico`, + `/favicon.ico`, and +- `/locales/{locale}/common.ftl` for the ten locale bundles. + +Vite is configured to emit stable, hash-free artefact names so the embedded +file list stays fixed; everything is served with `Cache-Control: no-cache`. + +`GET /api/features` on the gateway resolves, per flag: `FEATURE_FLAG_` +environment variable, then the deployment-scoped operator override +(persisted via `PUT /api/settings/feature_flag:` with an +`X-Deployment-Id` header), then a subsystem-availability default (flags +whose backing runtime is absent resolve off), then the compiled default +(`src/channels/web/handlers/features.rs`). The response carries an +`X-Axinite-Version` header; see RFC 0009's implementation notes for the +full contract and deviations. + +### 4.1 Authentication + +The gateway protects `/api/*` with a bearer token. The SPA probes +`GET /api/gateway/status` at boot: if the gateway answers anonymously (the +stub), the app loads directly; on 401 it presents a token form, verifies the +token, stores it in `sessionStorage`, sends it as `Authorization: Bearer` on +every request, and appends `?token=` to the two SSE URLs (which cannot carry +headers). + +## 5. How the stub differs from the daemon + +- No authentication, no rate limiting, no persistence. +- Chat responses are canned fixtures on fixed timers, not model output. +- Only the routes listed above exist; settings, pairing, OAuth, project + file browsing, WebSocket, and the OpenAI-compatible surface are absent. +- Job/routine/extension/skill mutations mutate in-memory state only. + +## 6. Test layers + +- `web-src` vitest (`make frontend-test`): unit and behaviour tests for the + API clients, auth token handling, feature flags, components, plus + `mock-backend-contract.test.ts`, which pins the stub's HTTP shapes and SSE + framing/ordering, and `api-contract-alignment.test.ts`, which pins the + browser contract to the daemon payload shapes. +- `web-src` Playwright (`bun run test:e2e` in `web-src/`): boots the full + stub stack and exercises navigation, locales, the logs dialog, and the + debug flag panel. +- Rust unit tests (`make test`): SPA shell serving on every route, stable + asset names, locale bundles, legacy-variant fallback, and feature-flag + resolution. +- Python e2e (`tests/e2e/`): drives the SolidJS UI against the real + daemon through a documented testability contract — the `?token=` boot + parameter, the `#auth-screen` marker, the `sse-status` indicator, and + the minimal `window.__axinite` hook object (close/reconnect the chat + stream, inject a chat event). See `tests/e2e/CLAUDE.md`. diff --git a/migrations/V18__feature_flag_overrides.sql b/migrations/V18__feature_flag_overrides.sql new file mode 100644 index 000000000..60700eeb8 --- /dev/null +++ b/migrations/V18__feature_flag_overrides.sql @@ -0,0 +1,15 @@ +-- Deployment-scoped feature-flag overrides (RFC 0009). +-- +-- Feature flags are deployment-scoped, not user-scoped, so they live in a +-- dedicated table rather than the (user_id, key)-keyed `settings` table. The +-- settings API exposes them through the `feature_flag:` key prefix plus an +-- `X-Deployment-Id` header, but persistence is keyed by (deployment_id, +-- flag_name). + +CREATE TABLE IF NOT EXISTS feature_flag_overrides ( + deployment_id TEXT NOT NULL, + flag_name TEXT NOT NULL, + enabled BOOLEAN NOT NULL, + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + PRIMARY KEY (deployment_id, flag_name) +); diff --git a/migrations/libsql_schema.sql b/migrations/libsql_schema.sql index 8529f13fa..99482a202 100644 --- a/migrations/libsql_schema.sql +++ b/migrations/libsql_schema.sql @@ -499,6 +499,19 @@ CREATE TABLE IF NOT EXISTS settings ( PRIMARY KEY (user_id, key) ); +-- ==================== Feature flags (RFC 0009) ==================== + +-- Deployment-scoped feature-flag overrides. Booleans store as INTEGER (0/1) +-- per the libSQL dialect. Mirrors PostgreSQL migration V18. Existing databases +-- gain this table via the incremental migration in `libsql_migrations.rs`. +CREATE TABLE IF NOT EXISTS feature_flag_overrides ( + deployment_id TEXT NOT NULL, + flag_name TEXT NOT NULL, + enabled INTEGER NOT NULL, + updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')), + PRIMARY KEY (deployment_id, flag_name) +); + -- ==================== Missing indexes (parity with PostgreSQL) ==================== -- agent_jobs diff --git a/src/bootstrap/tests/migration_support.rs b/src/bootstrap/tests/migration_support.rs index e22184eaf..ef0f87643 100644 --- a/src/bootstrap/tests/migration_support.rs +++ b/src/bootstrap/tests/migration_support.rs @@ -109,6 +109,22 @@ impl MigrationStore { } impl SettingsStore for MigrationStore { + fn list_deployment_flags<'a>( + &'a self, + _deployment_id: &'a str, + ) -> DbFuture<'a, Result, DatabaseError>> { + Box::pin(async { Ok(Vec::new()) }) + } + + fn set_deployment_flag<'a>( + &'a self, + _deployment_id: &'a str, + _flag_name: &'a str, + _enabled: bool, + ) -> DbFuture<'a, Result<(), DatabaseError>> { + Box::pin(async { Ok(()) }) + } + fn get_setting<'a>( &'a self, _user_id: UserId, diff --git a/src/channels/wasm/wrapper/tests/dispatch.rs b/src/channels/wasm/wrapper/tests/dispatch.rs index 77b7e2ba3..e7165fcab 100644 --- a/src/channels/wasm/wrapper/tests/dispatch.rs +++ b/src/channels/wasm/wrapper/tests/dispatch.rs @@ -3,111 +3,9 @@ use std::sync::Arc; use super::super::dispatch::DispatchContext; +use super::recording_store::RecordingSettingsStore; use crate::channels::wasm::wrapper::WasmChannel; -struct RecordingSettingsStore { - writes: std::sync::Mutex>, -} - -impl RecordingSettingsStore { - fn new() -> Self { - Self { - writes: std::sync::Mutex::new(Vec::new()), - } - } - - fn writes(&self) -> Vec { - self.writes - .lock() - .expect("settings writes lock poisoned") - .clone() - } -} - -fn ready_db_ok<'a, T: Send + 'a>( - value: T, -) -> crate::db::DbFuture<'a, Result> { - Box::pin(async move { Ok(value) }) -} - -impl crate::db::SettingsStore for RecordingSettingsStore { - fn get_setting<'a>( - &'a self, - _user_id: crate::db::UserId, - _key: crate::db::SettingKey, - ) -> crate::db::DbFuture<'a, Result, crate::error::DatabaseError>> - { - ready_db_ok(None) - } - - fn get_setting_full<'a>( - &'a self, - _user_id: crate::db::UserId, - _key: crate::db::SettingKey, - ) -> crate::db::DbFuture< - 'a, - Result, crate::error::DatabaseError>, - > { - ready_db_ok(None) - } - - fn set_setting<'a>( - &'a self, - _user_id: crate::db::UserId, - key: crate::db::SettingKey, - _value: &'a serde_json::Value, - ) -> crate::db::DbFuture<'a, Result<(), crate::error::DatabaseError>> { - Box::pin(async move { - self.writes - .lock() - .expect("settings writes lock poisoned") - .push(key.to_string()); - Ok(()) - }) - } - - fn delete_setting<'a>( - &'a self, - _user_id: crate::db::UserId, - _key: crate::db::SettingKey, - ) -> crate::db::DbFuture<'a, Result> { - ready_db_ok(false) - } - - fn list_settings<'a>( - &'a self, - _user_id: crate::db::UserId, - ) -> crate::db::DbFuture<'a, Result, crate::error::DatabaseError>> - { - ready_db_ok(Vec::new()) - } - - fn get_all_settings<'a>( - &'a self, - _user_id: crate::db::UserId, - ) -> crate::db::DbFuture< - 'a, - Result, crate::error::DatabaseError>, - > { - ready_db_ok(std::collections::HashMap::new()) - } - - fn set_all_settings<'a>( - &'a self, - _user_id: crate::db::UserId, - _settings: &'a std::collections::HashMap, - ) -> crate::db::DbFuture<'a, Result<(), crate::error::DatabaseError>> { - ready_db_ok(()) - } - - fn has_settings<'a>( - &'a self, - _user_id: crate::db::UserId, - ) -> crate::db::DbFuture<'a, Result> { - ready_db_ok(false) - } -} - #[tokio::test] async fn test_dispatch_emitted_messages_sends_to_channel() { use crate::channels::wasm::host::EmittedMessage; diff --git a/src/channels/wasm/wrapper/tests/mod.rs b/src/channels/wasm/wrapper/tests/mod.rs index 3b5d45994..3cdbe7bb4 100644 --- a/src/channels/wasm/wrapper/tests/mod.rs +++ b/src/channels/wasm/wrapper/tests/mod.rs @@ -6,6 +6,7 @@ mod channel; mod clone; mod convert; mod dispatch; +mod recording_store; mod store; use std::sync::Arc; diff --git a/src/channels/wasm/wrapper/tests/recording_store.rs b/src/channels/wasm/wrapper/tests/recording_store.rs new file mode 100644 index 000000000..271fb3156 --- /dev/null +++ b/src/channels/wasm/wrapper/tests/recording_store.rs @@ -0,0 +1,123 @@ +//! Recording settings-store test double shared by wrapper dispatch tests. +//! +//! Captures setting writes so tests can assert on persistence calls without a +//! real database. + +pub(super) struct RecordingSettingsStore { + writes: std::sync::Mutex>, +} + +impl RecordingSettingsStore { + pub(super) fn new() -> Self { + Self { + writes: std::sync::Mutex::new(Vec::new()), + } + } + + pub(super) fn writes(&self) -> Vec { + self.writes + .lock() + .expect("settings writes lock poisoned") + .clone() + } +} + +fn ready_db_ok<'a, T: Send + 'a>( + value: T, +) -> crate::db::DbFuture<'a, Result> { + Box::pin(async move { Ok(value) }) +} + +impl crate::db::SettingsStore for RecordingSettingsStore { + fn list_deployment_flags<'a>( + &'a self, + _deployment_id: &'a str, + ) -> crate::db::DbFuture<'a, Result, crate::error::DatabaseError>> { + ready_db_ok(Vec::new()) + } + + fn set_deployment_flag<'a>( + &'a self, + _deployment_id: &'a str, + _flag_name: &'a str, + _enabled: bool, + ) -> crate::db::DbFuture<'a, Result<(), crate::error::DatabaseError>> { + ready_db_ok(()) + } + + fn get_setting<'a>( + &'a self, + _user_id: crate::db::UserId, + _key: crate::db::SettingKey, + ) -> crate::db::DbFuture<'a, Result, crate::error::DatabaseError>> + { + ready_db_ok(None) + } + + fn get_setting_full<'a>( + &'a self, + _user_id: crate::db::UserId, + _key: crate::db::SettingKey, + ) -> crate::db::DbFuture< + 'a, + Result, crate::error::DatabaseError>, + > { + ready_db_ok(None) + } + + fn set_setting<'a>( + &'a self, + _user_id: crate::db::UserId, + key: crate::db::SettingKey, + _value: &'a serde_json::Value, + ) -> crate::db::DbFuture<'a, Result<(), crate::error::DatabaseError>> { + Box::pin(async move { + self.writes + .lock() + .expect("settings writes lock poisoned") + .push(key.to_string()); + Ok(()) + }) + } + + fn delete_setting<'a>( + &'a self, + _user_id: crate::db::UserId, + _key: crate::db::SettingKey, + ) -> crate::db::DbFuture<'a, Result> { + ready_db_ok(false) + } + + fn list_settings<'a>( + &'a self, + _user_id: crate::db::UserId, + ) -> crate::db::DbFuture<'a, Result, crate::error::DatabaseError>> + { + ready_db_ok(Vec::new()) + } + + fn get_all_settings<'a>( + &'a self, + _user_id: crate::db::UserId, + ) -> crate::db::DbFuture< + 'a, + Result, crate::error::DatabaseError>, + > { + ready_db_ok(std::collections::HashMap::new()) + } + + fn set_all_settings<'a>( + &'a self, + _user_id: crate::db::UserId, + _settings: &'a std::collections::HashMap, + ) -> crate::db::DbFuture<'a, Result<(), crate::error::DatabaseError>> { + ready_db_ok(()) + } + + fn has_settings<'a>( + &'a self, + _user_id: crate::db::UserId, + ) -> crate::db::DbFuture<'a, Result> { + ready_db_ok(false) + } +} diff --git a/src/channels/web/CLAUDE.md b/src/channels/web/CLAUDE.md index 35d36cd12..f82ee8508 100644 --- a/src/channels/web/CLAUDE.md +++ b/src/channels/web/CLAUDE.md @@ -18,7 +18,8 @@ bearer-token auth. | `handlers/` | Handler modules grouped by API domain. | | `openai_compat.rs` | OpenAI-compatible proxy routes. | | `util.rs` | Shared handler helpers. | -| `static/` | Embedded single-page app assets. | +| `static/solid/` | Embedded SolidJS SPA build output (default UI). | +| `static/` | Embedded legacy shell (rollback: `AXINITE_WEB_UI=legacy`). | ## API Routes @@ -126,17 +127,40 @@ require `X-Confirm-Action: true`. | GET | `/api/pairing/{channel}` | List pending pairing requests. | | POST | `/api/pairing/{channel}/approve` | Approve a pairing request. | | GET | `/api/gateway/status` | Server uptime, clients, and config. | +| GET | `/api/features` | Deployment feature flags (RFC 0009 subset). | | POST | `/v1/chat/completions` | OpenAI-compatible Large Language Model (LLM) proxy. | | GET | `/v1/models` | OpenAI-compatible model list. | ### Static And Project Files +The public asset routes depend on the UI variant (`AXINITE_WEB_UI`, +default `solid`; see `routes_for()` in `handlers/static_files.rs`). + +Solid variant (default). The app shell is served at `/` and at each +client-side route (`/chat`, `/memory`, `/jobs`, `/routines`, `/extensions`, +`/skills`) so deep links and reloads work. + +| Method | Path | Description | +| ------ | ------------------------------ | ---------------------------------- | +| GET | `/` and SPA routes | SolidJS app shell. | +| GET | `/assets/app.js` | SPA bundle. | +| GET | `/assets/index.css` | SPA stylesheet. | +| GET | `/assets/axinite32.ico` | App icon (also `/favicon.ico`). | +| GET | `/locales/{locale}/common.ftl` | Fluent locale bundle (10 locales). | + +Legacy variant (`AXINITE_WEB_UI=legacy`): + | Method | Path | Description | | ------ | -------------------------------- | -------------------------------- | -| GET | `/` | Single-page app HTML. | +| GET | `/` | Legacy single-page app HTML. | | GET | `/style.css` | App stylesheet. | | GET | `/app.js` | App JavaScript. | | GET | `/favicon.ico` | Favicon, cached for one day. | + +Both variants: + +| Method | Path | Description | +| ------ | -------------------------------- | -------------------------------- | | GET | `/projects/{project_id}/` | Redirect into the job browser. | | GET | `/projects/{project_id}/{*path}` | Serve an authenticated job file. | diff --git a/src/channels/web/handlers/feature_registry.rs b/src/channels/web/handlers/feature_registry.rs new file mode 100644 index 000000000..e2ed6fac8 --- /dev/null +++ b/src/channels/web/handlers/feature_registry.rs @@ -0,0 +1,144 @@ +//! In-memory registry of deployment-scoped feature-flag overrides (RFC 0009). +//! +//! The registry caches the operator overrides persisted in +//! `feature_flag_overrides`, keyed by deployment. It holds only the override +//! layer, not fully resolved flag values: resolution (environment variable > +//! deployment override > compiled default) happens in +//! [`super::features`] when serving `GET /api/features`. +//! +//! The registry is held in `GatewayState` behind an `Arc>` so +//! handlers can read it on the hot path and update it synchronously when an +//! operator writes an override through the settings API. Writes update both the +//! database and this registry, so the effect is visible on the next +//! `GET /api/features` without a restart. +//! +//! Hydration is lazy: on the first read for a deployment that has not yet been +//! loaded, [`super::features`] queries the store once and caches the overrides +//! here (see `ensure_deployment_hydrated`). This avoids threading an async +//! store load through the synchronous `GatewayChannel::new()` construction path. + +use std::collections::HashMap; + +use axum::http::HeaderMap; + +/// A deployment identifier (for example `"production"` or `"default"`). +pub type DeploymentId = String; + +/// Header carrying the deployment identifier for feature-flag reads and writes. +/// +/// Reads (`GET /api/features`) treat it as optional and fall back to +/// [`DEFAULT_DEPLOYMENT_ID`]; writes (`PUT /api/settings/feature_flag:`) +/// require it. +pub const DEPLOYMENT_ID_HEADER: &str = "x-deployment-id"; + +/// Deployment used when the `X-Deployment-Id` header is absent on reads. +pub const DEFAULT_DEPLOYMENT_ID: &str = "default"; + +/// Extract a trimmed, non-empty deployment identifier from request headers. +/// +/// Returns `None` when the header is absent, empty, whitespace-only, or not +/// valid UTF-8. +pub fn deployment_id_from_headers(headers: &HeaderMap) -> Option { + headers + .get(DEPLOYMENT_ID_HEADER) + .and_then(|value| value.to_str().ok()) + .map(str::trim) + .filter(|value| !value.is_empty()) + .map(str::to_string) +} + +/// A mutable registry of deployment-scoped feature-flag overrides. +/// +/// Maps deployment -> (flag name -> enabled). Presence of a deployment key +/// means it has been hydrated from the store, even if it has no overrides. +#[derive(Debug, Default)] +pub struct FeatureFlagRegistry { + /// Cached override states: deployment -> (name -> enabled). + flags: HashMap>, +} + +impl FeatureFlagRegistry { + /// Create an empty registry. + pub fn new() -> Self { + Self::default() + } + + /// Return the override for a single flag, if one is cached. + pub fn get(&self, deployment_id: &str, name: &str) -> Option { + self.flags + .get(deployment_id) + .and_then(|deployment_flags| deployment_flags.get(name).copied()) + } + + /// Insert or replace one deployment-scoped override. + pub fn set(&mut self, deployment_id: DeploymentId, name: String, enabled: bool) { + self.flags + .entry(deployment_id) + .or_default() + .insert(name, enabled); + } + + /// Whether a deployment's overrides have been loaded from the store. + /// + /// Returns `true` once the deployment has been hydrated (including when it + /// has no overrides), so callers can skip a repeat store query. + pub fn is_hydrated(&self, deployment_id: &str) -> bool { + self.flags.contains_key(deployment_id) + } + + /// Cache a deployment's overrides loaded from the store. + /// + /// Marks the deployment as hydrated even when `overrides` is empty. + pub fn hydrate(&mut self, deployment_id: DeploymentId, overrides: Vec<(String, bool)>) { + let entry = self.flags.entry(deployment_id).or_default(); + for (name, enabled) in overrides { + entry.insert(name, enabled); + } + } + + /// Return a copy of a deployment's cached overrides, if any. + pub fn overrides_for(&self, deployment_id: &str) -> HashMap { + self.flags.get(deployment_id).cloned().unwrap_or_default() + } +} + +#[cfg(test)] +mod tests { + //! Unit tests for the deployment-scoped feature-flag registry. + + use super::*; + + #[test] + fn get_returns_none_for_unknown_deployment_or_flag() { + let mut registry = FeatureFlagRegistry::new(); + assert_eq!(registry.get("production", "route_chat"), None); + registry.set("production".to_string(), "route_chat".to_string(), true); + assert_eq!(registry.get("production", "route_chat"), Some(true)); + assert_eq!(registry.get("production", "unknown"), None); + assert_eq!(registry.get("staging", "route_chat"), None); + } + + #[test] + fn hydrate_marks_deployment_loaded_even_when_empty() { + let mut registry = FeatureFlagRegistry::new(); + assert!(!registry.is_hydrated("default")); + registry.hydrate("default".to_string(), vec![]); + assert!(registry.is_hydrated("default")); + assert!(registry.overrides_for("default").is_empty()); + } + + #[test] + fn hydrate_populates_and_set_overwrites() { + let mut registry = FeatureFlagRegistry::new(); + registry.hydrate( + "production".to_string(), + vec![("panel_logs".to_string(), false)], + ); + assert_eq!(registry.get("production", "panel_logs"), Some(false)); + registry.set("production".to_string(), "panel_logs".to_string(), true); + assert_eq!(registry.get("production", "panel_logs"), Some(true)); + + let overrides = registry.overrides_for("production"); + assert_eq!(overrides.get("panel_logs"), Some(&true)); + } +} diff --git a/src/channels/web/handlers/features.rs b/src/channels/web/handlers/features.rs new file mode 100644 index 000000000..a6e7557ae --- /dev/null +++ b/src/channels/web/handlers/features.rs @@ -0,0 +1,346 @@ +//! Deployment feature flags for the browser UI. +//! +//! Implements the RFC 0009 delivery mechanism: the resolved flag map is exposed +//! at `GET /api/features`. Each flag resolves through the precedence chain +//! +//! 1. `FEATURE_FLAG_` environment variable (highest), +//! 2. deployment-scoped operator override (from the registry / store), +//! 3. subsystem-availability default (forces a flag off when its backing +//! subsystem is not wired into `GatewayState`; never enables a flag), +//! 4. compiled default (lowest). +//! +//! For the environment layer, the value `true` (case-insensitively) enables the +//! flag; any other set value disables it; unset falls through to the next +//! layer. +//! +//! Deployment resolution follows the ExecPlan decision: reads use the optional +//! `X-Deployment-Id` header, defaulting to `"default"` when absent so the +//! existing SPA boot fetch keeps working; writes (in the settings handler) +//! require the header. Overrides are cached in the +//! [`FeatureFlagRegistry`](super::feature_registry::FeatureFlagRegistry), which +//! is hydrated lazily from the store on the first read for a deployment. + +use std::collections::{BTreeMap, HashMap}; +use std::sync::Arc; + +use axum::{Json, Router, extract::State, http::HeaderMap, routing::get}; + +use crate::channels::web::handlers::feature_registry::{ + DEFAULT_DEPLOYMENT_ID, deployment_id_from_headers, +}; +use crate::channels::web::server::GatewayState; + +/// Compiled defaults for the browser flags, mirroring +/// `web-src/axinite/src/lib/feature-flags/registry.ts`. Keep the two lists in +/// step when adding a flag. +const FLAG_DEFAULTS: &[(&str, bool)] = &[ + ("route_chat", true), + ("route_memory", true), + ("route_jobs", true), + ("route_routines", true), + ("route_extensions", true), + ("route_skills", true), + ("route_logs", true), + ("panel_logs", true), + ("action_memory_edit", false), + ("action_job_restart", false), + ("action_routine_trigger", false), + ("action_extension_install", false), + ("action_skill_install", false), + ("surface_tee_attestation", false), +]; + +pub fn routes() -> Router> { + Router::new().route("/api/features", get(features_handler)) +} + +/// Response header carrying the gateway build version so browsers can +/// correlate flag availability with the host build without polluting the flat +/// RFC 0009 body shape. +pub const VERSION_HEADER: &str = "x-axinite-version"; + +pub async fn features_handler( + State(state): State>, + headers: HeaderMap, +) -> impl axum::response::IntoResponse { + let deployment_id = + deployment_id_from_headers(&headers).unwrap_or_else(|| DEFAULT_DEPLOYMENT_ID.to_string()); + + ensure_deployment_hydrated(&state, &deployment_id).await; + + let overrides = state + .feature_flags + .read() + .await + .overrides_for(&deployment_id); + let unavailable = unavailable_subsystem_flags(&state).await; + + ( + [(VERSION_HEADER, env!("CARGO_PKG_VERSION"))], + Json(resolve_flags( + |name| std::env::var(name).ok(), + &overrides, + &unavailable, + )), + ) +} + +/// Flags whose backing subsystem is absent from `GatewayState`, per the +/// registry's own `backendContract` metadata (for example `route_jobs` is +/// "hide when jobs runtime is absent"). +/// +/// The subsystem layer only ever *disables*: presence of a subsystem falls +/// through to the compiled default rather than enabling a flag early. +async fn unavailable_subsystem_flags(state: &GatewayState) -> Vec<&'static str> { + let mut unavailable = Vec::new(); + + let scheduler_present = match state.scheduler.as_ref() { + Some(slot) => slot.read().await.is_some(), + None => false, + }; + if state.job_manager.is_none() && !scheduler_present { + unavailable.extend(["route_jobs", "action_job_restart"]); + } + if !state.routine_engine.read().await.is_some() { + unavailable.extend(["route_routines", "action_routine_trigger"]); + } + if state.extension_manager.is_none() { + unavailable.extend(["route_extensions", "action_extension_install"]); + } + if state.skill_registry.is_none() { + unavailable.extend(["route_skills", "action_skill_install"]); + } + if state.log_broadcaster.is_none() { + unavailable.extend(["route_logs", "panel_logs"]); + } + + unavailable +} + +/// Ensure the registry has loaded the given deployment's overrides from the +/// store exactly once. +/// +/// Lazy hydration keeps `GatewayChannel::new()` synchronous (it has no store +/// yet) while still reflecting persisted overrides after a restart. When no +/// store is wired, resolution falls back to environment variables and compiled +/// defaults, so the deployment is left un-hydrated and simply resolves from +/// defaults. +async fn ensure_deployment_hydrated(state: &GatewayState, deployment_id: &str) { + if state.feature_flags.read().await.is_hydrated(deployment_id) { + return; + } + + let Some(store) = state.store.as_ref() else { + return; + }; + + match store.list_deployment_flags(deployment_id).await { + Ok(overrides) => { + state + .feature_flags + .write() + .await + .hydrate(deployment_id.to_string(), overrides); + } + Err(error) => { + tracing::error!( + deployment_id, + %error, + "Failed to load deployment feature-flag overrides" + ); + } + } +} + +/// Resolve every known flag through the precedence chain: environment variable +/// > deployment override > subsystem-availability default > compiled default. +/// +/// `unavailable` lists flags whose backing subsystem is absent; they resolve +/// to `false` unless an environment variable or operator override says +/// otherwise. Only names in [`FLAG_DEFAULTS`] are emitted; unknown override +/// names are ignored, matching RFC 0009's flag-name validation posture. +fn resolve_flags( + env: impl Fn(&str) -> Option, + overrides: &HashMap, + unavailable: &[&str], +) -> BTreeMap { + FLAG_DEFAULTS + .iter() + .map(|(name, default)| { + let variable = format!("FEATURE_FLAG_{}", name.to_ascii_uppercase()); + let value = match env(&variable) { + Some(raw) => raw.eq_ignore_ascii_case("true"), + None => overrides.get(*name).copied().unwrap_or_else(|| { + if unavailable.contains(name) { + false + } else { + *default + } + }), + }; + ((*name).to_string(), value) + }) + .collect() +} + +/// Persist and cache a deployment-scoped override, then return the resolved +/// value (which may still be overridden by an environment variable). +/// +/// Used by the settings handler when intercepting `feature_flag:` writes so the +/// database and the in-memory registry stay in step without a restart. +pub(crate) async fn apply_flag_override( + state: &GatewayState, + deployment_id: &str, + flag_name: &str, + enabled: bool, +) -> Result<(), crate::error::DatabaseError> { + let store = state + .store + .as_ref() + .ok_or_else(|| crate::error::DatabaseError::Query("no store configured".to_string()))?; + + // Ensure the deployment is hydrated first so the write does not create an + // isolated, partially populated cache entry that hides other overrides. + ensure_deployment_hydrated(state, deployment_id).await; + + store + .set_deployment_flag(deployment_id, flag_name, enabled) + .await?; + + state.feature_flags.write().await.set( + deployment_id.to_string(), + flag_name.to_string(), + enabled, + ); + + Ok(()) +} + +#[cfg(test)] +mod tests { + //! Unit tests for feature-flag resolution. + + use super::*; + + fn no_overrides() -> HashMap { + HashMap::new() + } + + #[test] + fn defaults_apply_when_no_environment_or_override_exists() { + let flags = resolve_flags(|_| None, &no_overrides(), &[]); + assert_eq!(flags.get("route_chat"), Some(&true)); + assert_eq!(flags.get("panel_logs"), Some(&true)); + assert_eq!(flags.get("action_memory_edit"), Some(&false)); + assert_eq!(flags.len(), FLAG_DEFAULTS.len()); + } + + #[test] + fn environment_variables_override_defaults() { + let flags = resolve_flags( + |name| match name { + "FEATURE_FLAG_ACTION_MEMORY_EDIT" => Some("TRUE".to_string()), + "FEATURE_FLAG_ROUTE_SKILLS" => Some("false".to_string()), + _ => None, + }, + &no_overrides(), + &[], + ); + assert_eq!(flags.get("action_memory_edit"), Some(&true)); + assert_eq!(flags.get("route_skills"), Some(&false)); + // Untouched flags keep their compiled defaults. + assert_eq!(flags.get("route_chat"), Some(&true)); + } + + #[test] + fn non_true_values_disable_the_flag() { + let flags = resolve_flags( + |name| (name == "FEATURE_FLAG_ROUTE_CHAT").then(|| "1".to_string()), + &no_overrides(), + &[], + ); + assert_eq!(flags.get("route_chat"), Some(&false)); + } + + #[test] + fn deployment_override_beats_compiled_default() { + let mut overrides = HashMap::new(); + overrides.insert("panel_logs".to_string(), false); + overrides.insert("action_job_restart".to_string(), true); + let flags = resolve_flags(|_| None, &overrides, &[]); + assert_eq!(flags.get("panel_logs"), Some(&false)); + assert_eq!(flags.get("action_job_restart"), Some(&true)); + // A flag with no override keeps its default. + assert_eq!(flags.get("route_chat"), Some(&true)); + } + + #[test] + fn environment_variable_beats_deployment_override() { + let mut overrides = HashMap::new(); + overrides.insert("route_chat".to_string(), false); + let flags = resolve_flags( + |name| (name == "FEATURE_FLAG_ROUTE_CHAT").then(|| "true".to_string()), + &overrides, + &[], + ); + // Env var wins over the override. + assert_eq!(flags.get("route_chat"), Some(&true)); + } + + #[test] + fn unknown_override_names_are_ignored() { + let mut overrides = HashMap::new(); + overrides.insert("not_a_real_flag".to_string(), true); + let flags = resolve_flags(|_| None, &overrides, &[]); + assert!(!flags.contains_key("not_a_real_flag")); + assert_eq!(flags.len(), FLAG_DEFAULTS.len()); + } + + #[test] + fn unavailable_subsystem_forces_a_flag_off() { + let flags = resolve_flags(|_| None, &no_overrides(), &["route_routines"]); + assert_eq!(flags.get("route_routines"), Some(&false)); + // Other flags keep their compiled defaults. + assert_eq!(flags.get("route_jobs"), Some(&true)); + } + + #[test] + fn override_beats_subsystem_unavailability() { + let overrides = HashMap::from([("route_routines".to_string(), true)]); + let flags = resolve_flags(|_| None, &overrides, &["route_routines"]); + assert_eq!(flags.get("route_routines"), Some(&true)); + } + + #[test] + fn environment_variable_beats_subsystem_unavailability() { + let flags = resolve_flags( + |name| (name == "FEATURE_FLAG_ROUTE_JOBS").then(|| "true".to_string()), + &no_overrides(), + &["route_jobs"], + ); + assert_eq!(flags.get("route_jobs"), Some(&true)); + } + + #[test] + fn subsystem_layer_never_enables_a_flag() { + // action flags default off; an available subsystem must not flip them. + let flags = resolve_flags(|_| None, &no_overrides(), &[]); + assert_eq!(flags.get("action_job_restart"), Some(&false)); + } + + #[tokio::test] + async fn bare_test_state_reports_all_gated_subsystems_unavailable() { + let state = crate::channels::web::test_helpers::TestGatewayBuilder::new().build(); + let unavailable = unavailable_subsystem_flags(&state).await; + for flag in [ + "route_jobs", + "route_routines", + "route_extensions", + "route_skills", + "route_logs", + "panel_logs", + ] { + assert!(unavailable.contains(&flag), "missing {flag}"); + } + } +} diff --git a/src/channels/web/handlers/mod.rs b/src/channels/web/handlers/mod.rs index 35f004b0d..d51c1b9c7 100644 --- a/src/channels/web/handlers/mod.rs +++ b/src/channels/web/handlers/mod.rs @@ -8,6 +8,8 @@ pub mod chat_history; pub mod chat_threads; pub(crate) mod common; pub mod extensions; +pub mod feature_registry; +pub mod features; pub(crate) mod install_helpers; pub mod job_control; pub mod job_files; @@ -20,3 +22,4 @@ pub mod routines; pub mod settings; pub mod skills; pub mod static_files; +pub mod ui_assets; diff --git a/src/channels/web/handlers/settings.rs b/src/channels/web/handlers/settings.rs index 5045e0375..d55da8168 100644 --- a/src/channels/web/handlers/settings.rs +++ b/src/channels/web/handlers/settings.rs @@ -1,18 +1,35 @@ //! Settings API handlers. +//! +//! Most keys are per-user preferences stored in the `settings` table. Keys with +//! the `feature_flag:` prefix are a deployment-scoped exception (RFC 0009): +//! they require an `X-Deployment-Id` header, persist to +//! `feature_flag_overrides` (never the user-scoped `settings` table), and +//! update the in-memory [`FeatureFlagRegistry`] so `GET /api/features` reflects +//! the change without a restart. Reads and deletes of `feature_flag:` keys are +//! rejected here and directed to `GET /api/features`. +//! +//! [`FeatureFlagRegistry`]: +//! crate::channels::web::handlers::feature_registry::FeatureFlagRegistry use std::sync::Arc; use axum::{ Json, Router, extract::{Path, State}, - http::StatusCode, + http::{HeaderMap, StatusCode}, + response::{IntoResponse, Response}, routing::{delete, get, post, put}, }; +use crate::channels::web::handlers::feature_registry::deployment_id_from_headers; +use crate::channels::web::handlers::features::apply_flag_override; use crate::channels::web::server::GatewayState; use crate::channels::web::types::*; use crate::db::{SettingKey, UserId}; +/// Settings key prefix marking a deployment-scoped feature-flag override. +const FEATURE_FLAG_PREFIX: &str = "feature_flag:"; + pub fn routes() -> Router> { Router::new() .route("/api/settings", get(settings_list_handler)) @@ -54,6 +71,10 @@ pub async fn settings_get_handler( State(state): State>, Path(key): Path, ) -> Result, StatusCode> { + // Feature-flag state is deployment-scoped; read it via GET /api/features. + if key.starts_with(FEATURE_FLAG_PREFIX) { + return Err(StatusCode::BAD_REQUEST); + } let store = state .store .as_ref() @@ -80,12 +101,19 @@ pub async fn settings_get_handler( pub async fn settings_set_handler( State(state): State>, Path(key): Path, + headers: HeaderMap, Json(body): Json, -) -> Result { +) -> Result { + // Deployment-scoped feature-flag overrides (RFC 0009) take a separate + // persistence path and must never touch the user-scoped `settings` table. + if let Some(flag_name) = key.strip_prefix(FEATURE_FLAG_PREFIX) { + return set_feature_flag(&state, flag_name, &headers, &body.value).await; + } + let store = state .store .as_ref() - .ok_or(StatusCode::SERVICE_UNAVAILABLE)?; + .ok_or((StatusCode::SERVICE_UNAVAILABLE, "no store".to_string()))?; store .set_setting( UserId::from(state.user_id.as_str()), @@ -95,16 +123,95 @@ pub async fn settings_set_handler( .await .map_err(|e| { tracing::error!("Failed to set setting '{}': {}", key, e); - StatusCode::INTERNAL_SERVER_ERROR + ( + StatusCode::INTERNAL_SERVER_ERROR, + "failed to set setting".to_string(), + ) })?; - Ok(StatusCode::NO_CONTENT) + Ok(StatusCode::NO_CONTENT.into_response()) +} + +/// Validate, persist, and cache a deployment-scoped feature-flag override. +/// +/// Requires a non-empty `X-Deployment-Id` header, a `[a-z0-9_]+` flag name, and +/// a value that is a JSON boolean or the string `"true"`/`"false"` +/// (case-insensitively). Returns a `SettingResponse`-shaped success body on the +/// happy path. +async fn set_feature_flag( + state: &GatewayState, + flag_name: &str, + headers: &HeaderMap, + value: &serde_json::Value, +) -> Result { + let deployment_id = deployment_id_from_headers(headers).ok_or(( + StatusCode::BAD_REQUEST, + "feature_flag writes require a non-empty X-Deployment-Id header".to_string(), + ))?; + + if !is_valid_flag_name(flag_name) { + return Err(( + StatusCode::BAD_REQUEST, + format!("invalid feature flag name '{flag_name}': expected [a-z0-9_]+"), + )); + } + + let enabled = coerce_flag_value(value).ok_or(( + StatusCode::BAD_REQUEST, + "feature flag value must be a JSON boolean or \"true\"/\"false\"".to_string(), + ))?; + + apply_flag_override(state, &deployment_id, flag_name, enabled) + .await + .map_err(|e| { + tracing::error!( + deployment_id, + flag_name, + "Failed to persist feature flag override: {e}" + ); + ( + StatusCode::INTERNAL_SERVER_ERROR, + "failed to persist feature flag override".to_string(), + ) + })?; + + Ok(Json(SettingResponse { + key: format!("{FEATURE_FLAG_PREFIX}{flag_name}"), + value: serde_json::Value::Bool(enabled), + updated_at: chrono::Utc::now().to_rfc3339(), + }) + .into_response()) +} + +/// Flag names are lowercase ASCII letters, digits, and underscores (RFC 0009). +fn is_valid_flag_name(name: &str) -> bool { + !name.is_empty() + && name + .bytes() + .all(|b| b.is_ascii_lowercase() || b.is_ascii_digit() || b == b'_') +} + +/// Coerce a settings write value into a boolean: accept a JSON boolean, or the +/// strings `"true"`/`"false"` (case-insensitively). Anything else is rejected. +fn coerce_flag_value(value: &serde_json::Value) -> Option { + match value { + serde_json::Value::Bool(b) => Some(*b), + serde_json::Value::String(s) if s.eq_ignore_ascii_case("true") => Some(true), + serde_json::Value::String(s) if s.eq_ignore_ascii_case("false") => Some(false), + _ => None, + } } pub async fn settings_delete_handler( State(state): State>, Path(key): Path, ) -> Result { + // Feature-flag overrides are deployment-scoped; the settings DELETE path + // only manages user-scoped rows. Deletion is out of scope for RFC 0009's + // minimal surface, so reject rather than silently no-op. + if key.starts_with(FEATURE_FLAG_PREFIX) { + return Err(StatusCode::BAD_REQUEST); + } let store = state .store .as_ref() @@ -159,3 +266,6 @@ pub async fn settings_import_handler( Ok(StatusCode::NO_CONTENT) } + +#[cfg(test)] +mod tests; diff --git a/src/channels/web/handlers/settings/tests.rs b/src/channels/web/handlers/settings/tests.rs new file mode 100644 index 000000000..412c07259 --- /dev/null +++ b/src/channels/web/handlers/settings/tests.rs @@ -0,0 +1,225 @@ +//! Handler-level tests for the `feature_flag:` settings interception. + +use axum::body::Body; +use axum::http::Request; +use tower::ServiceExt; + +use super::*; +use crate::channels::web::handlers::features; +use crate::channels::web::test_helpers::TestGatewayBuilder; + +#[test] +fn valid_flag_name_accepts_lowercase_digits_underscore() { + assert!(is_valid_flag_name("panel_logs")); + assert!(is_valid_flag_name("route_chat2")); + assert!(!is_valid_flag_name("")); + assert!(!is_valid_flag_name("Panel_Logs")); + assert!(!is_valid_flag_name("panel-logs")); + assert!(!is_valid_flag_name("panel logs")); +} + +#[test] +fn coerce_flag_value_accepts_bool_and_string_variants() { + use serde_json::json; + assert_eq!(coerce_flag_value(&json!(true)), Some(true)); + assert_eq!(coerce_flag_value(&json!(false)), Some(false)); + assert_eq!(coerce_flag_value(&json!("TRUE")), Some(true)); + assert_eq!(coerce_flag_value(&json!("False")), Some(false)); + assert_eq!(coerce_flag_value(&json!("1")), None); + assert_eq!(coerce_flag_value(&json!(1)), None); + assert_eq!(coerce_flag_value(&json!(null)), None); +} + +fn app(state: Arc) -> Router { + super::routes().merge(features::routes()).with_state(state) +} + +async fn body_string(response: axum::response::Response) -> String { + let bytes = axum::body::to_bytes(response.into_body(), usize::MAX) + .await + .unwrap(); + String::from_utf8_lossy(&bytes).into_owned() +} + +#[tokio::test] +async fn put_feature_flag_without_deployment_header_returns_400() { + let state = TestGatewayBuilder::new().build(); + let response = app(state) + .oneshot( + Request::builder() + .method("PUT") + .uri("/api/settings/feature_flag:route_memory") + .header("content-type", "application/json") + .body(Body::from(r#"{"value":"false"}"#)) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::BAD_REQUEST); +} + +#[tokio::test] +async fn put_feature_flag_with_invalid_name_returns_400() { + let state = TestGatewayBuilder::new().build(); + let response = app(state) + .oneshot( + Request::builder() + .method("PUT") + .uri("/api/settings/feature_flag:Bad-Name") + .header("content-type", "application/json") + .header("x-deployment-id", "production") + .body(Body::from(r#"{"value":true}"#)) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::BAD_REQUEST); +} + +#[tokio::test] +async fn put_feature_flag_with_uncoercible_value_returns_400() { + // Store present so the failure is attributable to value coercion, not a + // missing store. + let backend = new_test_store().await; + let state = TestGatewayBuilder::new().store(backend).build(); + let response = app(state) + .oneshot( + Request::builder() + .method("PUT") + .uri("/api/settings/feature_flag:route_memory") + .header("content-type", "application/json") + .header("x-deployment-id", "production") + .body(Body::from(r#"{"value":"maybe"}"#)) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::BAD_REQUEST); +} + +#[tokio::test] +async fn get_feature_flag_key_via_settings_is_rejected() { + let state = TestGatewayBuilder::new().build(); + let response = app(state) + .oneshot( + Request::builder() + .method("GET") + .uri("/api/settings/feature_flag:route_memory") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::BAD_REQUEST); +} + +#[tokio::test] +async fn features_get_without_header_uses_default_deployment() { + // No store: resolution falls back to compiled defaults. + let state = TestGatewayBuilder::new().build(); + let response = app(state) + .oneshot( + Request::builder() + .uri("/api/features") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(response.status(), StatusCode::OK); + assert!( + response + .headers() + .get(super::super::features::VERSION_HEADER) + .is_some(), + "features response should carry the gateway version header" + ); + let body = body_string(response).await; + let flags: std::collections::BTreeMap = + serde_json::from_str(&body).expect("valid JSON map"); + // Compiled defaults for the "default" deployment. + assert_eq!(flags.get("route_chat"), Some(&true)); + // The bare test gateway wires no log broadcaster, so the + // subsystem-availability layer forces the logs surfaces off. + assert_eq!(flags.get("panel_logs"), Some(&false)); +} + +// --- libSQL-backed persistence proof (requires the libsql backend) --- + +#[cfg(feature = "libsql")] +async fn new_test_store() -> Arc { + use crate::db::Database as _; + let backend = crate::db::libsql::LibSqlBackend::new_memory() + .await + .unwrap(); + backend.run_migrations().await.unwrap(); + Arc::new(backend) +} + +#[cfg(not(feature = "libsql"))] +async fn new_test_store() -> Arc { + // The postgres-only test build has no in-process store; the null + // database satisfies the trait so value-validation tests can still run. + Arc::new(crate::testing::null_db::NullDatabase::new()) +} + +#[cfg(feature = "libsql")] +#[tokio::test] +async fn put_feature_flag_then_get_reflects_override_without_restart() { + // Guard against a leaked environment override from another test. + // SAFETY: single-threaded test; no other thread reads the environment. + unsafe { + std::env::remove_var("FEATURE_FLAG_ROUTE_MEMORY"); + } + + let backend = new_test_store().await; + let state = TestGatewayBuilder::new().store(backend).build(); + + // Override route_memory=false for the "production" deployment + // (route_memory has no subsystem gate, so the compiled default applies + // elsewhere). + let put = app(state.clone()) + .oneshot( + Request::builder() + .method("PUT") + .uri("/api/settings/feature_flag:route_memory") + .header("content-type", "application/json") + .header("x-deployment-id", "production") + .body(Body::from(r#"{"value":"false"}"#)) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(put.status(), StatusCode::OK); + + // The same deployment now reflects the override immediately. + let get = app(state.clone()) + .oneshot( + Request::builder() + .uri("/api/features") + .header("x-deployment-id", "production") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + assert_eq!(get.status(), StatusCode::OK); + let flags: std::collections::BTreeMap = + serde_json::from_str(&body_string(get).await).unwrap(); + assert_eq!(flags.get("route_memory"), Some(&false)); + + // A different deployment is unaffected and keeps the compiled default. + let other = app(state) + .oneshot( + Request::builder() + .uri("/api/features") + .header("x-deployment-id", "staging") + .body(Body::empty()) + .unwrap(), + ) + .await + .unwrap(); + let other_flags: std::collections::BTreeMap = + serde_json::from_str(&body_string(other).await).unwrap(); + assert_eq!(other_flags.get("route_memory"), Some(&true)); +} diff --git a/src/channels/web/handlers/static_files.rs b/src/channels/web/handlers/static_files.rs index b3a578e15..64b622b93 100644 --- a/src/channels/web/handlers/static_files.rs +++ b/src/channels/web/handlers/static_files.rs @@ -19,15 +19,6 @@ use crate::bootstrap::axinite_base_dir; use crate::channels::web::server::GatewayState; use crate::channels::web::types::*; -pub fn public_routes() -> Router> { - Router::new() - .route("/", get(index_handler)) - .route("/style.css", get(css_handler)) - .route("/app.js", get(js_handler)) - .route("/favicon.ico", get(favicon_handler)) - .route("/api/health", get(health_handler)) -} - pub fn protected_routes() -> Router> { Router::new() .route("/api/logs/events", get(logs_events_handler)) @@ -39,46 +30,6 @@ pub fn protected_routes() -> Router> { .route("/projects/{project_id}/{*path}", get(project_file_handler)) } -pub async fn index_handler() -> impl IntoResponse { - ( - [ - (header::CONTENT_TYPE, "text/html; charset=utf-8"), - (header::CACHE_CONTROL, "no-cache"), - ], - include_str!("../static/index.html"), - ) -} - -pub async fn css_handler() -> impl IntoResponse { - ( - [ - (header::CONTENT_TYPE, "text/css"), - (header::CACHE_CONTROL, "no-cache"), - ], - include_str!("../static/style.css"), - ) -} - -pub async fn js_handler() -> impl IntoResponse { - ( - [ - (header::CONTENT_TYPE, "application/javascript"), - (header::CACHE_CONTROL, "no-cache"), - ], - include_str!("../static/app.js"), - ) -} - -pub async fn favicon_handler() -> impl IntoResponse { - ( - [ - (header::CONTENT_TYPE, "image/x-icon"), - (header::CACHE_CONTROL, "public, max-age=86400"), - ], - include_bytes!("../static/favicon.ico").as_slice(), - ) -} - pub async fn health_handler() -> Json { Json(HealthResponse { status: "healthy", diff --git a/src/channels/web/handlers/ui_assets.rs b/src/channels/web/handlers/ui_assets.rs new file mode 100644 index 000000000..5646693a0 --- /dev/null +++ b/src/channels/web/handlers/ui_assets.rs @@ -0,0 +1,298 @@ +//! Embedded browser UI assets and variant selection. +//! +//! Serves the SolidJS single-page app (default) or the legacy handwritten +//! shell, both embedded at compile time. + +use std::sync::Arc; + +use axum::{ + Router, + extract::Path, + http::{StatusCode, header}, + response::IntoResponse, + routing::get, +}; + +use crate::channels::web::handlers::static_files::health_handler; +use crate::channels::web::server::GatewayState; + +/// Which browser implementation the gateway serves at `/`. +/// +/// The SolidJS single-page app (built from `web-src/` into +/// `src/channels/web/static/solid/`) is the default. The legacy handwritten +/// shell remains embedded purely as an operator rollback path during the +/// migration (RFC 0018 Stage 3) and is selected by setting +/// `AXINITE_WEB_UI=legacy` before startup. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum UiVariant { + Solid, + Legacy, +} + +/// Resolve the UI variant from the `AXINITE_WEB_UI` environment variable. +pub fn ui_variant() -> UiVariant { + match std::env::var("AXINITE_WEB_UI") { + Ok(value) if value.eq_ignore_ascii_case("legacy") => UiVariant::Legacy, + _ => UiVariant::Solid, + } +} + +/// Paths handled client-side by the SolidJS router. Each must serve the app +/// shell so deep links and reloads work. +const SOLID_APP_ROUTES: &[&str] = &[ + "/", + "/chat", + "/memory", + "/jobs", + "/routines", + "/extensions", + "/skills", + "/logs", +]; + +/// Embedded Fluent locale bundles for the SolidJS app, keyed by locale code. +const SOLID_LOCALES: &[(&str, &str)] = &[ + ("ar", include_str!("../static/solid/locales/ar/common.ftl")), + ("de", include_str!("../static/solid/locales/de/common.ftl")), + ( + "en-GB", + include_str!("../static/solid/locales/en-GB/common.ftl"), + ), + ("fr", include_str!("../static/solid/locales/fr/common.ftl")), + ("hi", include_str!("../static/solid/locales/hi/common.ftl")), + ("it", include_str!("../static/solid/locales/it/common.ftl")), + ("ja", include_str!("../static/solid/locales/ja/common.ftl")), + ("nl", include_str!("../static/solid/locales/nl/common.ftl")), + ("pl", include_str!("../static/solid/locales/pl/common.ftl")), + ( + "zh-CN", + include_str!("../static/solid/locales/zh-CN/common.ftl"), + ), +]; + +pub fn public_routes() -> Router> { + routes_for(ui_variant()) +} + +/// Build the public asset routes for the given UI variant. +pub fn routes_for(variant: UiVariant) -> Router> { + match variant { + UiVariant::Solid => { + let mut router = Router::new(); + for path in SOLID_APP_ROUTES { + router = router.route(path, get(solid_index_handler)); + } + router + .route("/assets/app.js", get(solid_js_handler)) + .route("/assets/index.css", get(solid_css_handler)) + .route("/assets/axinite32.ico", get(solid_icon_handler)) + .route("/favicon.ico", get(solid_icon_handler)) + .route("/locales/{locale}/common.ftl", get(solid_locale_handler)) + .route("/api/health", get(health_handler)) + } + UiVariant::Legacy => Router::new() + .route("/", get(index_handler)) + .route("/style.css", get(css_handler)) + .route("/app.js", get(js_handler)) + .route("/favicon.ico", get(favicon_handler)) + .route("/api/health", get(health_handler)), + } +} + +pub async fn solid_index_handler() -> impl IntoResponse { + ( + [ + (header::CONTENT_TYPE, "text/html; charset=utf-8"), + (header::CACHE_CONTROL, "no-cache"), + ], + include_str!("../static/solid/index.html"), + ) +} + +pub async fn solid_js_handler() -> impl IntoResponse { + ( + [ + (header::CONTENT_TYPE, "application/javascript"), + (header::CACHE_CONTROL, "no-cache"), + ], + include_str!("../static/solid/assets/app.js"), + ) +} + +pub async fn solid_css_handler() -> impl IntoResponse { + ( + [ + (header::CONTENT_TYPE, "text/css"), + (header::CACHE_CONTROL, "no-cache"), + ], + include_str!("../static/solid/assets/index.css"), + ) +} + +pub async fn solid_icon_handler() -> impl IntoResponse { + ( + [ + (header::CONTENT_TYPE, "image/x-icon"), + (header::CACHE_CONTROL, "public, max-age=86400"), + ], + include_bytes!("../static/solid/assets/axinite32.ico").as_slice(), + ) +} + +pub async fn solid_locale_handler(Path(locale): Path) -> axum::response::Response { + match SOLID_LOCALES + .iter() + .find(|(code, _)| code.eq_ignore_ascii_case(&locale)) + { + Some((_, bundle)) => ( + [ + (header::CONTENT_TYPE, "text/plain; charset=utf-8"), + (header::CACHE_CONTROL, "no-cache"), + ], + *bundle, + ) + .into_response(), + None => (StatusCode::NOT_FOUND, "Unknown locale").into_response(), + } +} + +pub async fn index_handler() -> impl IntoResponse { + ( + [ + (header::CONTENT_TYPE, "text/html; charset=utf-8"), + (header::CACHE_CONTROL, "no-cache"), + ], + include_str!("../static/index.html"), + ) +} + +pub async fn css_handler() -> impl IntoResponse { + ( + [ + (header::CONTENT_TYPE, "text/css"), + (header::CACHE_CONTROL, "no-cache"), + ], + include_str!("../static/style.css"), + ) +} + +pub async fn js_handler() -> impl IntoResponse { + ( + [ + (header::CONTENT_TYPE, "application/javascript"), + (header::CACHE_CONTROL, "no-cache"), + ], + include_str!("../static/app.js"), + ) +} + +pub async fn favicon_handler() -> impl IntoResponse { + ( + [ + (header::CONTENT_TYPE, "image/x-icon"), + (header::CACHE_CONTROL, "public, max-age=86400"), + ], + include_bytes!("../static/favicon.ico").as_slice(), + ) +} + +#[cfg(test)] +mod tests { + //! Unit tests for UI variant selection and embedded asset serving. + + use axum::body::Body; + use axum::http::Request; + use tower::ServiceExt; + + use super::*; + use crate::channels::web::test_helpers::TestGatewayBuilder; + + fn app(variant: UiVariant) -> Router { + routes_for(variant).with_state(TestGatewayBuilder::new().build()) + } + + async fn get_path(variant: UiVariant, path: &str) -> (StatusCode, String, String) { + let response = app(variant) + .oneshot(Request::builder().uri(path).body(Body::empty()).unwrap()) + .await + .unwrap(); + let status = response.status(); + let content_type = response + .headers() + .get(header::CONTENT_TYPE) + .map(|v| v.to_str().unwrap_or_default().to_string()) + .unwrap_or_default(); + let bytes = axum::body::to_bytes(response.into_body(), usize::MAX) + .await + .unwrap(); + ( + status, + content_type, + String::from_utf8_lossy(&bytes).into_owned(), + ) + } + + #[tokio::test] + async fn solid_variant_serves_spa_shell_at_root_and_app_routes() { + for path in [ + "/", + "/chat", + "/memory", + "/jobs", + "/routines", + "/extensions", + "/skills", + ] { + let (status, content_type, body) = get_path(UiVariant::Solid, path).await; + assert_eq!(status, StatusCode::OK, "path {path}"); + assert!(content_type.starts_with("text/html"), "path {path}"); + assert!(body.contains("id=\"app\""), "SPA mount missing for {path}"); + assert!( + body.contains("/assets/app.js"), + "bundle ref missing for {path}" + ); + } + } + + #[tokio::test] + async fn solid_variant_serves_stable_asset_names() { + let (status, content_type, body) = get_path(UiVariant::Solid, "/assets/app.js").await; + assert_eq!(status, StatusCode::OK); + assert_eq!(content_type, "application/javascript"); + assert!(!body.is_empty()); + + let (status, content_type, _) = get_path(UiVariant::Solid, "/assets/index.css").await; + assert_eq!(status, StatusCode::OK); + assert_eq!(content_type, "text/css"); + } + + #[tokio::test] + async fn solid_variant_serves_locale_bundles() { + let (status, content_type, body) = + get_path(UiVariant::Solid, "/locales/en-GB/common.ftl").await; + assert_eq!(status, StatusCode::OK); + assert!(content_type.starts_with("text/plain")); + assert!(body.contains("route-chat-label")); + + let (status, _, _) = get_path(UiVariant::Solid, "/locales/xx/common.ftl").await; + assert_eq!(status, StatusCode::NOT_FOUND); + } + + #[tokio::test] + async fn legacy_variant_still_serves_the_handwritten_shell() { + let (status, content_type, body) = get_path(UiVariant::Legacy, "/").await; + assert_eq!(status, StatusCode::OK); + assert!(content_type.starts_with("text/html")); + assert!(body.contains("app.js")); + + let (status, _, _) = get_path(UiVariant::Legacy, "/chat").await; + assert_eq!(status, StatusCode::NOT_FOUND); + } + + #[test] + fn ui_variant_defaults_to_solid() { + // Note: reads the real process environment; AXINITE_WEB_UI is not + // set in the test environment. + assert_eq!(ui_variant(), UiVariant::Solid); + } +} diff --git a/src/channels/web/mod.rs b/src/channels/web/mod.rs index 1df4a7b5b..767657758 100644 --- a/src/channels/web/mod.rs +++ b/src/channels/web/mod.rs @@ -95,6 +95,9 @@ impl GatewayChannel { cost_guard: None, routine_engine: Arc::new(tokio::sync::RwLock::new(None)), startup_time: std::time::Instant::now(), + feature_flags: Arc::new(tokio::sync::RwLock::new( + handlers::feature_registry::FeatureFlagRegistry::new(), + )), }); Self { @@ -132,6 +135,9 @@ impl GatewayChannel { cost_guard: self.state.cost_guard.clone(), routine_engine: Arc::clone(&self.state.routine_engine), startup_time: self.state.startup_time, + // Preserve the registry Arc so overrides written before wiring a + // subsystem survive state rebuilds. + feature_flags: Arc::clone(&self.state.feature_flags), }; mutate(&mut new_state); self.state = Arc::new(new_state); diff --git a/src/channels/web/server.rs b/src/channels/web/server.rs index 732c46250..7c9eda346 100644 --- a/src/channels/web/server.rs +++ b/src/channels/web/server.rs @@ -22,7 +22,8 @@ use crate::agent::SessionManager; use crate::channels::IncomingMessage; use crate::channels::web::auth::{AuthState, auth_middleware}; use crate::channels::web::handlers::{ - chat, extensions, jobs, memory, oauth, pairing, routines, settings, skills, static_files, + chat, extensions, features, jobs, memory, oauth, pairing, routines, settings, skills, + static_files, ui_assets, }; use crate::channels::web::log_layer::LogBroadcaster; use crate::channels::web::sse::SseManager; @@ -164,6 +165,14 @@ pub struct GatewayState { pub routine_engine: RoutineEngineSlot, /// Server startup time for uptime calculation. pub startup_time: std::time::Instant, + /// Deployment-scoped feature-flag override registry (RFC 0009). + /// + /// Caches operator overrides per deployment; resolution against + /// environment variables and compiled defaults happens in the + /// `features` handler. Lazily hydrated from `store` on first read. + pub feature_flags: Arc< + tokio::sync::RwLock, + >, } /// Bind the TCP listener and resolve the actual bound address. @@ -190,6 +199,7 @@ async fn bind_listener( fn protected_routes(auth_token: String) -> Router> { let auth_state = AuthState { token: auth_token }; chat::routes() + .merge(features::routes()) .merge(memory::routes()) .merge(jobs::routes()) .merge(static_files::protected_routes()) @@ -243,7 +253,7 @@ fn build_cors_layer(addr: SocketAddr) -> Result, auth_token: String, cors: CorsLayer) -> Router { - let public = oauth::public_routes().merge(static_files::public_routes()); + let public = oauth::public_routes().merge(ui_assets::public_routes()); Router::new() .merge(public) .merge(protected_routes(auth_token)) diff --git a/src/channels/web/server/tests/fixtures.rs b/src/channels/web/server/tests/fixtures.rs index 566d3c6b0..bb2d3d102 100644 --- a/src/channels/web/server/tests/fixtures.rs +++ b/src/channels/web/server/tests/fixtures.rs @@ -44,6 +44,9 @@ impl TestGatewayStateFactory { cost_guard: None, routine_engine: Arc::new(tokio::sync::RwLock::new(None)), startup_time: std::time::Instant::now(), + feature_flags: Arc::new(tokio::sync::RwLock::new( + crate::channels::web::handlers::feature_registry::FeatureFlagRegistry::new(), + )), }) } } diff --git a/src/channels/web/static/solid/assets/app.js b/src/channels/web/static/solid/assets/app.js new file mode 100644 index 000000000..7e22ce9fc --- /dev/null +++ b/src/channels/web/static/solid/assets/app.js @@ -0,0 +1,40 @@ +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const r of document.querySelectorAll('link[rel="modulepreload"]'))s(r);new MutationObserver(r=>{for(const i of r)if(i.type==="childList")for(const o of i.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&s(o)}).observe(document,{childList:!0,subtree:!0});function n(r){const i={};return r.integrity&&(i.integrity=r.integrity),r.referrerPolicy&&(i.referrerPolicy=r.referrerPolicy),r.crossOrigin==="use-credentials"?i.credentials="include":r.crossOrigin==="anonymous"?i.credentials="omit":i.credentials="same-origin",i}function s(r){if(r.ep)return;r.ep=!0;const i=n(r);fetch(r.href,i)}})();const J={context:void 0,registry:void 0,effects:void 0,done:!1,getContextId(){return ia(this.context.count)},getNextContextId(){return ia(this.context.count++)}};function ia(e){const t=String(e),n=t.length-1;return J.context.id+(n?String.fromCharCode(96+n):"")+t}function fs(e){J.context=e}const hf=!1,gf=(e,t)=>e===t,gt=Symbol("solid-proxy"),$c=typeof Proxy=="function",Ei=Symbol("solid-track"),rr={equals:gf};let vs=null,Cc=Tc;const ut=1,ws=2,Pc={owned:null,cleanups:null,context:null,owner:null},Zr={};var X=null;let K=null,pf=null,ue=null,Ue=null,Ie=null,Lr=0;function mn(e,t){const n=ue,s=X,r=e.length===0,i=t===void 0?s:t,o=r?Pc:{owned:null,cleanups:null,context:i?i.context:null,owner:i},a=r?e:()=>e(()=>$e(()=>Ht(o)));X=o,ue=null;try{return et(a,!0)}finally{ue=n,X=s}}function A(e,t){t=t?Object.assign({},rr,t):rr;const n={value:e,observers:null,observerSlots:null,comparator:t.equals||void 0},s=r=>(typeof r=="function"&&(K&&K.running&&K.sources.has(n)?r=r(n.tValue):r=r(n.value)),Lc(n,r));return[Oc.bind(n),s]}function Tt(e,t,n){const s=Ms(e,t,!0,ut);Gn(s)}function j(e,t,n){const s=Ms(e,t,!1,ut);Gn(s)}function q(e,t,n){Cc=xf;const s=Ms(e,t,!1,ut),r=wn&&Te(wn);r&&(s.suspense=r),(!n||!n.render)&&(s.user=!0),Ie?Ie.push(s):Gn(s)}function D(e,t,n){n=n?Object.assign({},rr,n):rr;const s=Ms(e,t,!0,0);return s.observers=null,s.observerSlots=null,s.comparator=n.equals||void 0,Gn(s),Oc.bind(s)}function mf(e){return e&&typeof e=="object"&&"then"in e}function hs(e,t,n){let s,r,i;typeof t=="function"?(s=e,r=t,i={}):(s=!0,r=e,i=t||{});let o=null,a=Zr,l=null,c=!1,d=!1,u="initialValue"in i,f=typeof s=="function"&&D(s);const h=new Set,[g,m]=(i.storage||A)(i.initialValue),[y,v]=A(void 0),[b,w]=A(void 0,{equals:!1}),[S,x]=A(u?"ready":"unresolved");J.context&&(l=J.getNextContextId(),i.ssrLoadFrom==="initial"?a=i.initialValue:J.load&&J.has(l)&&(a=J.load(l)));function k(C,I,T,L){return o===C&&(o=null,L!==void 0&&(u=!0),(C===a||I===a)&&i.onHydrated&&queueMicrotask(()=>i.onHydrated(L,{value:I})),a=Zr,K&&C&&c?(K.promises.delete(C),c=!1,et(()=>{K.running=!0,P(I,T)},!1)):P(I,T)),I}function P(C,I){et(()=>{I===void 0&&m(()=>C),x(I!==void 0?"errored":u?"ready":"unresolved"),v(I);for(const T of h.keys())T.decrement();h.clear()},!1)}function E(){const C=wn&&Te(wn),I=g(),T=y();if(T!==void 0&&!o)throw T;return ue&&!ue.user&&C&&Tt(()=>{b(),o&&(C.resolved&&K&&c?K.promises.add(o):h.has(C)||(C.increment(),h.add(C)))}),I}function O(C=!0){if(C!==!1&&d)return;d=!1;const I=f?f():s;if(c=K&&K.running,I==null||I===!1){k(o,$e(g));return}K&&o&&K.promises.delete(o);let T;const L=a!==Zr?a:$e(()=>{try{return r(I,{value:g(),refetching:C})}catch(M){T=M}});if(T!==void 0){k(o,void 0,Xs(T),I);return}else if(!mf(L))return k(o,L,void 0,I),L;return o=L,"v"in L?(L.s===1?k(o,L.v,void 0,I):k(o,void 0,Xs(L.v),I),L):(d=!0,queueMicrotask(()=>d=!1),et(()=>{x(u?"refreshing":"pending"),w()},!1),L.then(M=>k(L,M,void 0,I),M=>k(L,void 0,Xs(M),I)))}Object.defineProperties(E,{state:{get:()=>S()},error:{get:()=>y()},loading:{get(){const C=S();return C==="pending"||C==="refreshing"}},latest:{get(){if(!u)return E();const C=y();if(C&&!o)throw C;return g()}}});let $=X;return f?Tt(()=>($=X,O(!1))):O(!1),[E,{refetch:C=>kc($,()=>O(C)),mutate:m}]}function oo(e){return et(e,!1)}function $e(e){if(ue===null)return e();const t=ue;ue=null;try{return e()}finally{ue=t}}function Ve(e,t,n){const s=Array.isArray(e);let r,i=n&&n.defer;return o=>{let a;if(s){a=Array(e.length);for(let c=0;ct(a,r,o));return r=a,l}}function Mt(e){q(()=>$e(e))}function Z(e){return X===null||(X.cleanups===null?X.cleanups=[e]:X.cleanups.push(e)),e}function vf(e,t){vs||(vs=Symbol("error")),X=Ms(void 0,void 0,!0),X.context={...X.context,[vs]:[t]},K&&K.running&&K.sources.add(X);try{return e()}catch(n){Fs(n)}finally{X=X.owner}}function Ri(){return ue}function Oi(){return X}function kc(e,t){const n=X,s=ue;X=e,ue=null;try{return et(t,!0)}catch(r){Fs(r)}finally{X=n,ue=s}}function Ec(e){if(K&&K.running)return e(),K.done;const t=ue,n=X;return Promise.resolve().then(()=>{ue=t,X=n;let s;return wn&&(s=K||(K={sources:new Set,effects:[],promises:new Set,disposed:new Set,queue:new Set,running:!0}),s.done||(s.done=new Promise(r=>s.resolve=r)),s.running=!0),et(e,!1),ue=X=null,s?s.done:void 0})}const[yf,oa]=A(!1);function bf(){return[yf,Ec]}function _f(e){Ie.push.apply(Ie,e),e.length=0}function He(e,t){const n=Symbol("context");return{id:n,Provider:$f(n),defaultValue:e}}function Te(e){let t;return X&&X.context&&(t=X.context[e.id])!==void 0?t:e.defaultValue}function Rc(e){const t=D(e),n=D(()=>Li(t()));return n.toArray=()=>{const s=n();return Array.isArray(s)?s:s!=null?[s]:[]},n}let wn;function wf(){return wn||(wn=He())}function Oc(){const e=K&&K.running;if(this.sources&&(e?this.tState:this.state))if((e?this.tState:this.state)===ut)Gn(this);else{const t=Ue;Ue=null,et(()=>or(this),!1),Ue=t}if(ue){const t=this.observers?this.observers.length:0;ue.sources?(ue.sources.push(this),ue.sourceSlots.push(t)):(ue.sources=[this],ue.sourceSlots=[t]),this.observers?(this.observers.push(ue),this.observerSlots.push(ue.sources.length-1)):(this.observers=[ue],this.observerSlots=[ue.sources.length-1])}return e&&K.sources.has(this)?this.tValue:this.value}function Lc(e,t,n){let s=K&&K.running&&K.sources.has(e)?e.tValue:e.value;if(!e.comparator||!e.comparator(s,t)){if(K){const r=K.running;(r||!n&&K.sources.has(e))&&(K.sources.add(e),e.tValue=t),r||(e.value=t)}else e.value=t;e.observers&&e.observers.length&&et(()=>{for(let r=0;r1e6)throw Ue=[],new Error},!1)}return t}function Gn(e){if(!e.fn)return;Ht(e);const t=Lr;aa(e,K&&K.running&&K.sources.has(e)?e.tValue:e.value,t),K&&!K.running&&K.sources.has(e)&&queueMicrotask(()=>{et(()=>{K&&(K.running=!0),ue=X=e,aa(e,e.tValue,t),ue=X=null},!1)})}function aa(e,t,n){let s;const r=X,i=ue;ue=X=e;try{s=e.fn(t)}catch(o){return e.pure&&(K&&K.running?(e.tState=ut,e.tOwned&&e.tOwned.forEach(Ht),e.tOwned=void 0):(e.state=ut,e.owned&&e.owned.forEach(Ht),e.owned=null)),e.updatedAt=n+1,Fs(o)}finally{ue=i,X=r}(!e.updatedAt||e.updatedAt<=n)&&(e.updatedAt!=null&&"observers"in e?Lc(e,s,!0):K&&K.running&&e.pure?(K.sources.has(e)||(e.value=s),K.sources.add(e),e.tValue=s):e.value=s,e.updatedAt=n)}function Ms(e,t,n,s=ut,r){const i={fn:e,state:s,updatedAt:null,owned:null,sources:null,sourceSlots:null,cleanups:null,value:t,owner:X,context:X?X.context:null,pure:n};return K&&K.running&&(i.state=0,i.tState=s),X===null||X!==Pc&&(K&&K.running&&X.pure?X.tOwned?X.tOwned.push(i):X.tOwned=[i]:X.owned?X.owned.push(i):X.owned=[i]),i}function ir(e){const t=K&&K.running;if((t?e.tState:e.state)===0)return;if((t?e.tState:e.state)===ws)return or(e);if(e.suspense&&$e(e.suspense.inFallback))return e.suspense.effects.push(e);const n=[e];for(;(e=e.owner)&&(!e.updatedAt||e.updatedAt=0;s--){if(e=n[s],t){let r=e,i=n[s+1];for(;(r=r.owner)&&r!==i;)if(K.disposed.has(r))return}if((t?e.tState:e.state)===ut)Gn(e);else if((t?e.tState:e.state)===ws){const r=Ue;Ue=null,et(()=>or(e,n[0]),!1),Ue=r}}}function et(e,t){if(Ue)return e();let n=!1;t||(Ue=[]),Ie?n=!0:Ie=[],Lr++;try{const s=e();return Sf(n),s}catch(s){n||(Ie=null),Ue=null,Fs(s)}}function Sf(e){if(Ue&&(Tc(Ue),Ue=null),e)return;let t;if(K){if(!K.promises.size&&!K.queue.size){const s=K.sources,r=K.disposed;Ie.push.apply(Ie,K.effects),t=K.resolve;for(const i of Ie)"tState"in i&&(i.state=i.tState),delete i.tState;K=null,et(()=>{for(const i of r)Ht(i);for(const i of s){if(i.value=i.tValue,i.owned)for(let o=0,a=i.owned.length;oCc(n),!1),t&&t()}function Tc(e){for(let t=0;t=0;t--)Ht(e.tOwned[t]);delete e.tOwned}if(K&&K.running&&e.pure)Ac(e,!0);else if(e.owned){for(t=e.owned.length-1;t>=0;t--)Ht(e.owned[t]);e.owned=null}if(e.cleanups){for(t=e.cleanups.length-1;t>=0;t--)e.cleanups[t]();e.cleanups=null}K&&K.running?e.tState=0:e.state=0}function Ac(e,t){if(t||(e.tState=0,K.disposed.add(e)),e.owned)for(let n=0;nr=$e(()=>(X.context={...X.context,[e]:s.value},Rc(()=>s.children))),void 0),r}}const Cf=Symbol("fallback");function ca(e){for(let t=0;t1?[]:null;return Z(()=>ca(i)),()=>{let l=e()||[],c=l.length,d,u;return l[Ei],$e(()=>{let h,g,m,y,v,b,w,S,x;if(c===0)o!==0&&(ca(i),i=[],s=[],r=[],o=0,a&&(a=[])),n.fallback&&(s=[Cf],r[0]=mn(k=>(i[0]=k,n.fallback())),o=1);else if(o===0){for(r=new Array(c),u=0;u=b&&S>=b&&s[w]===l[S];w--,S--)m[S]=r[w],y[S]=i[w],a&&(v[S]=a[w]);for(h=new Map,g=new Array(S+1),u=S;u>=b;u--)x=l[u],d=h.get(x),g[u]=d===void 0?-1:d,h.set(x,u);for(d=b;d<=w;d++)x=s[d],u=h.get(x),u!==void 0&&u!==-1?(m[u]=r[d],y[u]=i[d],a&&(v[u]=a[d]),u=g[u],h.set(x,u)):i[d]();for(u=b;ue(t||{}))}function Us(){return!0}const Ti={get(e,t,n){return t===gt?n:e.get(t)},has(e,t){return t===gt?!0:e.has(t)},set:Us,deleteProperty:Us,getOwnPropertyDescriptor(e,t){return{configurable:!0,enumerable:!0,get(){return e.get(t)},set:Us,deleteProperty:Us}},ownKeys(e){return e.keys()}};function ei(e){return(e=typeof e=="function"?e():e)?e:{}}function kf(){for(let e=0,t=this.length;e=0;a--){const l=ei(e[a])[o];if(l!==void 0)return l}},has(o){for(let a=e.length-1;a>=0;a--)if(o in ei(e[a]))return!0;return!1},keys(){const o=[];for(let a=0;a=0;o--){const a=e[o];if(!a)continue;const l=Object.getOwnPropertyNames(a);for(let c=l.length-1;c>=0;c--){const d=l[c];if(d==="__proto__"||d==="constructor")continue;const u=Object.getOwnPropertyDescriptor(a,d);if(!s[d])s[d]=u.get?{enumerable:!0,configurable:!0,get:kf.bind(n[d]=[u.get.bind(a)])}:u.value!==void 0?u:void 0;else{const f=n[d];f&&(u.get?f.push(u.get.bind(a)):u.value!==void 0&&f.push(()=>u.value))}}}const r={},i=Object.keys(s);for(let o=i.length-1;o>=0;o--){const a=i[o],l=s[a];l&&l.get?Object.defineProperty(r,a,l):r[a]=l?l.value:void 0}return r}function ae(e,...t){const n=t.length;if($c&> in e){const r=n>1?t.flat():t[0],i=t.map(o=>new Proxy({get(a){return o.includes(a)?e[a]:void 0},has(a){return o.includes(a)&&a in e},keys(){return o.filter(a=>a in e)}},Ti));return i.push(new Proxy({get(o){return r.includes(o)?void 0:e[o]},has(o){return r.includes(o)?!1:o in e},keys(){return Object.keys(e).filter(o=>!r.includes(o))}},Ti)),i}const s=[];for(let r=0;r<=n;r++)s[r]={};for(const r of Object.getOwnPropertyNames(e)){let i=n;for(let l=0;l`Stale read from <${e}>.`;function oe(e){const t="fallback"in e&&{fallback:()=>e.fallback};return D(Pf(()=>e.each,e.children,t||void 0))}function N(e){const t=e.keyed,n=D(()=>e.when,void 0,void 0),s=t?n:D(n,void 0,{equals:(r,i)=>!r==!i});return D(()=>{const r=s();if(r){const i=e.children;return typeof i=="function"&&i.length>0?$e(()=>i(t?r:()=>{if(!$e(s))throw Mc("Show");return n()})):i}return e.fallback},void 0,void 0)}function ao(e){const t=Rc(()=>e.children),n=D(()=>{const s=t(),r=Array.isArray(s)?s:[s];let i=()=>{};for(let o=0;oc()?void 0:l.when,void 0,void 0),u=l.keyed?d:D(d,void 0,{equals:(f,h)=>!f==!h});i=()=>c()||(u()?[a,d,l]:void 0)}return i});return D(()=>{const s=n()();if(!s)return e.fallback;const[r,i,o]=s,a=o.children;return typeof a=="function"&&a.length>0?$e(()=>a(o.keyed?i():()=>{if($e(n)()?.[0]!==r)throw Mc("Match");return i()})):a},void 0,void 0)}function at(e){return e}let Vs;function Rf(e){let t;J.context&&J.load&&(t=J.load(J.getContextId()));const[n,s]=A(t,void 0);return Vs||(Vs=new Set),Vs.add(s),Z(()=>Vs.delete(s)),D(()=>{let r;if(r=n()){const i=e.fallback;return typeof i=="function"&&i.length?$e(()=>i(r,()=>s())):i}return vf(()=>e.children,s)},void 0,void 0)}const Of=He();function lo(e){let t=0,n,s,r,i,o;const[a,l]=A(!1),c=wf(),d={increment:()=>{++t===1&&l(!0)},decrement:()=>{--t===0&&l(!1)},inFallback:a,effects:[],resolved:!1},u=Oi();if(J.context&&J.load){const g=J.getContextId();let m=J.load(g);if(m&&(typeof m!="object"||m.s!==1?r=m:J.gather(g)),r&&r!=="$$f"){const[y,v]=A(void 0,{equals:!1});i=y,r.then(()=>{if(J.done)return v();J.gather(g),fs(s),v(),fs()},b=>{o=b,v()})}}const f=Te(Of);f&&(n=f.register(d.inFallback));let h;return Z(()=>h&&h()),_(c.Provider,{value:d,get children(){return D(()=>{if(o)throw o;if(s=J.context,i)return i(),i=void 0;s&&r==="$$f"&&fs();const g=D(()=>e.children);return D(m=>{const y=d.inFallback(),{showContent:v=!0,showFallback:b=!0}=n?n():{};if((!y||r&&r!=="$$f")&&v)return d.resolved=!0,h&&h(),h=s=r=void 0,_f(d.effects),g();if(b)return h?m:mn(w=>(h=w,s&&(fs({id:s.id+"F",count:0}),s=void 0),e.fallback),u)})})}})}const Lf=["allowfullscreen","async","alpha","autofocus","autoplay","checked","controls","default","disabled","formnovalidate","hidden","indeterminate","inert","ismap","loop","multiple","muted","nomodule","novalidate","open","playsinline","readonly","required","reversed","seamless","selected","adauctionheaders","browsingtopics","credentialless","defaultchecked","defaultmuted","defaultselected","defer","disablepictureinpicture","disableremoteplayback","preservespitch","shadowrootclonable","shadowrootcustomelementregistry","shadowrootdelegatesfocus","shadowrootserializable","sharedstoragewritable"],Tf=new Set(["className","value","readOnly","noValidate","formNoValidate","isMap","noModule","playsInline","adAuctionHeaders","allowFullscreen","browsingTopics","defaultChecked","defaultMuted","defaultSelected","disablePictureInPicture","disableRemotePlayback","preservesPitch","shadowRootClonable","shadowRootCustomElementRegistry","shadowRootDelegatesFocus","shadowRootSerializable","sharedStorageWritable",...Lf]),If=new Set(["innerHTML","textContent","innerText","children"]),Af=Object.assign(Object.create(null),{className:"class",htmlFor:"for"}),Mf=Object.assign(Object.create(null),{class:"className",novalidate:{$:"noValidate",FORM:1},formnovalidate:{$:"formNoValidate",BUTTON:1,INPUT:1},ismap:{$:"isMap",IMG:1},nomodule:{$:"noModule",SCRIPT:1},playsinline:{$:"playsInline",VIDEO:1},readonly:{$:"readOnly",INPUT:1,TEXTAREA:1},adauctionheaders:{$:"adAuctionHeaders",IFRAME:1},allowfullscreen:{$:"allowFullscreen",IFRAME:1},browsingtopics:{$:"browsingTopics",IMG:1},defaultchecked:{$:"defaultChecked",INPUT:1},defaultmuted:{$:"defaultMuted",AUDIO:1,VIDEO:1},defaultselected:{$:"defaultSelected",OPTION:1},disablepictureinpicture:{$:"disablePictureInPicture",VIDEO:1},disableremoteplayback:{$:"disableRemotePlayback",AUDIO:1,VIDEO:1},preservespitch:{$:"preservesPitch",AUDIO:1,VIDEO:1},shadowrootclonable:{$:"shadowRootClonable",TEMPLATE:1},shadowrootdelegatesfocus:{$:"shadowRootDelegatesFocus",TEMPLATE:1},shadowrootserializable:{$:"shadowRootSerializable",TEMPLATE:1},sharedstoragewritable:{$:"sharedStorageWritable",IFRAME:1,IMG:1}});function Ff(e,t){const n=Mf[e];return typeof n=="object"?n[t]?n.$:void 0:n}const Df=new Set(["beforeinput","click","dblclick","contextmenu","focusin","focusout","input","keydown","keyup","mousedown","mousemove","mouseout","mouseover","mouseup","pointerdown","pointermove","pointerout","pointerover","pointerup","touchend","touchmove","touchstart"]),Nf=new Set(["altGlyph","altGlyphDef","altGlyphItem","animate","animateColor","animateMotion","animateTransform","circle","clipPath","color-profile","cursor","defs","desc","ellipse","feBlend","feColorMatrix","feComponentTransfer","feComposite","feConvolveMatrix","feDiffuseLighting","feDisplacementMap","feDistantLight","feDropShadow","feFlood","feFuncA","feFuncB","feFuncG","feFuncR","feGaussianBlur","feImage","feMerge","feMergeNode","feMorphology","feOffset","fePointLight","feSpecularLighting","feSpotLight","feTile","feTurbulence","filter","font","font-face","font-face-format","font-face-name","font-face-src","font-face-uri","foreignObject","g","glyph","glyphRef","hkern","image","line","linearGradient","marker","mask","metadata","missing-glyph","mpath","path","pattern","polygon","polyline","radialGradient","rect","set","stop","svg","switch","symbol","text","textPath","tref","tspan","use","view","vkern"]),jf={xlink:"http://www.w3.org/1999/xlink",xml:"http://www.w3.org/XML/1998/namespace"},ee=e=>D(()=>e());function Kf(e,t,n){let s=n.length,r=t.length,i=s,o=0,a=0,l=t[r-1].nextSibling,c=null;for(;od-a){const g=t[o];for(;a{r=i,t===document?e():p(t,e(),t.firstChild?null:void 0,n)},s.owner),()=>{r(),t.textContent=""}}function R(e,t,n,s){let r;const i=()=>{const a=document.createElement("template");return a.innerHTML=e,a.content.firstChild},o=()=>(r||(r=i())).cloneNode(!0);return o.cloneNode=o,o}function Ae(e,t=window.document){const n=t[ua]||(t[ua]=new Set);for(let s=0,r=e.length;sr.call(e,n[1],i))}else e.addEventListener(t,n,typeof n!="function"&&n)}function qf(e,t,n={}){const s=Object.keys(t||{}),r=Object.keys(n);let i,o;for(i=0,o=r.length;ir.children=Ss(e,t.children,r.children)),j(()=>typeof t.ref=="function"&&Yn(t.ref,e)),j(()=>zf(e,t,n,!0,r,!0)),r}function Yn(e,t,n){return $e(()=>e(t,n))}function p(e,t,n,s){if(n!==void 0&&!s&&(s=[]),typeof t!="function")return Ss(e,t,s,n);j(r=>Ss(e,t(),r,n),s)}function zf(e,t,n,s,r={},i=!1){t||(t={});for(const o in r)if(!(o in t)){if(o==="children")continue;r[o]=fa(e,o,null,r[o],n,i,t)}for(const o in t){if(o==="children")continue;const a=t[o];r[o]=fa(e,o,a,r[o],n,i,t)}}function Wf(e){let t,n;return!Pn()||!(t=J.registry.get(n=Jf()))?e():(J.completed&&J.completed.add(t),J.registry.delete(n),t)}function Pn(e){return!!J.context&&!J.done&&(!e||e.isConnected)}function Qf(e){return e.toLowerCase().replace(/-([a-z])/g,(t,n)=>n.toUpperCase())}function da(e,t,n){const s=t.trim().split(/\s+/);for(let r=0,i=s.length;r-1&&jf[t.split(":")[0]];f?Uf(e,f,t,n):Q(e,Af[t]||t,n)}return n}function Gf(e){if(J.registry&&J.events&&J.events.find(([l,c])=>c===e))return;let t=e.target;const n=`$$${e.type}`,s=e.target,r=e.currentTarget,i=l=>Object.defineProperty(e,"target",{configurable:!0,value:l}),o=()=>{const l=t[n];if(l&&!t.disabled){const c=t[`${n}Data`];if(c!==void 0?l.call(t,c,e):l.call(t,e),e.cancelBubble)return}return t.host&&typeof t.host!="string"&&!t.host._$host&&t.contains(e.target)&&i(t.host),!0},a=()=>{for(;o()&&(t=t._$host||t.parentNode||t.host););};if(Object.defineProperty(e,"currentTarget",{configurable:!0,get(){return t||document}}),J.registry&&!J.done&&(J.done=_$HY.done=!0),e.composedPath){const l=e.composedPath();i(l[0]);for(let c=0;c{let l=t();for(;typeof l=="function";)l=l();n=Ss(e,l,n,s)}),()=>n;if(Array.isArray(t)){const l=[],c=n&&Array.isArray(n);if(Ai(l,t,n,r))return j(()=>n=Ss(e,l,n,s,!0)),()=>n;if(i){if(!l.length)return n;if(s===void 0)return n=[...e.childNodes];let d=l[0];if(d.parentNode!==e)return n;const u=[d];for(;(d=d.nextSibling)!==s;)u.push(d);return n=u}if(l.length===0){if(n=In(e,n,s),a)return n}else c?n.length===0?ha(e,l,s):Kf(e,n,l):(n&&In(e),ha(e,l));n=l}else if(t.nodeType){if(i&&t.parentNode)return n=a?[t]:t;if(Array.isArray(n)){if(a)return n=In(e,n,s,t);In(e,n,null,t)}else n==null||n===""||!e.firstChild?e.appendChild(t):e.replaceChild(t,e.firstChild);n=t}}return n}function Ai(e,t,n,s){let r=!1;for(let i=0,o=t.length;i=0;o--){const a=t[o];if(r!==a){const l=a.parentNode===e;!i&&!o?l?e.replaceChild(r,a):e.insertBefore(r,n):l&&a.remove()}else i=!0}}else e.insertBefore(r,n);return[r]}function Jf(){return J.getNextContextId()}const Yf="http://www.w3.org/2000/svg";function Dc(e,t=!1,n=void 0){return t?document.createElementNS(Yf,e):document.createElement(e,{is:n})}function Nc(e){const{useShadow:t}=e,n=document.createTextNode(""),s=()=>e.mount||document.body,r=Oi();let i,o=!!J.context;return q(()=>{o&&(Oi().user=o=!1),i||(i=kc(r,()=>D(()=>e.children)));const a=s();if(a instanceof HTMLHeadElement){const[l,c]=A(!1),d=()=>c(!0);mn(u=>p(a,()=>l()?u():i(),null)),Z(d)}else{const l=Dc(e.isSVG?"g":"div",e.isSVG),c=t&&l.attachShadow?l.attachShadow({mode:"open"}):l;Object.defineProperty(l,"_$host",{get(){return n.parentNode},configurable:!0}),p(c,i),a.appendChild(l),e.ref&&e.ref(l),Z(()=>a.removeChild(l))}},void 0,{render:!o}),n}function Xf(e,t){const n=D(e);return D(()=>{const s=n();switch(typeof s){case"function":return $e(()=>s(t));case"string":const r=Nf.has(s),i=J.context?Wf():Dc(s,r,$e(()=>t.is));return Ii(i,t,r),i}})}function ht(e){const[,t]=ae(e,["component"]);return Xf(()=>e.component,t)}var Xn=class{constructor(){this.listeners=new Set,this.subscribe=this.subscribe.bind(this)}subscribe(e){return this.listeners.add(e),this.onSubscribe(),()=>{this.listeners.delete(e),this.onUnsubscribe()}}hasListeners(){return this.listeners.size>0}onSubscribe(){}onUnsubscribe(){}},Zf=class extends Xn{#e;#t;#n;constructor(){super(),this.#n=e=>{if(typeof window<"u"&&window.addEventListener){const t=()=>e();return window.addEventListener("visibilitychange",t,!1),()=>{window.removeEventListener("visibilitychange",t)}}}}onSubscribe(){this.#t||this.setEventListener(this.#n)}onUnsubscribe(){this.hasListeners()||(this.#t?.(),this.#t=void 0)}setEventListener(e){this.#n=e,this.#t?.(),this.#t=e(t=>{typeof t=="boolean"?this.setFocused(t):this.onFocus()})}setFocused(e){this.#e!==e&&(this.#e=e,this.onFocus())}onFocus(){const e=this.isFocused();this.listeners.forEach(t=>{t(e)})}isFocused(){return typeof this.#e=="boolean"?this.#e:globalThis.document?.visibilityState!=="hidden"}},co=new Zf,eh={setTimeout:(e,t)=>setTimeout(e,t),clearTimeout:e=>clearTimeout(e),setInterval:(e,t)=>setInterval(e,t),clearInterval:e=>clearInterval(e)},th=class{#e=eh;#t=!1;setTimeoutProvider(e){this.#e=e}setTimeout(e,t){return this.#e.setTimeout(e,t)}clearTimeout(e){this.#e.clearTimeout(e)}setInterval(e,t){return this.#e.setInterval(e,t)}clearInterval(e){this.#e.clearInterval(e)}},un=new th;function nh(e){setTimeout(e,0)}var sh=typeof window>"u"||"Deno"in globalThis;function Ke(){}function rh(e,t){return typeof e=="function"?e(t):e}function Mi(e){return typeof e=="number"&&e>=0&&e!==1/0}function jc(e,t){return Math.max(e+(t||0)-Date.now(),0)}function Ut(e,t){return typeof e=="function"?e(t):e}function lt(e,t){return typeof e=="function"?e(t):e}function ga(e,t){const{type:n="all",exact:s,fetchStatus:r,predicate:i,queryKey:o,stale:a}=e;if(o){if(s){if(t.queryHash!==uo(o,t.options))return!1}else if(!xs(t.queryKey,o))return!1}if(n!=="all"){const l=t.isActive();if(n==="active"&&!l||n==="inactive"&&l)return!1}return!(typeof a=="boolean"&&t.isStale()!==a||r&&r!==t.state.fetchStatus||i&&!i(t))}function pa(e,t){const{exact:n,status:s,predicate:r,mutationKey:i}=e;if(i){if(!t.options.mutationKey)return!1;if(n){if(Sn(t.options.mutationKey)!==Sn(i))return!1}else if(!xs(t.options.mutationKey,i))return!1}return!(s&&t.state.status!==s||r&&!r(t))}function uo(e,t){return(t?.queryKeyHashFn||Sn)(e)}function Sn(e){return JSON.stringify(e,(t,n)=>Fi(n)?Object.keys(n).sort().reduce((s,r)=>(s[r]=n[r],s),{}):n)}function xs(e,t){return e===t?!0:typeof e!=typeof t?!1:e&&t&&typeof e=="object"&&typeof t=="object"?Object.keys(t).every(n=>xs(e[n],t[n])):!1}var ih=Object.prototype.hasOwnProperty;function Kc(e,t,n=0){if(e===t)return e;if(n>500)return t;const s=ma(e)&&ma(t);if(!s&&!(Fi(e)&&Fi(t)))return t;const i=(s?e:Object.keys(e)).length,o=s?t:Object.keys(t),a=o.length,l=s?new Array(a):{};let c=0;for(let d=0;d{un.setTimeout(t,e)})}function Di(e,t,n){return typeof n.structuralSharing=="function"?n.structuralSharing(e,t):n.structuralSharing!==!1?Kc(e,t):t}function dn(e){return e}function ah(e,t,n=0){const s=[...e,t];return n&&s.length>n?s.slice(1):s}function lh(e,t,n=0){const s=[t,...e];return n&&s.length>n?s.slice(0,-1):s}var fo=Symbol();function Bc(e,t){return!e.queryFn&&t?.initialPromise?()=>t.initialPromise:!e.queryFn||e.queryFn===fo?()=>Promise.reject(new Error(`Missing queryFn: '${e.queryHash}'`)):e.queryFn}function Uc(e,t){return typeof e=="function"?e(...t):!!e}function ch(e,t,n){let s=!1,r;return Object.defineProperty(e,"signal",{enumerable:!0,get:()=>(r??=t(),s||(s=!0,r.aborted?n():r.addEventListener("abort",n,{once:!0})),r)}),e}var lr=(()=>{let e=()=>sh;return{isServer(){return e()},setIsServer(t){e=t}}})();function Ni(){let e,t;const n=new Promise((r,i)=>{e=r,t=i});n.status="pending",n.catch(()=>{});function s(r){Object.assign(n,r),delete n.resolve,delete n.reject}return n.resolve=r=>{s({status:"fulfilled",value:r}),e(r)},n.reject=r=>{s({status:"rejected",reason:r}),t(r)},n}function uh(e){let t;if(e.then(n=>(t=n,n),Ke)?.catch(Ke),t!==void 0)return{data:t}}function dh(e){return e}function fh(e,t,n){if(typeof t!="object"||t===null)return;const s=e.getMutationCache(),r=e.getQueryCache(),i=e.getDefaultOptions().hydrate?.deserializeData??dh,o=t.mutations||[],a=t.queries||[];o.forEach(({state:l,...c})=>{s.build(e,{...e.getDefaultOptions().hydrate?.mutations,...n?.defaultOptions?.mutations,...c},l)}),a.forEach(({queryKey:l,state:c,queryHash:d,meta:u,promise:f,dehydratedAt:h})=>{const g=f?uh(f):void 0,m=c.data===void 0?g?.data:c.data,y=m===void 0?m:i(m);let v=r.get(d);const b=v?.state.status==="pending",w=v?.state.fetchStatus==="fetching";if(v){const S=g&&h!==void 0&&h>v.state.dataUpdatedAt;if(c.dataUpdatedAt>v.state.dataUpdatedAt||S){const{fetchStatus:x,...k}=c;v.setState({...k,data:y})}}else v=r.build(e,{...e.getDefaultOptions().hydrate?.queries,...n?.defaultOptions?.queries,queryKey:l,queryHash:d,meta:u},{...c,data:y,fetchStatus:"idle",status:y!==void 0?"success":c.status});f&&!b&&!w&&(h===void 0||h>v.state.dataUpdatedAt)&&v.fetch(void 0,{initialPromise:Promise.resolve(f).then(i)}).catch(Ke)})}var hh=nh;function gh(){let e=[],t=0,n=a=>{a()},s=a=>{a()},r=hh;const i=a=>{t?e.push(a):r(()=>{n(a)})},o=()=>{const a=e;e=[],a.length&&r(()=>{s(()=>{a.forEach(l=>{n(l)})})})};return{batch:a=>{let l;t++;try{l=a()}finally{t--,t||o()}return l},batchCalls:a=>(...l)=>{i(()=>{a(...l)})},schedule:i,setNotifyFunction:a=>{n=a},setBatchNotifyFunction:a=>{s=a},setScheduler:a=>{r=a}}}var Fe=gh(),ph=class extends Xn{#e=!0;#t;#n;constructor(){super(),this.#n=e=>{if(typeof window<"u"&&window.addEventListener){const t=()=>e(!0),n=()=>e(!1);return window.addEventListener("online",t,!1),window.addEventListener("offline",n,!1),()=>{window.removeEventListener("online",t),window.removeEventListener("offline",n)}}}}onSubscribe(){this.#t||this.setEventListener(this.#n)}onUnsubscribe(){this.hasListeners()||(this.#t?.(),this.#t=void 0)}setEventListener(e){this.#n=e,this.#t?.(),this.#t=e(this.setOnline.bind(this))}setOnline(e){this.#e!==e&&(this.#e=e,this.listeners.forEach(n=>{n(e)}))}isOnline(){return this.#e}},cr=new ph;function mh(e){return Math.min(1e3*2**e,3e4)}function Vc(e){return(e??"online")==="online"?cr.isOnline():!0}var ji=class extends Error{constructor(e){super("CancelledError"),this.revert=e?.revert,this.silent=e?.silent}};function qc(e){let t=!1,n=0,s;const r=Ni(),i=()=>r.status!=="pending",o=m=>{if(!i()){const y=new ji(m);f(y),e.onCancel?.(y)}},a=()=>{t=!0},l=()=>{t=!1},c=()=>co.isFocused()&&(e.networkMode==="always"||cr.isOnline())&&e.canRun(),d=()=>Vc(e.networkMode)&&e.canRun(),u=m=>{i()||(s?.(),r.resolve(m))},f=m=>{i()||(s?.(),r.reject(m))},h=()=>new Promise(m=>{s=y=>{(i()||c())&&m(y)},e.onPause?.()}).then(()=>{s=void 0,i()||e.onContinue?.()}),g=()=>{if(i())return;let m;const y=n===0?e.initialPromise:void 0;try{m=y??e.fn()}catch(v){m=Promise.reject(v)}Promise.resolve(m).then(u).catch(v=>{if(i())return;const b=e.retry??(lr.isServer()?0:3),w=e.retryDelay??mh,S=typeof w=="function"?w(n,v):w,x=b===!0||typeof b=="number"&&nc()?void 0:h()).then(()=>{t?f(v):g()})})};return{promise:r,status:()=>r.status,cancel:o,continue:()=>(s?.(),r),cancelRetry:a,continueRetry:l,canStart:d,start:()=>(d()?g():h().then(g),r)}}var Hc=class{#e;destroy(){this.clearGcTimeout()}scheduleGc(){this.clearGcTimeout(),Mi(this.gcTime)&&(this.#e=un.setTimeout(()=>{this.optionalRemove()},this.gcTime))}updateGcTime(e){this.gcTime=Math.max(this.gcTime||0,e??(lr.isServer()?1/0:300*1e3))}clearGcTimeout(){this.#e&&(un.clearTimeout(this.#e),this.#e=void 0)}},vh=class extends Hc{#e;#t;#n;#r;#s;#i;#o;constructor(e){super(),this.#o=!1,this.#i=e.defaultOptions,this.setOptions(e.options),this.observers=[],this.#r=e.client,this.#n=this.#r.getQueryCache(),this.queryKey=e.queryKey,this.queryHash=e.queryHash,this.#e=ba(this.options),this.state=e.state??this.#e,this.scheduleGc()}get meta(){return this.options.meta}get promise(){return this.#s?.promise}setOptions(e){if(this.options={...this.#i,...e},this.updateGcTime(this.options.gcTime),this.state&&this.state.data===void 0){const t=ba(this.options);t.data!==void 0&&(this.setState(ya(t.data,t.dataUpdatedAt)),this.#e=t)}}optionalRemove(){!this.observers.length&&this.state.fetchStatus==="idle"&&this.#n.remove(this)}setData(e,t){const n=Di(this.state.data,e,this.options);return this.#l({data:n,type:"success",dataUpdatedAt:t?.updatedAt,manual:t?.manual}),n}setState(e,t){this.#l({type:"setState",state:e,setStateOptions:t})}cancel(e){const t=this.#s?.promise;return this.#s?.cancel(e),t?t.then(Ke).catch(Ke):Promise.resolve()}destroy(){super.destroy(),this.cancel({silent:!0})}get resetState(){return this.#e}reset(){this.destroy(),this.setState(this.resetState)}isActive(){return this.observers.some(e=>lt(e.options.enabled,this)!==!1)}isDisabled(){return this.getObserversCount()>0?!this.isActive():this.options.queryFn===fo||!this.isFetched()}isFetched(){return this.state.dataUpdateCount+this.state.errorUpdateCount>0}isStatic(){return this.getObserversCount()>0?this.observers.some(e=>Ut(e.options.staleTime,this)==="static"):!1}isStale(){return this.getObserversCount()>0?this.observers.some(e=>e.getCurrentResult().isStale):this.state.data===void 0||this.state.isInvalidated}isStaleByTime(e=0){return this.state.data===void 0?!0:e==="static"?!1:this.state.isInvalidated?!0:!jc(this.state.dataUpdatedAt,e)}onFocus(){this.observers.find(t=>t.shouldFetchOnWindowFocus())?.refetch({cancelRefetch:!1}),this.#s?.continue()}onOnline(){this.observers.find(t=>t.shouldFetchOnReconnect())?.refetch({cancelRefetch:!1}),this.#s?.continue()}addObserver(e){this.observers.includes(e)||(this.observers.push(e),this.clearGcTimeout(),this.#n.notify({type:"observerAdded",query:this,observer:e}))}removeObserver(e){this.observers.includes(e)&&(this.observers=this.observers.filter(t=>t!==e),this.observers.length||(this.#s&&(this.#o||this.#a()?this.#s.cancel({revert:!0}):this.#s.cancelRetry()),this.scheduleGc()),this.#n.notify({type:"observerRemoved",query:this,observer:e}))}getObserversCount(){return this.observers.length}#a(){return this.state.fetchStatus==="paused"&&this.state.status==="pending"}invalidate(){this.state.isInvalidated||this.#l({type:"invalidate"})}async fetch(e,t){if(this.state.fetchStatus!=="idle"&&this.#s?.status()!=="rejected"){if(this.state.data!==void 0&&t?.cancelRefetch)this.cancel({silent:!0});else if(this.#s)return this.#s.continueRetry(),this.#s.promise}if(e&&this.setOptions(e),!this.options.queryFn){const a=this.observers.find(l=>l.options.queryFn);a&&this.setOptions(a.options)}const n=new AbortController,s=a=>{Object.defineProperty(a,"signal",{enumerable:!0,get:()=>(this.#o=!0,n.signal)})},r=()=>{const a=Bc(this.options,t),c=(()=>{const d={client:this.#r,queryKey:this.queryKey,meta:this.meta};return s(d),d})();return this.#o=!1,this.options.persister?this.options.persister(a,c,this):a(c)},o=(()=>{const a={fetchOptions:t,options:this.options,queryKey:this.queryKey,client:this.#r,state:this.state,fetchFn:r};return s(a),a})();this.options.behavior?.onFetch(o,this),this.#t=this.state,(this.state.fetchStatus==="idle"||this.state.fetchMeta!==o.fetchOptions?.meta)&&this.#l({type:"fetch",meta:o.fetchOptions?.meta}),this.#s=qc({initialPromise:t?.initialPromise,fn:o.fetchFn,onCancel:a=>{a instanceof ji&&a.revert&&this.setState({...this.#t,fetchStatus:"idle"}),n.abort()},onFail:(a,l)=>{this.#l({type:"failed",failureCount:a,error:l})},onPause:()=>{this.#l({type:"pause"})},onContinue:()=>{this.#l({type:"continue"})},retry:o.options.retry,retryDelay:o.options.retryDelay,networkMode:o.options.networkMode,canRun:()=>!0});try{const a=await this.#s.start();if(a===void 0)throw new Error(`${this.queryHash} data is undefined`);return this.setData(a),this.#n.config.onSuccess?.(a,this),this.#n.config.onSettled?.(a,this.state.error,this),a}catch(a){if(a instanceof ji){if(a.silent)return this.#s.promise;if(a.revert){if(this.state.data===void 0)throw a;return this.state.data}}throw this.#l({type:"error",error:a}),this.#n.config.onError?.(a,this),this.#n.config.onSettled?.(this.state.data,a,this),a}finally{this.scheduleGc()}}#l(e){const t=n=>{switch(e.type){case"failed":return{...n,fetchFailureCount:e.failureCount,fetchFailureReason:e.error};case"pause":return{...n,fetchStatus:"paused"};case"continue":return{...n,fetchStatus:"fetching"};case"fetch":return{...n,...zc(n.data,this.options),fetchMeta:e.meta??null};case"success":const s={...n,...ya(e.data,e.dataUpdatedAt),dataUpdateCount:n.dataUpdateCount+1,...!e.manual&&{fetchStatus:"idle",fetchFailureCount:0,fetchFailureReason:null}};return this.#t=e.manual?s:void 0,s;case"error":const r=e.error;return{...n,error:r,errorUpdateCount:n.errorUpdateCount+1,errorUpdatedAt:Date.now(),fetchFailureCount:n.fetchFailureCount+1,fetchFailureReason:r,fetchStatus:"idle",status:"error",isInvalidated:!0};case"invalidate":return{...n,isInvalidated:!0};case"setState":return{...n,...e.state}}};this.state=t(this.state),Fe.batch(()=>{this.observers.forEach(n=>{n.onQueryUpdate()}),this.#n.notify({query:this,type:"updated",action:e})})}};function zc(e,t){return{fetchFailureCount:0,fetchFailureReason:null,fetchStatus:Vc(t.networkMode)?"fetching":"paused",...e===void 0&&{error:null,status:"pending"}}}function ya(e,t){return{data:e,dataUpdatedAt:t??Date.now(),error:null,isInvalidated:!1,status:"success"}}function ba(e){const t=typeof e.initialData=="function"?e.initialData():e.initialData,n=t!==void 0,s=n?typeof e.initialDataUpdatedAt=="function"?e.initialDataUpdatedAt():e.initialDataUpdatedAt:0;return{data:t,dataUpdateCount:0,dataUpdatedAt:n?s??Date.now():0,error:null,errorUpdateCount:0,errorUpdatedAt:0,fetchFailureCount:0,fetchFailureReason:null,fetchMeta:null,isInvalidated:!1,status:n?"success":"pending",fetchStatus:"idle"}}var yh=class extends Xn{constructor(e,t){super(),this.options=t,this.#e=e,this.#a=null,this.#o=Ni(),this.bindMethods(),this.setOptions(t)}#e;#t=void 0;#n=void 0;#r=void 0;#s;#i;#o;#a;#l;#h;#g;#u;#d;#c;#p=new Set;bindMethods(){this.refetch=this.refetch.bind(this)}onSubscribe(){this.listeners.size===1&&(this.#t.addObserver(this),_a(this.#t,this.options)?this.#f():this.updateResult(),this.#b())}onUnsubscribe(){this.hasListeners()||this.destroy()}shouldFetchOnReconnect(){return Ki(this.#t,this.options,this.options.refetchOnReconnect)}shouldFetchOnWindowFocus(){return Ki(this.#t,this.options,this.options.refetchOnWindowFocus)}destroy(){this.listeners=new Set,this.#_(),this.#w(),this.#t.removeObserver(this)}setOptions(e){const t=this.options,n=this.#t;if(this.options=this.#e.defaultQueryOptions(e),this.options.enabled!==void 0&&typeof this.options.enabled!="boolean"&&typeof this.options.enabled!="function"&&typeof lt(this.options.enabled,this.#t)!="boolean")throw new Error("Expected enabled to be a boolean or a callback that returns a boolean");this.#S(),this.#t.setOptions(this.options),t._defaulted&&!ar(this.options,t)&&this.#e.getQueryCache().notify({type:"observerOptionsUpdated",query:this.#t,observer:this});const s=this.hasListeners();s&&wa(this.#t,n,this.options,t)&&this.#f(),this.updateResult(),s&&(this.#t!==n||lt(this.options.enabled,this.#t)!==lt(t.enabled,this.#t)||Ut(this.options.staleTime,this.#t)!==Ut(t.staleTime,this.#t))&&this.#m();const r=this.#v();s&&(this.#t!==n||lt(this.options.enabled,this.#t)!==lt(t.enabled,this.#t)||r!==this.#c)&&this.#y(r)}getOptimisticResult(e){const t=this.#e.getQueryCache().build(this.#e,e),n=this.createResult(t,e);return _h(this,n)&&(this.#r=n,this.#i=this.options,this.#s=this.#t.state),n}getCurrentResult(){return this.#r}trackResult(e,t){return new Proxy(e,{get:(n,s)=>(this.trackProp(s),t?.(s),s==="promise"&&(this.trackProp("data"),!this.options.experimental_prefetchInRender&&this.#o.status==="pending"&&this.#o.reject(new Error("experimental_prefetchInRender feature flag is not enabled"))),Reflect.get(n,s))})}trackProp(e){this.#p.add(e)}getCurrentQuery(){return this.#t}refetch({...e}={}){return this.fetch({...e})}fetchOptimistic(e){const t=this.#e.defaultQueryOptions(e),n=this.#e.getQueryCache().build(this.#e,t);return n.fetch().then(()=>this.createResult(n,t))}fetch(e){return this.#f({...e,cancelRefetch:e.cancelRefetch??!0}).then(()=>(this.updateResult(),this.#r))}#f(e){this.#S();let t=this.#t.fetch(this.options,e);return e?.throwOnError||(t=t.catch(Ke)),t}#m(){this.#_();const e=Ut(this.options.staleTime,this.#t);if(lr.isServer()||this.#r.isStale||!Mi(e))return;const n=jc(this.#r.dataUpdatedAt,e)+1;this.#u=un.setTimeout(()=>{this.#r.isStale||this.updateResult()},n)}#v(){return(typeof this.options.refetchInterval=="function"?this.options.refetchInterval(this.#t):this.options.refetchInterval)??!1}#y(e){this.#w(),this.#c=e,!(lr.isServer()||lt(this.options.enabled,this.#t)===!1||!Mi(this.#c)||this.#c===0)&&(this.#d=un.setInterval(()=>{(this.options.refetchIntervalInBackground||co.isFocused())&&this.#f()},this.#c))}#b(){this.#m(),this.#y(this.#v())}#_(){this.#u&&(un.clearTimeout(this.#u),this.#u=void 0)}#w(){this.#d&&(un.clearInterval(this.#d),this.#d=void 0)}createResult(e,t){const n=this.#t,s=this.options,r=this.#r,i=this.#s,o=this.#i,l=e!==n?e.state:this.#n,{state:c}=e;let d={...c},u=!1,f;if(t._optimisticResults){const E=this.hasListeners(),O=!E&&_a(e,t),$=E&&wa(e,n,t,s);(O||$)&&(d={...d,...zc(c.data,e.options)}),t._optimisticResults==="isRestoring"&&(d.fetchStatus="idle")}let{error:h,errorUpdatedAt:g,status:m}=d;f=d.data;let y=!1;if(t.placeholderData!==void 0&&f===void 0&&m==="pending"){let E;r?.isPlaceholderData&&t.placeholderData===o?.placeholderData?(E=r.data,y=!0):E=typeof t.placeholderData=="function"?t.placeholderData(this.#g?.state.data,this.#g):t.placeholderData,E!==void 0&&(m="success",f=Di(r?.data,E,t),u=!0)}if(t.select&&f!==void 0&&!y)if(r&&f===i?.data&&t.select===this.#l)f=this.#h;else try{this.#l=t.select,f=t.select(f),f=Di(r?.data,f,t),this.#h=f,this.#a=null}catch(E){this.#a=E}this.#a&&(h=this.#a,f=this.#h,g=Date.now(),m="error");const v=d.fetchStatus==="fetching",b=m==="pending",w=m==="error",S=b&&v,x=f!==void 0,P={status:m,fetchStatus:d.fetchStatus,isPending:b,isSuccess:m==="success",isError:w,isInitialLoading:S,isLoading:S,data:f,dataUpdatedAt:d.dataUpdatedAt,error:h,errorUpdatedAt:g,failureCount:d.fetchFailureCount,failureReason:d.fetchFailureReason,errorUpdateCount:d.errorUpdateCount,isFetched:e.isFetched(),isFetchedAfterMount:d.dataUpdateCount>l.dataUpdateCount||d.errorUpdateCount>l.errorUpdateCount,isFetching:v,isRefetching:v&&!b,isLoadingError:w&&!x,isPaused:d.fetchStatus==="paused",isPlaceholderData:u,isRefetchError:w&&x,isStale:ho(e,t),refetch:this.refetch,promise:this.#o,isEnabled:lt(t.enabled,e)!==!1};if(this.options.experimental_prefetchInRender){const E=P.data!==void 0,O=P.status==="error"&&!E,$=T=>{O?T.reject(P.error):E&&T.resolve(P.data)},C=()=>{const T=this.#o=P.promise=Ni();$(T)},I=this.#o;switch(I.status){case"pending":e.queryHash===n.queryHash&&$(I);break;case"fulfilled":(O||P.data!==I.value)&&C();break;case"rejected":(!O||P.error!==I.reason)&&C();break}}return P}updateResult(){const e=this.#r,t=this.createResult(this.#t,this.options);if(this.#s=this.#t.state,this.#i=this.options,this.#s.data!==void 0&&(this.#g=this.#t),ar(t,e))return;this.#r=t;const n=()=>{if(!e)return!0;const{notifyOnChangeProps:s}=this.options,r=typeof s=="function"?s():s;if(r==="all"||!r&&!this.#p.size)return!0;const i=new Set(r??this.#p);return this.options.throwOnError&&i.add("error"),Object.keys(this.#r).some(o=>{const a=o;return this.#r[a]!==e[a]&&i.has(a)})};this.#x({listeners:n()})}#S(){const e=this.#e.getQueryCache().build(this.#e,this.options);if(e===this.#t)return;const t=this.#t;this.#t=e,this.#n=e.state,this.hasListeners()&&(t?.removeObserver(this),e.addObserver(this))}onQueryUpdate(){this.updateResult(),this.hasListeners()&&this.#b()}#x(e){Fe.batch(()=>{e.listeners&&this.listeners.forEach(t=>{t(this.#r)}),this.#e.getQueryCache().notify({query:this.#t,type:"observerResultsUpdated"})})}};function bh(e,t){return lt(t.enabled,e)!==!1&&e.state.data===void 0&&!(e.state.status==="error"&&t.retryOnMount===!1)}function _a(e,t){return bh(e,t)||e.state.data!==void 0&&Ki(e,t,t.refetchOnMount)}function Ki(e,t,n){if(lt(t.enabled,e)!==!1&&Ut(t.staleTime,e)!=="static"){const s=typeof n=="function"?n(e):n;return s==="always"||s!==!1&&ho(e,t)}return!1}function wa(e,t,n,s){return(e!==t||lt(s.enabled,e)===!1)&&(!n.suspense||e.state.status!=="error")&&ho(e,n)}function ho(e,t){return lt(t.enabled,e)!==!1&&e.isStaleByTime(Ut(t.staleTime,e))}function _h(e,t){return!ar(e.getCurrentResult(),t)}function Sa(e){return{onFetch:(t,n)=>{const s=t.options,r=t.fetchOptions?.meta?.fetchMore?.direction,i=t.state.data?.pages||[],o=t.state.data?.pageParams||[];let a={pages:[],pageParams:[]},l=0;const c=async()=>{let d=!1;const u=g=>{ch(g,()=>t.signal,()=>d=!0)},f=Bc(t.options,t.fetchOptions),h=async(g,m,y)=>{if(d)return Promise.reject();if(m==null&&g.pages.length)return Promise.resolve(g);const b=(()=>{const k={client:t.client,queryKey:t.queryKey,pageParam:m,direction:y?"backward":"forward",meta:t.options.meta};return u(k),k})(),w=await f(b),{maxPages:S}=t.options,x=y?lh:ah;return{pages:x(g.pages,w,S),pageParams:x(g.pageParams,m,S)}};if(r&&i.length){const g=r==="backward",m=g?wh:xa,y={pages:i,pageParams:o},v=m(s,y);a=await h(y,v,g)}else{const g=e??i.length;do{const m=l===0?o[0]??s.initialPageParam:xa(s,a);if(l>0&&m==null)break;a=await h(a,m),l++}while(lt.options.persister?.(c,{client:t.client,queryKey:t.queryKey,meta:t.options.meta,signal:t.signal},n):t.fetchFn=c}}}function xa(e,{pages:t,pageParams:n}){const s=t.length-1;return t.length>0?e.getNextPageParam(t[s],t,n[s],n):void 0}function wh(e,{pages:t,pageParams:n}){return t.length>0?e.getPreviousPageParam?.(t[0],t,n[0],n):void 0}var Sh=class extends Hc{#e;#t;#n;#r;constructor(e){super(),this.#e=e.client,this.mutationId=e.mutationId,this.#n=e.mutationCache,this.#t=[],this.state=e.state||Wc(),this.setOptions(e.options),this.scheduleGc()}setOptions(e){this.options=e,this.updateGcTime(this.options.gcTime)}get meta(){return this.options.meta}addObserver(e){this.#t.includes(e)||(this.#t.push(e),this.clearGcTimeout(),this.#n.notify({type:"observerAdded",mutation:this,observer:e}))}removeObserver(e){this.#t=this.#t.filter(t=>t!==e),this.scheduleGc(),this.#n.notify({type:"observerRemoved",mutation:this,observer:e})}optionalRemove(){this.#t.length||(this.state.status==="pending"?this.scheduleGc():this.#n.remove(this))}continue(){return this.#r?.continue()??this.execute(this.state.variables)}async execute(e){const t=()=>{this.#s({type:"continue"})},n={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};this.#r=qc({fn:()=>this.options.mutationFn?this.options.mutationFn(e,n):Promise.reject(new Error("No mutationFn found")),onFail:(i,o)=>{this.#s({type:"failed",failureCount:i,error:o})},onPause:()=>{this.#s({type:"pause"})},onContinue:t,retry:this.options.retry??0,retryDelay:this.options.retryDelay,networkMode:this.options.networkMode,canRun:()=>this.#n.canRun(this)});const s=this.state.status==="pending",r=!this.#r.canStart();try{if(s)t();else{this.#s({type:"pending",variables:e,isPaused:r}),this.#n.config.onMutate&&await this.#n.config.onMutate(e,this,n);const o=await this.options.onMutate?.(e,n);o!==this.state.context&&this.#s({type:"pending",context:o,variables:e,isPaused:r})}const i=await this.#r.start();return await this.#n.config.onSuccess?.(i,e,this.state.context,this,n),await this.options.onSuccess?.(i,e,this.state.context,n),await this.#n.config.onSettled?.(i,null,this.state.variables,this.state.context,this,n),await this.options.onSettled?.(i,null,e,this.state.context,n),this.#s({type:"success",data:i}),i}catch(i){try{await this.#n.config.onError?.(i,e,this.state.context,this,n)}catch(o){Promise.reject(o)}try{await this.options.onError?.(i,e,this.state.context,n)}catch(o){Promise.reject(o)}try{await this.#n.config.onSettled?.(void 0,i,this.state.variables,this.state.context,this,n)}catch(o){Promise.reject(o)}try{await this.options.onSettled?.(void 0,i,e,this.state.context,n)}catch(o){Promise.reject(o)}throw this.#s({type:"error",error:i}),i}finally{this.#n.runNext(this)}}#s(e){const t=n=>{switch(e.type){case"failed":return{...n,failureCount:e.failureCount,failureReason:e.error};case"pause":return{...n,isPaused:!0};case"continue":return{...n,isPaused:!1};case"pending":return{...n,context:e.context,data:void 0,failureCount:0,failureReason:null,error:null,isPaused:e.isPaused,status:"pending",variables:e.variables,submittedAt:Date.now()};case"success":return{...n,data:e.data,failureCount:0,failureReason:null,error:null,status:"success",isPaused:!1};case"error":return{...n,data:void 0,error:e.error,failureCount:n.failureCount+1,failureReason:e.error,isPaused:!1,status:"error"}}};this.state=t(this.state),Fe.batch(()=>{this.#t.forEach(n=>{n.onMutationUpdate(e)}),this.#n.notify({mutation:this,type:"updated",action:e})})}};function Wc(){return{context:void 0,data:void 0,error:null,failureCount:0,failureReason:null,isPaused:!1,status:"idle",variables:void 0,submittedAt:0}}var xh=class extends Xn{constructor(e={}){super(),this.config=e,this.#e=new Set,this.#t=new Map,this.#n=0}#e;#t;#n;build(e,t,n){const s=new Sh({client:e,mutationCache:this,mutationId:++this.#n,options:e.defaultMutationOptions(t),state:n});return this.add(s),s}add(e){this.#e.add(e);const t=qs(e);if(typeof t=="string"){const n=this.#t.get(t);n?n.push(e):this.#t.set(t,[e])}this.notify({type:"added",mutation:e})}remove(e){if(this.#e.delete(e)){const t=qs(e);if(typeof t=="string"){const n=this.#t.get(t);if(n)if(n.length>1){const s=n.indexOf(e);s!==-1&&n.splice(s,1)}else n[0]===e&&this.#t.delete(t)}}this.notify({type:"removed",mutation:e})}canRun(e){const t=qs(e);if(typeof t=="string"){const s=this.#t.get(t)?.find(r=>r.state.status==="pending");return!s||s===e}else return!0}runNext(e){const t=qs(e);return typeof t=="string"?this.#t.get(t)?.find(s=>s!==e&&s.state.isPaused)?.continue()??Promise.resolve():Promise.resolve()}clear(){Fe.batch(()=>{this.#e.forEach(e=>{this.notify({type:"removed",mutation:e})}),this.#e.clear(),this.#t.clear()})}getAll(){return Array.from(this.#e)}find(e){const t={exact:!0,...e};return this.getAll().find(n=>pa(t,n))}findAll(e={}){return this.getAll().filter(t=>pa(e,t))}notify(e){Fe.batch(()=>{this.listeners.forEach(t=>{t(e)})})}resumePausedMutations(){const e=this.getAll().filter(t=>t.state.isPaused);return Fe.batch(()=>Promise.all(e.map(t=>t.continue().catch(Ke))))}};function qs(e){return e.options.scope?.id}var $h=class extends Xn{#e;#t=void 0;#n;#r;constructor(t,n){super(),this.#e=t,this.setOptions(n),this.bindMethods(),this.#s()}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(t){const n=this.options;this.options=this.#e.defaultMutationOptions(t),ar(this.options,n)||this.#e.getMutationCache().notify({type:"observerOptionsUpdated",mutation:this.#n,observer:this}),n?.mutationKey&&this.options.mutationKey&&Sn(n.mutationKey)!==Sn(this.options.mutationKey)?this.reset():this.#n?.state.status==="pending"&&this.#n.setOptions(this.options)}onUnsubscribe(){this.hasListeners()||this.#n?.removeObserver(this)}onMutationUpdate(t){this.#s(),this.#i(t)}getCurrentResult(){return this.#t}reset(){this.#n?.removeObserver(this),this.#n=void 0,this.#s(),this.#i()}mutate(t,n){return this.#r=n,this.#n?.removeObserver(this),this.#n=this.#e.getMutationCache().build(this.#e,this.options),this.#n.addObserver(this),this.#n.execute(t)}#s(){const t=this.#n?.state??Wc();this.#t={...t,isPending:t.status==="pending",isSuccess:t.status==="success",isError:t.status==="error",isIdle:t.status==="idle",mutate:this.mutate,reset:this.reset}}#i(t){Fe.batch(()=>{if(this.#r&&this.hasListeners()){const n=this.#t.variables,s=this.#t.context,r={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};if(t?.type==="success"){try{this.#r.onSuccess?.(t.data,n,s,r)}catch(i){Promise.reject(i)}try{this.#r.onSettled?.(t.data,null,n,s,r)}catch(i){Promise.reject(i)}}else if(t?.type==="error"){try{this.#r.onError?.(t.error,n,s,r)}catch(i){Promise.reject(i)}try{this.#r.onSettled?.(void 0,t.error,n,s,r)}catch(i){Promise.reject(i)}}}this.listeners.forEach(n=>{n(this.#t)})})}},Ch=class extends Xn{constructor(e={}){super(),this.config=e,this.#e=new Map}#e;build(e,t,n){const s=t.queryKey,r=t.queryHash??uo(s,t);let i=this.get(r);return i||(i=new vh({client:e,queryKey:s,queryHash:r,options:e.defaultQueryOptions(t),state:n,defaultOptions:e.getQueryDefaults(s)}),this.add(i)),i}add(e){this.#e.has(e.queryHash)||(this.#e.set(e.queryHash,e),this.notify({type:"added",query:e}))}remove(e){const t=this.#e.get(e.queryHash);t&&(e.destroy(),t===e&&this.#e.delete(e.queryHash),this.notify({type:"removed",query:e}))}clear(){Fe.batch(()=>{this.getAll().forEach(e=>{this.remove(e)})})}get(e){return this.#e.get(e)}getAll(){return[...this.#e.values()]}find(e){const t={exact:!0,...e};return this.getAll().find(n=>ga(t,n))}findAll(e={}){const t=this.getAll();return Object.keys(e).length>0?t.filter(n=>ga(e,n)):t}notify(e){Fe.batch(()=>{this.listeners.forEach(t=>{t(e)})})}onFocus(){Fe.batch(()=>{this.getAll().forEach(e=>{e.onFocus()})})}onOnline(){Fe.batch(()=>{this.getAll().forEach(e=>{e.onOnline()})})}},Ph=class{#e;#t;#n;#r;#s;#i;#o;#a;constructor(t={}){this.#e=t.queryCache||new Ch,this.#t=t.mutationCache||new xh,this.#n=t.defaultOptions||{},this.#r=new Map,this.#s=new Map,this.#i=0}mount(){this.#i++,this.#i===1&&(this.#o=co.subscribe(async t=>{t&&(await this.resumePausedMutations(),this.#e.onFocus())}),this.#a=cr.subscribe(async t=>{t&&(await this.resumePausedMutations(),this.#e.onOnline())}))}unmount(){this.#i--,this.#i===0&&(this.#o?.(),this.#o=void 0,this.#a?.(),this.#a=void 0)}isFetching(t){return this.#e.findAll({...t,fetchStatus:"fetching"}).length}isMutating(t){return this.#t.findAll({...t,status:"pending"}).length}getQueryData(t){const n=this.defaultQueryOptions({queryKey:t});return this.#e.get(n.queryHash)?.state.data}ensureQueryData(t){const n=this.defaultQueryOptions(t),s=this.#e.build(this,n),r=s.state.data;return r===void 0?this.fetchQuery(t):(t.revalidateIfStale&&s.isStaleByTime(Ut(n.staleTime,s))&&this.prefetchQuery(n),Promise.resolve(r))}getQueriesData(t){return this.#e.findAll(t).map(({queryKey:n,state:s})=>{const r=s.data;return[n,r]})}setQueryData(t,n,s){const r=this.defaultQueryOptions({queryKey:t}),o=this.#e.get(r.queryHash)?.state.data,a=rh(n,o);if(a!==void 0)return this.#e.build(this,r).setData(a,{...s,manual:!0})}setQueriesData(t,n,s){return Fe.batch(()=>this.#e.findAll(t).map(({queryKey:r})=>[r,this.setQueryData(r,n,s)]))}getQueryState(t){const n=this.defaultQueryOptions({queryKey:t});return this.#e.get(n.queryHash)?.state}removeQueries(t){const n=this.#e;Fe.batch(()=>{n.findAll(t).forEach(s=>{n.remove(s)})})}resetQueries(t,n){const s=this.#e;return Fe.batch(()=>(s.findAll(t).forEach(r=>{r.reset()}),this.refetchQueries({type:"active",...t},n)))}cancelQueries(t,n={}){const s={revert:!0,...n},r=Fe.batch(()=>this.#e.findAll(t).map(i=>i.cancel(s)));return Promise.all(r).then(Ke).catch(Ke)}invalidateQueries(t,n={}){return Fe.batch(()=>(this.#e.findAll(t).forEach(s=>{s.invalidate()}),t?.refetchType==="none"?Promise.resolve():this.refetchQueries({...t,type:t?.refetchType??t?.type??"active"},n)))}refetchQueries(t,n={}){const s={...n,cancelRefetch:n.cancelRefetch??!0},r=Fe.batch(()=>this.#e.findAll(t).filter(i=>!i.isDisabled()&&!i.isStatic()).map(i=>{let o=i.fetch(void 0,s);return s.throwOnError||(o=o.catch(Ke)),i.state.fetchStatus==="paused"?Promise.resolve():o}));return Promise.all(r).then(Ke)}fetchQuery(t){const n=this.defaultQueryOptions(t);n.retry===void 0&&(n.retry=!1);const s=this.#e.build(this,n);return s.isStaleByTime(Ut(n.staleTime,s))?s.fetch(n):Promise.resolve(s.state.data)}prefetchQuery(t){return this.fetchQuery(t).then(Ke).catch(Ke)}fetchInfiniteQuery(t){return t.behavior=Sa(t.pages),this.fetchQuery(t)}prefetchInfiniteQuery(t){return this.fetchInfiniteQuery(t).then(Ke).catch(Ke)}ensureInfiniteQueryData(t){return t.behavior=Sa(t.pages),this.ensureQueryData(t)}resumePausedMutations(){return cr.isOnline()?this.#t.resumePausedMutations():Promise.resolve()}getQueryCache(){return this.#e}getMutationCache(){return this.#t}getDefaultOptions(){return this.#n}setDefaultOptions(t){this.#n=t}setQueryDefaults(t,n){this.#r.set(Sn(t),{queryKey:t,defaultOptions:n})}getQueryDefaults(t){const n=[...this.#r.values()],s={};return n.forEach(r=>{xs(t,r.queryKey)&&Object.assign(s,r.defaultOptions)}),s}setMutationDefaults(t,n){this.#s.set(Sn(t),{mutationKey:t,defaultOptions:n})}getMutationDefaults(t){const n=[...this.#s.values()],s={};return n.forEach(r=>{xs(t,r.mutationKey)&&Object.assign(s,r.defaultOptions)}),s}defaultQueryOptions(t){if(t._defaulted)return t;const n={...this.#n.queries,...this.getQueryDefaults(t.queryKey),...t,_defaulted:!0};return n.queryHash||(n.queryHash=uo(n.queryKey,n)),n.refetchOnReconnect===void 0&&(n.refetchOnReconnect=n.networkMode!=="always"),n.throwOnError===void 0&&(n.throwOnError=!!n.suspense),!n.networkMode&&n.persister&&(n.networkMode="offlineFirst"),n.queryFn===fo&&(n.enabled=!1),n}defaultMutationOptions(t){return t?._defaulted?t:{...this.#n.mutations,...t?.mutationKey&&this.getMutationDefaults(t.mutationKey),...t,_defaulted:!0}}clear(){this.#e.clear(),this.#t.clear()}};const Bi=Symbol("store-raw"),Kn=Symbol("store-node"),Rt=Symbol("store-has"),Qc=Symbol("store-self");function Gc(e){let t=e[gt];if(!t&&(Object.defineProperty(e,gt,{value:t=new Proxy(e,Rh)}),!Array.isArray(e))){const n=Object.keys(e),s=Object.getOwnPropertyDescriptors(e);for(let r=0,i=n.length;re[gt][t]),n}function Jc(e){Ri()&&$s(ur(e,Kn),Qc)()}function Eh(e){return Jc(e),Reflect.ownKeys(e)}const Rh={get(e,t,n){if(t===Bi)return e;if(t===gt)return n;if(t===Ei)return Jc(e),n;const s=ur(e,Kn),r=s[t];let i=r?r():e[t];if(t===Kn||t===Rt||t==="__proto__")return i;if(!r){const o=Object.getOwnPropertyDescriptor(e,t);Ri()&&(typeof i!="function"||e.hasOwnProperty(t))&&!(o&&o.get)&&(i=$s(s,t,i)())}return zt(i)?Gc(i):i},has(e,t){return t===Bi||t===gt||t===Ei||t===Kn||t===Rt||t==="__proto__"?!0:(Ri()&&$s(ur(e,Rt),t)(),t in e)},set(){return!0},deleteProperty(){return!0},ownKeys:Eh,getOwnPropertyDescriptor:kh};function ot(e,t,n,s=!1){if(!s&&e[t]===n)return;const r=e[t],i=e.length;n===void 0?(delete e[t],e[Rt]&&e[Rt][t]&&r!==void 0&&e[Rt][t].$()):(e[t]=n,e[Rt]&&e[Rt][t]&&r===void 0&&e[Rt][t].$());let o=ur(e,Kn),a;if((a=$s(o,t,r))&&a.$(()=>n),Array.isArray(e)&&e.length!==i){for(let l=e.length;l1){s=t.shift();const o=typeof s,a=Array.isArray(e);if(Array.isArray(s)){for(let l=0;l1){gs(e[s],t,[s].concat(n));return}r=e[s],n=[s].concat(n)}let i=t[0];typeof i=="function"&&(i=i(r,n),i===r)||s===void 0&&i==null||(i=xn(i),s===void 0||zt(r)&&zt(i)&&!Array.isArray(i)?Yc(r,i):ot(e,s,i))}function Xc(...[e,t]){const n=xn(e||{}),s=Array.isArray(n),r=Gc(n);function i(...o){oo(()=>{s&&o.length===1?Oh(n,o[0]):gs(n,o)})}return[r,i]}const Ui=Symbol("store-root");function Fn(e,t,n,s,r){const i=t[n];if(e===i)return;const o=Array.isArray(e);if(n!==Ui&&(!zt(e)||!zt(i)||o!==Array.isArray(i)||r&&e[r]!==i[r])){ot(t,n,e);return}if(o){if(e.length&&i.length&&(!s||r&&e[0]&&e[0][r]!=null)){let c,d,u,f,h,g,m,y;for(u=0,f=Math.min(i.length,e.length);u=u&&h>=u&&(i[f]===e[h]||r&&i[f]&&e[h]&&i[f][r]&&i[f][r]===e[h][r]);f--,h--)v[h]=i[f];if(u>h||u>f){for(d=u;d<=h;d++)ot(i,d,e[d]);for(;de.length&&ot(i,"length",e.length);return}for(m=new Array(h+1),d=h;d>=u;d--)g=e[d],y=r&&g?g[r]:g,c=b.get(y),m[d]=c===void 0?-1:c,b.set(y,d);for(c=u;c<=f;c++)g=i[c],y=r&&g?g[r]:g,d=b.get(y),d!==void 0&&d!==-1&&(v[d]=i[c],d=m[d],b.set(y,d));for(d=u;de.length&&ot(i,"length",e.length);return}const a=Object.keys(e);for(let c=0,d=a.length;c{if(!zt(i)||!zt(r))return r;const o=Fn(r,{[Ui]:i},Ui,n,s);return o===void 0?i:o}}var Zc=He(void 0),Jt=e=>{if(e)return e;const t=Te(Zc);if(!t)throw new Error("No QueryClient set, use QueryClientProvider to set one");return t()},Th=e=>(j(t=>(t?.(),e.client.mount(),e.client.unmount.bind(e.client))),Z(()=>e.client.unmount()),_(Zc.Provider,{value:()=>e.client,get children(){return e.children}})),eu=He(()=>!1),Ih=()=>Te(eu);eu.Provider;function Ah(e,t,n,s){if(n===!1)return t;if(typeof n=="function"){const o=n(e.data,t.data);return{...t,data:o}}let r=t.data;if(e.data===void 0)try{r=structuredClone(r)}catch{}const i=Lh(r,{key:n})(e.data);return{...t,data:i}}var Mh=(e,t)=>t;function Fh(e,t,n){const s=D(()=>Jt(n?.())),r=Ih(),i=D(()=>{const S=s().defaultQueryOptions(e());return S._optimisticResults=r()?"isRestoring":"optimistic",S.structuralSharing=!1,S}),o=i(),[a,l]=A(new t(s(),i()));let c=a().getOptimisticResult(i());const[d,u]=Xc(c),f=()=>a().subscribe(x=>{c=x,queueMicrotask(()=>{m&&b()})});function h(S){const x=a().options,k=x.reconcile;u(P=>Ah(P,S,k===void 0?!1:k,x.queryHash))}function g(){return[()=>d,S=>{const x=xn(d);if(typeof S=="function"&&(S=S(x)),S?.hydrationData){const{hydrationData:k,...P}=S;S=P}h(S)}]}let m=null,y=null;const[v,{refetch:b}]=hs(()=>{const S=a();return new Promise((x,k)=>{if(y=x,!m&&!r()&&(m=f()),S.updateResult(),c.isError&&!c.isFetching&&!r()&&Uc(S.options.throwOnError,[c.error,S.getCurrentQuery()]))return h(c),k(c.error);if(!c.isLoading)return y=null,x(Mh(S.getCurrentQuery(),c));h(c)})},{storage:g,get deferStream(){return e().deferStream},onHydrated(S,x){if(x.value&&"hydrationData"in x.value&&fh(s(),{queries:[{...x.value.hydrationData}]}),m)return;const k={...o};(o.staleTime||!o.initialData)&&x.value&&(k.refetchOnMount=!1),a().setOptions(k),h(a().getOptimisticResult(k)),m=f()}});Tt(Ve(s,S=>{m&&m();const x=new t(S,i());m=f(),l(x)},{defer:!0})),Tt(Ve(r,S=>{S||b()},{defer:!0})),Z(()=>{m&&(m(),m=null),y&&(y(c),y=null)}),Tt(Ve([a,i],([S,x])=>{S.setOptions(x),h(S.getOptimisticResult(x)),b()},{defer:!0}));const w={get(S,x){return x==="data"?d.data!==void 0?v.latest?.data:v()?.data:Reflect.get(S,x)}};return new Proxy(d,w)}function Dh(e,t){return Fh(D(()=>e()),yh,t)}function Nh(e,t){const n=D(()=>Jt(t?.())),s=new $h(n(),e()),r=(l,c)=>{s.mutate(l,c).catch(Ke)},[i,o]=Xc({...s.getCurrentResult(),mutate:r,mutateAsync:s.getCurrentResult().mutate});Tt(()=>{s.setOptions(e())}),Tt(Ve(()=>i.status,()=>{if(i.isError&&Uc(s.options.throwOnError,[i.error]))throw i.error}));const a=s.subscribe(l=>{o({...l,mutate:r,mutateAsync:l.mutate})});return Z(a),i}var jh=class extends Ph{constructor(e={}){super(e)}},ye=Dh,Pe=Nh;const go="axinite.gateway-token";function po(){return typeof window>"u"||!window.sessionStorage?null:window.sessionStorage}function mo(){return po()?.getItem(go)??null}function $a(e){po()?.setItem(go,e)}function Ca(){po()?.removeItem(go)}function Kh(e){const t=mo();if(!t)return e;const n=e.includes("?")?"&":"?";return`${e}${n}token=${encodeURIComponent(t)}`}async function vo(e,t={}){const s=new AbortController,r=setTimeout(()=>s.abort(),5e3);try{const i=mo(),o=await fetch(e,{...t,signal:s.signal,headers:{Accept:"application/json",...i?{Authorization:`Bearer ${i}`}:{},...t.headers??{}}});if(!o.ok){const a=await o.text();throw new Error(a.length>0?a:`Request failed for ${e} with ${o.status}`)}return o.status===204?void 0:await o.json()}catch(i){throw s.signal.aborted?new Error("Request timed out after 5000ms"):i}finally{clearTimeout(r)}}function xe(e){return vo(e)}function ke(e,t,n){return vo(e,{method:"POST",headers:{"Content-Type":"application/json"},body:typeof t>"u"?null:JSON.stringify(t)})}function tu(e){return vo(e,{method:"DELETE"})}function nu(e){const t=Kh(e);return typeof EventSource>"u"?{onerror:null,onmessage:null,onopen:null,readyState:0,url:t,withCredentials:!1,addEventListener(){},removeEventListener(){},dispatchEvent(){return!0},close(){}}:new EventSource(t,{withCredentials:!1})}async function su(){try{return await xe("/api/gateway/status")}catch{return null}}function Bh(e){if(!e)return{label:"Preview",detail:"Mock gateway unavailable"};const t=e.total_connections??0;return{label:t>0?"Connected":"Preview",detail:`v${e.version} · ${t} live browser stream${t===1?"":"s"}`}}async function Uh(){try{const e=await xe("/api/features"),t=typeof e=="object"&&e!==null&&"flags"in e&&typeof e.flags=="object"&&e.flags!==null?e.flags:e;return Object.entries(t).reduce((n,[s,r])=>(typeof r=="boolean"&&(n[s]=r),n),{})}catch{return{}}}const yo=[{name:"route_chat",defaultValue:!0,owner:"chat-ui",backendContract:"always available in preview shell"},{name:"route_memory",defaultValue:!0,owner:"memory-ui",backendContract:"always available in preview shell"},{name:"route_jobs",defaultValue:!0,owner:"jobs-ui",backendContract:"hide when jobs runtime is absent"},{name:"route_routines",defaultValue:!0,owner:"routines-ui",backendContract:"hide when scheduler runtime is absent"},{name:"route_extensions",defaultValue:!0,owner:"extensions-ui",backendContract:"hide when extension runtime is absent"},{name:"route_skills",defaultValue:!0,owner:"skills-ui",backendContract:"hide when skill registry is absent"},{name:"route_logs",defaultValue:!0,owner:"shell-ui",backendContract:"always available in preview shell"},{name:"panel_logs",defaultValue:!0,owner:"shell-ui",backendContract:"hide when log stream is absent"},{name:"action_memory_edit",defaultValue:!1,owner:"memory-ui",backendContract:"enable only after write API is stable"},{name:"action_job_restart",defaultValue:!1,owner:"jobs-ui",backendContract:"enable only after restart endpoint is stable"},{name:"action_routine_trigger",defaultValue:!1,owner:"routines-ui",backendContract:"enable only after trigger endpoint is stable"},{name:"action_extension_install",defaultValue:!1,owner:"extensions-ui",backendContract:"enable only after install endpoint is stable"},{name:"action_skill_install",defaultValue:!1,owner:"skills-ui",backendContract:"enable only after catalogue endpoints are stable"},{name:"surface_tee_attestation",defaultValue:!1,owner:"shell-ui",backendContract:"enable only when attestation data is available"}];function Vh(){return yo.reduce((e,t)=>(e[t.name]=t.defaultValue,e),{})}const ru="axinite.feature-flag-overrides";function Pa(){if(typeof window>"u")return{};const e=window.localStorage.getItem(ru);if(!e)return{};try{return JSON.parse(e)}catch{return{}}}function ka(e){typeof window>"u"||window.localStorage.setItem(ru,JSON.stringify(e))}function qh(e,t){const n=Vh();for(const s of yo){const r=e[s.name],i=t[s.name];if(typeof i=="boolean"){n[s.name]=i;continue}typeof r=="boolean"&&(n[s.name]=r)}return n}function iu(){return typeof window>"u"?!1:new URLSearchParams(window.location.search).get("debug-flags")==="1"}function Hh(e){return!!e&&iu()}const ou=He(),zh=e=>{const[t,n]=A(Pa()),s=ye(()=>({queryKey:["feature-flags"],queryFn:Uh}));q(()=>{Hh(s.error)&&console.error("[feature-flags] Failed to load runtime flags",s.error)});const r=D(()=>qh(s.data??{},t())),i=()=>n(Pa());typeof window<"u"&&(window.addEventListener("storage",i),Z(()=>window.removeEventListener("storage",i)));const o=(l,c)=>{const d={...t(),[l]:c};n(d),ka(d)},a=l=>{const c={...t()};delete c[l],n(c),ka(c)};return _(ou.Provider,{value:{overrides:t,resolvedFlags:r,setOverride:o,clearOverride:a,isRouteVisible:l=>r()[l],isDebugEnabled:iu},get children(){return e.children}})};function Zn(){const e=Te(ou);if(!e)throw new Error("FeatureFlagProvider is missing");return e}var Ea={};const Y=e=>typeof e=="string",rs=()=>{let e,t;const n=new Promise((s,r)=>{e=s,t=r});return n.resolve=e,n.reject=t,n},Ra=e=>e==null?"":""+e,Wh=(e,t,n)=>{e.forEach(s=>{t[s]&&(n[s]=t[s])})},Qh=/###/g,Oa=e=>e&&e.indexOf("###")>-1?e.replace(Qh,"."):e,La=e=>!e||Y(e),ys=(e,t,n)=>{const s=Y(t)?t.split("."):t;let r=0;for(;r{const{obj:s,k:r}=ys(e,t,Object);if(s!==void 0||t.length===1){s[r]=n;return}let i=t[t.length-1],o=t.slice(0,t.length-1),a=ys(e,o,Object);for(;a.obj===void 0&&o.length;)i=`${o[o.length-1]}.${i}`,o=o.slice(0,o.length-1),a=ys(e,o,Object),a?.obj&&typeof a.obj[`${a.k}.${i}`]<"u"&&(a.obj=void 0);a.obj[`${a.k}.${i}`]=n},Gh=(e,t,n,s)=>{const{obj:r,k:i}=ys(e,t,Object);r[i]=r[i]||[],r[i].push(n)},dr=(e,t)=>{const{obj:n,k:s}=ys(e,t);if(n&&Object.prototype.hasOwnProperty.call(n,s))return n[s]},Jh=(e,t,n)=>{const s=dr(e,n);return s!==void 0?s:dr(t,n)},au=(e,t,n)=>{for(const s in t)s!=="__proto__"&&s!=="constructor"&&(s in e?Y(e[s])||e[s]instanceof String||Y(t[s])||t[s]instanceof String?n&&(e[s]=t[s]):au(e[s],t[s],n):e[s]=t[s]);return e},rn=e=>e.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&");var Yh={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};const Xh=e=>Y(e)?e.replace(/[&<>"'\/]/g,t=>Yh[t]):e;class Zh{constructor(t){this.capacity=t,this.regExpMap=new Map,this.regExpQueue=[]}getRegExp(t){const n=this.regExpMap.get(t);if(n!==void 0)return n;const s=new RegExp(t);return this.regExpQueue.length===this.capacity&&this.regExpMap.delete(this.regExpQueue.shift()),this.regExpMap.set(t,s),this.regExpQueue.push(t),s}}const eg=[" ",",","?","!",";"],tg=new Zh(20),ng=(e,t,n)=>{t=t||"",n=n||"";const s=eg.filter(o=>t.indexOf(o)<0&&n.indexOf(o)<0);if(s.length===0)return!0;const r=tg.getRegExp(`(${s.map(o=>o==="?"?"\\?":o).join("|")})`);let i=!r.test(e);if(!i){const o=e.indexOf(n);o>0&&!r.test(e.substring(0,o))&&(i=!0)}return i},Vi=(e,t,n=".")=>{if(!e)return;if(e[t])return Object.prototype.hasOwnProperty.call(e,t)?e[t]:void 0;const s=t.split(n);let r=e;for(let i=0;i-1&&le?.replace(/_/g,"-"),sg={type:"logger",log(e){this.output("log",e)},warn(e){this.output("warn",e)},error(e){this.output("error",e)},output(e,t){console?.[e]?.apply?.(console,t)}};class fr{constructor(t,n={}){this.init(t,n)}init(t,n={}){this.prefix=n.prefix||"i18next:",this.logger=t||sg,this.options=n,this.debug=n.debug}log(...t){return this.forward(t,"log","",!0)}warn(...t){return this.forward(t,"warn","",!0)}error(...t){return this.forward(t,"error","")}deprecate(...t){return this.forward(t,"warn","WARNING DEPRECATED: ",!0)}forward(t,n,s,r){return r&&!this.debug?null:(Y(t[0])&&(t[0]=`${s}${this.prefix} ${t[0]}`),this.logger[n](t))}create(t){return new fr(this.logger,{prefix:`${this.prefix}:${t}:`,...this.options})}clone(t){return t=t||this.options,t.prefix=t.prefix||this.prefix,new fr(this.logger,t)}}var wt=new fr;class Tr{constructor(){this.observers={}}on(t,n){return t.split(" ").forEach(s=>{this.observers[s]||(this.observers[s]=new Map);const r=this.observers[s].get(n)||0;this.observers[s].set(n,r+1)}),this}off(t,n){if(this.observers[t]){if(!n){delete this.observers[t];return}this.observers[t].delete(n)}}emit(t,...n){this.observers[t]&&Array.from(this.observers[t].entries()).forEach(([r,i])=>{for(let o=0;o{for(let o=0;o-1&&this.options.ns.splice(n,1)}getResource(t,n,s,r={}){const i=r.keySeparator!==void 0?r.keySeparator:this.options.keySeparator,o=r.ignoreJSONStructure!==void 0?r.ignoreJSONStructure:this.options.ignoreJSONStructure;let a;t.indexOf(".")>-1?a=t.split("."):(a=[t,n],s&&(Array.isArray(s)?a.push(...s):Y(s)&&i?a.push(...s.split(i)):a.push(s)));const l=dr(this.data,a);return!l&&!n&&!s&&t.indexOf(".")>-1&&(t=a[0],n=a[1],s=a.slice(2).join(".")),l||!o||!Y(s)?l:Vi(this.data?.[t]?.[n],s,i)}addResource(t,n,s,r,i={silent:!1}){const o=i.keySeparator!==void 0?i.keySeparator:this.options.keySeparator;let a=[t,n];s&&(a=a.concat(o?s.split(o):s)),t.indexOf(".")>-1&&(a=t.split("."),r=n,n=a[1]),this.addNamespaces(n),Ta(this.data,a,r),i.silent||this.emit("added",t,n,s,r)}addResources(t,n,s,r={silent:!1}){for(const i in s)(Y(s[i])||Array.isArray(s[i]))&&this.addResource(t,n,i,s[i],{silent:!0});r.silent||this.emit("added",t,n,s)}addResourceBundle(t,n,s,r,i,o={silent:!1,skipCopy:!1}){let a=[t,n];t.indexOf(".")>-1&&(a=t.split("."),r=s,s=n,n=a[1]),this.addNamespaces(n);let l=dr(this.data,a)||{};o.skipCopy||(s=JSON.parse(JSON.stringify(s))),r?au(l,s,i):l={...l,...s},Ta(this.data,a,l),o.silent||this.emit("added",t,n,s)}removeResourceBundle(t,n){this.hasResourceBundle(t,n)&&delete this.data[t][n],this.removeNamespaces(n),this.emit("removed",t,n)}hasResourceBundle(t,n){return this.getResource(t,n)!==void 0}getResourceBundle(t,n){return n||(n=this.options.defaultNS),this.getResource(t,n)}getDataByLanguage(t){return this.data[t]}hasLanguageSomeTranslations(t){const n=this.getDataByLanguage(t);return!!(n&&Object.keys(n)||[]).find(r=>n[r]&&Object.keys(n[r]).length>0)}toJSON(){return this.data}}var lu={processors:{},addPostProcessor(e){this.processors[e.name]=e},handle(e,t,n,s,r){return e.forEach(i=>{t=this.processors[i]?.process(t,n,s,r)??t}),t}};const cu=Symbol("i18next/PATH_KEY");function rg(){const e=[],t=Object.create(null);let n;return t.get=(s,r)=>(n?.revoke?.(),r===cu?e:(e.push(r),n=Proxy.revocable(s,t),n.proxy)),Proxy.revocable(Object.create(null),t).proxy}function Bn(e,t){const{[cu]:n}=e(rg()),s=t?.keySeparator??".",r=t?.nsSeparator??":";if(n.length>1&&r){const i=t?.ns,o=Array.isArray(i)?i:null;if(o&&o.length>1&&o.slice(1).includes(n[0]))return`${n[0]}${r}${n.slice(1).join(s)}`}return n.join(s)}const Aa={},ti=e=>!Y(e)&&typeof e!="boolean"&&typeof e!="number";class hr extends Tr{constructor(t,n={}){super(),Wh(["resourceStore","languageUtils","pluralResolver","interpolator","backendConnector","i18nFormat","utils"],t,this),this.options=n,this.options.keySeparator===void 0&&(this.options.keySeparator="."),this.logger=wt.create("translator")}changeLanguage(t){t&&(this.language=t)}exists(t,n={interpolation:{}}){const s={...n};if(t==null)return!1;const r=this.resolve(t,s);if(r?.res===void 0)return!1;const i=ti(r.res);return!(s.returnObjects===!1&&i)}extractFromKey(t,n){let s=n.nsSeparator!==void 0?n.nsSeparator:this.options.nsSeparator;s===void 0&&(s=":");const r=n.keySeparator!==void 0?n.keySeparator:this.options.keySeparator;let i=n.ns||this.options.defaultNS||[];const o=s&&t.indexOf(s)>-1,a=!this.options.userDefinedKeySeparator&&!n.keySeparator&&!this.options.userDefinedNsSeparator&&!n.nsSeparator&&!ng(t,s,r);if(o&&!a){const l=t.match(this.interpolator.nestingRegexp);if(l&&l.length>0)return{key:t,namespaces:Y(i)?[i]:i};const c=t.split(s);(s!==r||s===r&&this.options.ns.indexOf(c[0])>-1)&&(i=c.shift()),t=c.join(r)}return{key:t,namespaces:Y(i)?[i]:i}}translate(t,n,s){let r=typeof n=="object"?{...n}:n;if(typeof r!="object"&&this.options.overloadTranslationOptionHandler&&(r=this.options.overloadTranslationOptionHandler(arguments)),typeof r=="object"&&(r={...r}),r||(r={}),t==null)return"";typeof t=="function"&&(t=Bn(t,{...this.options,...r})),Array.isArray(t)||(t=[String(t)]),t=t.map(T=>typeof T=="function"?Bn(T,{...this.options,...r}):String(T));const i=r.returnDetails!==void 0?r.returnDetails:this.options.returnDetails,o=r.keySeparator!==void 0?r.keySeparator:this.options.keySeparator,{key:a,namespaces:l}=this.extractFromKey(t[t.length-1],r),c=l[l.length-1];let d=r.nsSeparator!==void 0?r.nsSeparator:this.options.nsSeparator;d===void 0&&(d=":");const u=r.lng||this.language,f=r.appendNamespaceToCIMode||this.options.appendNamespaceToCIMode;if(u?.toLowerCase()==="cimode")return f?i?{res:`${c}${d}${a}`,usedKey:a,exactUsedKey:a,usedLng:u,usedNS:c,usedParams:this.getUsedParamsDetails(r)}:`${c}${d}${a}`:i?{res:a,usedKey:a,exactUsedKey:a,usedLng:u,usedNS:c,usedParams:this.getUsedParamsDetails(r)}:a;const h=this.resolve(t,r);let g=h?.res;const m=h?.usedKey||a,y=h?.exactUsedKey||a,v=["[object Number]","[object Function]","[object RegExp]"],b=r.joinArrays!==void 0?r.joinArrays:this.options.joinArrays,w=!this.i18nFormat||this.i18nFormat.handleAsObject,S=r.count!==void 0&&!Y(r.count),x=hr.hasDefaultValue(r),k=S?this.pluralResolver.getSuffix(u,r.count,r):"",P=r.ordinal&&S?this.pluralResolver.getSuffix(u,r.count,{ordinal:!1}):"",E=S&&!r.ordinal&&r.count===0,O=E&&r[`defaultValue${this.options.pluralSeparator}zero`]||r[`defaultValue${k}`]||r[`defaultValue${P}`]||r.defaultValue;let $=g;w&&!g&&x&&($=O);const C=ti($),I=Object.prototype.toString.apply($);if(w&&$&&C&&v.indexOf(I)<0&&!(Y(b)&&Array.isArray($))){if(!r.returnObjects&&!this.options.returnObjects){this.options.returnedObjectHandler||this.logger.warn("accessing an object - but returnObjects options is not enabled!");const T=this.options.returnedObjectHandler?this.options.returnedObjectHandler(m,$,{...r,ns:l}):`key '${a} (${this.language})' returned an object instead of string.`;return i?(h.res=T,h.usedParams=this.getUsedParamsDetails(r),h):T}if(o){const T=Array.isArray($),L=T?[]:{},M=T?y:m;for(const W in $)if(Object.prototype.hasOwnProperty.call($,W)){const H=`${M}${o}${W}`;x&&!g?L[W]=this.translate(H,{...r,defaultValue:ti(O)?O[W]:void 0,joinArrays:!1,ns:l}):L[W]=this.translate(H,{...r,joinArrays:!1,ns:l}),L[W]===H&&(L[W]=$[W])}g=L}}else if(w&&Y(b)&&Array.isArray(g))g=g.join(b),g&&(g=this.extendTranslation(g,t,r,s));else{let T=!1,L=!1;!this.isValidLookup(g)&&x&&(T=!0,g=O),this.isValidLookup(g)||(L=!0,g=a);const W=(r.missingKeyNoValueFallbackToKey||this.options.missingKeyNoValueFallbackToKey)&&L?void 0:g,H=x&&O!==g&&this.options.updateMissing;if(L||T||H){if(this.logger.log(H?"updateKey":"missingKey",u,c,a,H?O:g),o){const V=this.resolve(a,{...r,keySeparator:!1});V&&V.res&&this.logger.warn("Seems the loaded translations were in flat JSON format instead of nested. Either set keySeparator: false on init or make sure your translations are published in nested format.")}let se=[];const ne=this.languageUtils.getFallbackCodes(this.options.fallbackLng,r.lng||this.language);if(this.options.saveMissingTo==="fallback"&&ne&&ne[0])for(let V=0;V{const we=x&&ie!==g?ie:W;this.options.missingKeyHandler?this.options.missingKeyHandler(V,c,z,we,H,r):this.backendConnector?.saveMissing&&this.backendConnector.saveMissing(V,c,z,we,H,r),this.emit("missingKey",V,c,z,g)};this.options.saveMissing&&(this.options.saveMissingPlurals&&S?se.forEach(V=>{const z=this.pluralResolver.getSuffixes(V,r);E&&r[`defaultValue${this.options.pluralSeparator}zero`]&&z.indexOf(`${this.options.pluralSeparator}zero`)<0&&z.push(`${this.options.pluralSeparator}zero`),z.forEach(ie=>{_e([V],a+ie,r[`defaultValue${ie}`]||O)})}):_e(se,a,O))}g=this.extendTranslation(g,t,r,h,s),L&&g===a&&this.options.appendNamespaceToMissingKey&&(g=`${c}${d}${a}`),(L||T)&&this.options.parseMissingKeyHandler&&(g=this.options.parseMissingKeyHandler(this.options.appendNamespaceToMissingKey?`${c}${d}${a}`:a,T?g:void 0,r))}return i?(h.res=g,h.usedParams=this.getUsedParamsDetails(r),h):g}extendTranslation(t,n,s,r,i){if(this.i18nFormat?.parse)t=this.i18nFormat.parse(t,{...this.options.interpolation.defaultVariables,...s},s.lng||this.language||r.usedLng,r.usedNS,r.usedKey,{resolved:r});else if(!s.skipInterpolation){s.interpolation&&this.interpolator.init({...s,interpolation:{...this.options.interpolation,...s.interpolation}});const l=Y(t)&&(s?.interpolation?.skipOnVariables!==void 0?s.interpolation.skipOnVariables:this.options.interpolation.skipOnVariables);let c;if(l){const u=t.match(this.interpolator.nestingRegexp);c=u&&u.length}let d=s.replace&&!Y(s.replace)?s.replace:s;if(this.options.interpolation.defaultVariables&&(d={...this.options.interpolation.defaultVariables,...d}),t=this.interpolator.interpolate(t,d,s.lng||this.language||r.usedLng,s),l){const u=t.match(this.interpolator.nestingRegexp),f=u&&u.length;ci?.[0]===u[0]&&!s.context?(this.logger.warn(`It seems you are nesting recursively key: ${u[0]} in key: ${n[0]}`),null):this.translate(...u,n),s)),s.interpolation&&this.interpolator.reset()}const o=s.postProcess||this.options.postProcess,a=Y(o)?[o]:o;return t!=null&&a?.length&&s.applyPostProcessor!==!1&&(t=lu.handle(a,t,n,this.options&&this.options.postProcessPassResolved?{i18nResolved:{...r,usedParams:this.getUsedParamsDetails(s)},...s}:s,this)),t}resolve(t,n={}){let s,r,i,o,a;return Y(t)&&(t=[t]),Array.isArray(t)&&(t=t.map(l=>typeof l=="function"?Bn(l,{...this.options,...n}):l)),t.forEach(l=>{if(this.isValidLookup(s))return;const c=this.extractFromKey(l,n),d=c.key;r=d;let u=c.namespaces;this.options.fallbackNS&&(u=u.concat(this.options.fallbackNS));const f=n.count!==void 0&&!Y(n.count),h=f&&!n.ordinal&&n.count===0,g=n.context!==void 0&&(Y(n.context)||typeof n.context=="number")&&n.context!=="",m=n.lngs?n.lngs:this.languageUtils.toResolveHierarchy(n.lng||this.language,n.fallbackLng);u.forEach(y=>{this.isValidLookup(s)||(a=y,!Aa[`${m[0]}-${y}`]&&this.utils?.hasLoadedNamespace&&!this.utils?.hasLoadedNamespace(a)&&(Aa[`${m[0]}-${y}`]=!0,this.logger.warn(`key "${r}" for languages "${m.join(", ")}" won't get resolved as namespace "${a}" was not yet loaded`,"This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!")),m.forEach(v=>{if(this.isValidLookup(s))return;o=v;const b=[d];if(this.i18nFormat?.addLookupKeys)this.i18nFormat.addLookupKeys(b,d,v,y,n);else{let S;f&&(S=this.pluralResolver.getSuffix(v,n.count,n));const x=`${this.options.pluralSeparator}zero`,k=`${this.options.pluralSeparator}ordinal${this.options.pluralSeparator}`;if(f&&(n.ordinal&&S.indexOf(k)===0&&b.push(d+S.replace(k,this.options.pluralSeparator)),b.push(d+S),h&&b.push(d+x)),g){const P=`${d}${this.options.contextSeparator||"_"}${n.context}`;b.push(P),f&&(n.ordinal&&S.indexOf(k)===0&&b.push(P+S.replace(k,this.options.pluralSeparator)),b.push(P+S),h&&b.push(P+x))}}let w;for(;w=b.pop();)this.isValidLookup(s)||(i=w,s=this.getResource(v,y,w,n))}))})}),{res:s,usedKey:r,exactUsedKey:i,usedLng:o,usedNS:a}}isValidLookup(t){return t!==void 0&&!(!this.options.returnNull&&t===null)&&!(!this.options.returnEmptyString&&t==="")}getResource(t,n,s,r={}){return this.i18nFormat?.getResource?this.i18nFormat.getResource(t,n,s,r):this.resourceStore.getResource(t,n,s,r)}getUsedParamsDetails(t={}){const n=["defaultValue","ordinal","context","replace","lng","lngs","fallbackLng","ns","keySeparator","nsSeparator","returnObjects","returnDetails","joinArrays","postProcess","interpolation"],s=t.replace&&!Y(t.replace);let r=s?t.replace:t;if(s&&typeof t.count<"u"&&(r.count=t.count),this.options.interpolation.defaultVariables&&(r={...this.options.interpolation.defaultVariables,...r}),!s){r={...r};for(const i of n)delete r[i]}return r}static hasDefaultValue(t){const n="defaultValue";for(const s in t)if(Object.prototype.hasOwnProperty.call(t,s)&&n===s.substring(0,n.length)&&t[s]!==void 0)return!0;return!1}}class Ma{constructor(t){this.options=t,this.supportedLngs=this.options.supportedLngs||!1,this.logger=wt.create("languageUtils")}getScriptPartFromCode(t){if(t=Cs(t),!t||t.indexOf("-")<0)return null;const n=t.split("-");return n.length===2||(n.pop(),n[n.length-1].toLowerCase()==="x")?null:this.formatLanguageCode(n.join("-"))}getLanguagePartFromCode(t){if(t=Cs(t),!t||t.indexOf("-")<0)return t;const n=t.split("-");return this.formatLanguageCode(n[0])}formatLanguageCode(t){if(Y(t)&&t.indexOf("-")>-1){let n;try{n=Intl.getCanonicalLocales(t)[0]}catch{}return n&&this.options.lowerCaseLng&&(n=n.toLowerCase()),n||(this.options.lowerCaseLng?t.toLowerCase():t)}return this.options.cleanCode||this.options.lowerCaseLng?t.toLowerCase():t}isSupportedCode(t){return(this.options.load==="languageOnly"||this.options.nonExplicitSupportedLngs)&&(t=this.getLanguagePartFromCode(t)),!this.supportedLngs||!this.supportedLngs.length||this.supportedLngs.indexOf(t)>-1}getBestMatchFromCodes(t){if(!t)return null;let n;return t.forEach(s=>{if(n)return;const r=this.formatLanguageCode(s);(!this.options.supportedLngs||this.isSupportedCode(r))&&(n=r)}),!n&&this.options.supportedLngs&&t.forEach(s=>{if(n)return;const r=this.getScriptPartFromCode(s);if(this.isSupportedCode(r))return n=r;const i=this.getLanguagePartFromCode(s);if(this.isSupportedCode(i))return n=i;n=this.options.supportedLngs.find(o=>{if(o===i)return o;if(!(o.indexOf("-")<0&&i.indexOf("-")<0)&&(o.indexOf("-")>0&&i.indexOf("-")<0&&o.substring(0,o.indexOf("-"))===i||o.indexOf(i)===0&&i.length>1))return o})}),n||(n=this.getFallbackCodes(this.options.fallbackLng)[0]),n}getFallbackCodes(t,n){if(!t)return[];if(typeof t=="function"&&(t=t(n)),Y(t)&&(t=[t]),Array.isArray(t))return t;if(!n)return t.default||[];let s=t[n];return s||(s=t[this.getScriptPartFromCode(n)]),s||(s=t[this.formatLanguageCode(n)]),s||(s=t[this.getLanguagePartFromCode(n)]),s||(s=t.default),s||[]}toResolveHierarchy(t,n){const s=this.getFallbackCodes((n===!1?[]:n)||this.options.fallbackLng||[],t),r=[],i=o=>{o&&(this.isSupportedCode(o)?r.push(o):this.logger.warn(`rejecting language code not found in supportedLngs: ${o}`))};return Y(t)&&(t.indexOf("-")>-1||t.indexOf("_")>-1)?(this.options.load!=="languageOnly"&&i(this.formatLanguageCode(t)),this.options.load!=="languageOnly"&&this.options.load!=="currentOnly"&&i(this.getScriptPartFromCode(t)),this.options.load!=="currentOnly"&&i(this.getLanguagePartFromCode(t))):Y(t)&&i(this.formatLanguageCode(t)),s.forEach(o=>{r.indexOf(o)<0&&i(this.formatLanguageCode(o))}),r}}const Fa={zero:0,one:1,two:2,few:3,many:4,other:5},Da={select:e=>e===1?"one":"other",resolvedOptions:()=>({pluralCategories:["one","other"]})};class ig{constructor(t,n={}){this.languageUtils=t,this.options=n,this.logger=wt.create("pluralResolver"),this.pluralRulesCache={}}clearCache(){this.pluralRulesCache={}}getRule(t,n={}){const s=Cs(t==="dev"?"en":t),r=n.ordinal?"ordinal":"cardinal",i=JSON.stringify({cleanedCode:s,type:r});if(i in this.pluralRulesCache)return this.pluralRulesCache[i];let o;try{o=new Intl.PluralRules(s,{type:r})}catch{if(typeof Intl>"u")return this.logger.error("No Intl support, please use an Intl polyfill!"),Da;if(!t.match(/-|_/))return Da;const l=this.languageUtils.getLanguagePartFromCode(t);o=this.getRule(l,n)}return this.pluralRulesCache[i]=o,o}needsPlural(t,n={}){let s=this.getRule(t,n);return s||(s=this.getRule("dev",n)),s?.resolvedOptions().pluralCategories.length>1}getPluralFormsOfKey(t,n,s={}){return this.getSuffixes(t,s).map(r=>`${n}${r}`)}getSuffixes(t,n={}){let s=this.getRule(t,n);return s||(s=this.getRule("dev",n)),s?s.resolvedOptions().pluralCategories.sort((r,i)=>Fa[r]-Fa[i]).map(r=>`${this.options.prepend}${n.ordinal?`ordinal${this.options.prepend}`:""}${r}`):[]}getSuffix(t,n,s={}){const r=this.getRule(t,s);return r?`${this.options.prepend}${s.ordinal?`ordinal${this.options.prepend}`:""}${r.select(n)}`:(this.logger.warn(`no plural rule found for: ${t}`),this.getSuffix("dev",n,s))}}const Na=(e,t,n,s=".",r=!0)=>{let i=Jh(e,t,n);return!i&&r&&Y(n)&&(i=Vi(e,n,s),i===void 0&&(i=Vi(t,n,s))),i},ni=e=>e.replace(/\$/g,"$$$$");class ja{constructor(t={}){this.logger=wt.create("interpolator"),this.options=t,this.format=t?.interpolation?.format||(n=>n),this.init(t)}init(t={}){t.interpolation||(t.interpolation={escapeValue:!0});const{escape:n,escapeValue:s,useRawValueToEscape:r,prefix:i,prefixEscaped:o,suffix:a,suffixEscaped:l,formatSeparator:c,unescapeSuffix:d,unescapePrefix:u,nestingPrefix:f,nestingPrefixEscaped:h,nestingSuffix:g,nestingSuffixEscaped:m,nestingOptionsSeparator:y,maxReplaces:v,alwaysFormat:b}=t.interpolation;this.escape=n!==void 0?n:Xh,this.escapeValue=s!==void 0?s:!0,this.useRawValueToEscape=r!==void 0?r:!1,this.prefix=i?rn(i):o||"{{",this.suffix=a?rn(a):l||"}}",this.formatSeparator=c||",",this.unescapePrefix=d?"":u||"-",this.unescapeSuffix=this.unescapePrefix?"":d||"",this.nestingPrefix=f?rn(f):h||rn("$t("),this.nestingSuffix=g?rn(g):m||rn(")"),this.nestingOptionsSeparator=y||",",this.maxReplaces=v||1e3,this.alwaysFormat=b!==void 0?b:!1,this.resetRegExp()}reset(){this.options&&this.init(this.options)}resetRegExp(){const t=(n,s)=>n?.source===s?(n.lastIndex=0,n):new RegExp(s,"g");this.regexp=t(this.regexp,`${this.prefix}(.+?)${this.suffix}`),this.regexpUnescape=t(this.regexpUnescape,`${this.prefix}${this.unescapePrefix}(.+?)${this.unescapeSuffix}${this.suffix}`),this.nestingRegexp=t(this.nestingRegexp,`${this.nestingPrefix}((?:[^()"']+|"[^"]*"|'[^']*'|\\((?:[^()]|"[^"]*"|'[^']*')*\\))*?)${this.nestingSuffix}`)}interpolate(t,n,s,r){let i,o,a;const l=this.options&&this.options.interpolation&&this.options.interpolation.defaultVariables||{},c=h=>{if(h.indexOf(this.formatSeparator)<0){const v=Na(n,l,h,this.options.keySeparator,this.options.ignoreJSONStructure);return this.alwaysFormat?this.format(v,void 0,s,{...r,...n,interpolationkey:h}):v}const g=h.split(this.formatSeparator),m=g.shift().trim(),y=g.join(this.formatSeparator).trim();return this.format(Na(n,l,m,this.options.keySeparator,this.options.ignoreJSONStructure),y,s,{...r,...n,interpolationkey:m})};this.resetRegExp();const d=r?.missingInterpolationHandler||this.options.missingInterpolationHandler,u=r?.interpolation?.skipOnVariables!==void 0?r.interpolation.skipOnVariables:this.options.interpolation.skipOnVariables;return[{regex:this.regexpUnescape,safeValue:h=>ni(h)},{regex:this.regexp,safeValue:h=>this.escapeValue?ni(this.escape(h)):ni(h)}].forEach(h=>{for(a=0;i=h.regex.exec(t);){const g=i[1].trim();if(o=c(g),o===void 0)if(typeof d=="function"){const y=d(t,i,r);o=Y(y)?y:""}else if(r&&Object.prototype.hasOwnProperty.call(r,g))o="";else if(u){o=i[0];continue}else this.logger.warn(`missed to pass in variable ${g} for interpolating ${t}`),o="";else!Y(o)&&!this.useRawValueToEscape&&(o=Ra(o));const m=h.safeValue(o);if(t=t.replace(i[0],m),u?(h.regex.lastIndex+=o.length,h.regex.lastIndex-=i[0].length):h.regex.lastIndex=0,a++,a>=this.maxReplaces)break}}),t}nest(t,n,s={}){let r,i,o;const a=(l,c)=>{const d=this.nestingOptionsSeparator;if(l.indexOf(d)<0)return l;const u=l.split(new RegExp(`${rn(d)}[ ]*{`));let f=`{${u[1]}`;l=u[0],f=this.interpolate(f,o);const h=f.match(/'/g),g=f.match(/"/g);((h?.length??0)%2===0&&!g||(g?.length??0)%2!==0)&&(f=f.replace(/'/g,'"'));try{o=JSON.parse(f),c&&(o={...c,...o})}catch(m){return this.logger.warn(`failed parsing options string in nesting for key ${l}`,m),`${l}${d}${f}`}return o.defaultValue&&o.defaultValue.indexOf(this.prefix)>-1&&delete o.defaultValue,l};for(;r=this.nestingRegexp.exec(t);){let l=[];o={...s},o=o.replace&&!Y(o.replace)?o.replace:o,o.applyPostProcessor=!1,delete o.defaultValue;const c=/{.*}/.test(r[1])?r[1].lastIndexOf("}")+1:r[1].indexOf(this.formatSeparator);if(c!==-1&&(l=r[1].slice(c).split(this.formatSeparator).map(d=>d.trim()).filter(Boolean),r[1]=r[1].slice(0,c)),i=n(a.call(this,r[1].trim(),o),o),i&&r[0]===t&&!Y(i))return i;Y(i)||(i=Ra(i)),i||(this.logger.warn(`missed to resolve ${r[1]} for nesting ${t}`),i=""),l.length&&(i=l.reduce((d,u)=>this.format(d,u,s.lng,{...s,interpolationkey:r[1].trim()}),i.trim())),t=t.replace(r[0],i),this.regexp.lastIndex=0}return t}}const og=e=>{let t=e.toLowerCase().trim();const n={};if(e.indexOf("(")>-1){const s=e.split("(");t=s[0].toLowerCase().trim();const r=s[1].substring(0,s[1].length-1);t==="currency"&&r.indexOf(":")<0?n.currency||(n.currency=r.trim()):t==="relativetime"&&r.indexOf(":")<0?n.range||(n.range=r.trim()):r.split(";").forEach(o=>{if(o){const[a,...l]=o.split(":"),c=l.join(":").trim().replace(/^'+|'+$/g,""),d=a.trim();n[d]||(n[d]=c),c==="false"&&(n[d]=!1),c==="true"&&(n[d]=!0),isNaN(c)||(n[d]=parseInt(c,10))}})}return{formatName:t,formatOptions:n}},Ka=e=>{const t={};return(n,s,r)=>{let i=r;r&&r.interpolationkey&&r.formatParams&&r.formatParams[r.interpolationkey]&&r[r.interpolationkey]&&(i={...i,[r.interpolationkey]:void 0});const o=s+JSON.stringify(i);let a=t[o];return a||(a=e(Cs(s),r),t[o]=a),a(n)}},ag=e=>(t,n,s)=>e(Cs(n),s)(t);class lg{constructor(t={}){this.logger=wt.create("formatter"),this.options=t,this.init(t)}init(t,n={interpolation:{}}){this.formatSeparator=n.interpolation.formatSeparator||",";const s=n.cacheInBuiltFormats?Ka:ag;this.formats={number:s((r,i)=>{const o=new Intl.NumberFormat(r,{...i});return a=>o.format(a)}),currency:s((r,i)=>{const o=new Intl.NumberFormat(r,{...i,style:"currency"});return a=>o.format(a)}),datetime:s((r,i)=>{const o=new Intl.DateTimeFormat(r,{...i});return a=>o.format(a)}),relativetime:s((r,i)=>{const o=new Intl.RelativeTimeFormat(r,{...i});return a=>o.format(a,i.range||"day")}),list:s((r,i)=>{const o=new Intl.ListFormat(r,{...i});return a=>o.format(a)})}}add(t,n){this.formats[t.toLowerCase().trim()]=n}addCached(t,n){this.formats[t.toLowerCase().trim()]=Ka(n)}format(t,n,s,r={}){const i=n.split(this.formatSeparator);if(i.length>1&&i[0].indexOf("(")>1&&i[0].indexOf(")")<0&&i.find(a=>a.indexOf(")")>-1)){const a=i.findIndex(l=>l.indexOf(")")>-1);i[0]=[i[0],...i.splice(1,a)].join(this.formatSeparator)}return i.reduce((a,l)=>{const{formatName:c,formatOptions:d}=og(l);if(this.formats[c]){let u=a;try{const f=r?.formatParams?.[r.interpolationkey]||{},h=f.locale||f.lng||r.locale||r.lng||s;u=this.formats[c](a,h,{...d,...r,...f})}catch(f){this.logger.warn(f)}return u}else this.logger.warn(`there was no format function for ${c}`);return a},t)}}const cg=(e,t)=>{e.pending[t]!==void 0&&(delete e.pending[t],e.pendingCount--)};class ug extends Tr{constructor(t,n,s,r={}){super(),this.backend=t,this.store=n,this.services=s,this.languageUtils=s.languageUtils,this.options=r,this.logger=wt.create("backendConnector"),this.waitingReads=[],this.maxParallelReads=r.maxParallelReads||10,this.readingCalls=0,this.maxRetries=r.maxRetries>=0?r.maxRetries:5,this.retryTimeout=r.retryTimeout>=1?r.retryTimeout:350,this.state={},this.queue=[],this.backend?.init?.(s,r.backend,r)}queueLoad(t,n,s,r){const i={},o={},a={},l={};return t.forEach(c=>{let d=!0;n.forEach(u=>{const f=`${c}|${u}`;!s.reload&&this.store.hasResourceBundle(c,u)?this.state[f]=2:this.state[f]<0||(this.state[f]===1?o[f]===void 0&&(o[f]=!0):(this.state[f]=1,d=!1,o[f]===void 0&&(o[f]=!0),i[f]===void 0&&(i[f]=!0),l[u]===void 0&&(l[u]=!0)))}),d||(a[c]=!0)}),(Object.keys(i).length||Object.keys(o).length)&&this.queue.push({pending:o,pendingCount:Object.keys(o).length,loaded:{},errors:[],callback:r}),{toLoad:Object.keys(i),pending:Object.keys(o),toLoadLanguages:Object.keys(a),toLoadNamespaces:Object.keys(l)}}loaded(t,n,s){const r=t.split("|"),i=r[0],o=r[1];n&&this.emit("failedLoading",i,o,n),!n&&s&&this.store.addResourceBundle(i,o,s,void 0,void 0,{skipCopy:!0}),this.state[t]=n?-1:2,n&&s&&(this.state[t]=0);const a={};this.queue.forEach(l=>{Gh(l.loaded,[i],o),cg(l,t),n&&l.errors.push(n),l.pendingCount===0&&!l.done&&(Object.keys(l.loaded).forEach(c=>{a[c]||(a[c]={});const d=l.loaded[c];d.length&&d.forEach(u=>{a[c][u]===void 0&&(a[c][u]=!0)})}),l.done=!0,l.errors.length?l.callback(l.errors):l.callback())}),this.emit("loaded",a),this.queue=this.queue.filter(l=>!l.done)}read(t,n,s,r=0,i=this.retryTimeout,o){if(!t.length)return o(null,{});if(this.readingCalls>=this.maxParallelReads){this.waitingReads.push({lng:t,ns:n,fcName:s,tried:r,wait:i,callback:o});return}this.readingCalls++;const a=(c,d)=>{if(this.readingCalls--,this.waitingReads.length>0){const u=this.waitingReads.shift();this.read(u.lng,u.ns,u.fcName,u.tried,u.wait,u.callback)}if(c&&d&&r{this.read.call(this,t,n,s,r+1,i*2,o)},i);return}o(c,d)},l=this.backend[s].bind(this.backend);if(l.length===2){try{const c=l(t,n);c&&typeof c.then=="function"?c.then(d=>a(null,d)).catch(a):a(null,c)}catch(c){a(c)}return}return l(t,n,a)}prepareLoading(t,n,s={},r){if(!this.backend)return this.logger.warn("No backend was added via i18next.use. Will not load resources."),r&&r();Y(t)&&(t=this.languageUtils.toResolveHierarchy(t)),Y(n)&&(n=[n]);const i=this.queueLoad(t,n,s,r);if(!i.toLoad.length)return i.pending.length||r(),null;i.toLoad.forEach(o=>{this.loadOne(o)})}load(t,n,s){this.prepareLoading(t,n,{},s)}reload(t,n,s){this.prepareLoading(t,n,{reload:!0},s)}loadOne(t,n=""){const s=t.split("|"),r=s[0],i=s[1];this.read(r,i,"read",void 0,void 0,(o,a)=>{o&&this.logger.warn(`${n}loading namespace ${i} for language ${r} failed`,o),!o&&a&&this.logger.log(`${n}loaded namespace ${i} for language ${r}`,a),this.loaded(t,o,a)})}saveMissing(t,n,s,r,i,o={},a=()=>{}){if(this.services?.utils?.hasLoadedNamespace&&!this.services?.utils?.hasLoadedNamespace(n)){this.logger.warn(`did not save key "${s}" as the namespace "${n}" was not yet loaded`,"This means something IS WRONG in your setup. You access the t function before i18next.init / i18next.loadNamespace / i18next.changeLanguage was done. Wait for the callback or Promise to resolve before accessing it!!!");return}if(!(s==null||s==="")){if(this.backend?.create){const l={...o,isUpdate:i},c=this.backend.create.bind(this.backend);if(c.length<6)try{let d;c.length===5?d=c(t,n,s,r,l):d=c(t,n,s,r),d&&typeof d.then=="function"?d.then(u=>a(null,u)).catch(a):a(null,d)}catch(d){a(d)}else c(t,n,s,r,a,l)}!t||!t[0]||this.store.addResource(t[0],n,s,r)}}}const si=()=>({debug:!1,initAsync:!0,ns:["translation"],defaultNS:["translation"],fallbackLng:["dev"],fallbackNS:!1,supportedLngs:!1,nonExplicitSupportedLngs:!1,load:"all",preload:!1,simplifyPluralSuffix:!0,keySeparator:".",nsSeparator:":",pluralSeparator:"_",contextSeparator:"_",partialBundledLanguages:!1,saveMissing:!1,updateMissing:!1,saveMissingTo:"fallback",saveMissingPlurals:!0,missingKeyHandler:!1,missingInterpolationHandler:!1,postProcess:!1,postProcessPassResolved:!1,returnNull:!1,returnEmptyString:!0,returnObjects:!1,joinArrays:!1,returnedObjectHandler:!1,parseMissingKeyHandler:!1,appendNamespaceToMissingKey:!1,appendNamespaceToCIMode:!1,overloadTranslationOptionHandler:e=>{let t={};if(typeof e[1]=="object"&&(t=e[1]),Y(e[1])&&(t.defaultValue=e[1]),Y(e[2])&&(t.tDescription=e[2]),typeof e[2]=="object"||typeof e[3]=="object"){const n=e[3]||e[2];Object.keys(n).forEach(s=>{t[s]=n[s]})}return t},interpolation:{escapeValue:!0,format:e=>e,prefix:"{{",suffix:"}}",formatSeparator:",",unescapePrefix:"-",nestingPrefix:"$t(",nestingSuffix:")",nestingOptionsSeparator:",",maxReplaces:1e3,skipOnVariables:!0},cacheInBuiltFormats:!0}),Ba=e=>(Y(e.ns)&&(e.ns=[e.ns]),Y(e.fallbackLng)&&(e.fallbackLng=[e.fallbackLng]),Y(e.fallbackNS)&&(e.fallbackNS=[e.fallbackNS]),e.supportedLngs?.indexOf?.("cimode")<0&&(e.supportedLngs=e.supportedLngs.concat(["cimode"])),typeof e.initImmediate=="boolean"&&(e.initAsync=e.initImmediate),e),Hs=()=>{},dg=e=>{Object.getOwnPropertyNames(Object.getPrototypeOf(e)).forEach(n=>{typeof e[n]=="function"&&(e[n]=e[n].bind(e))})},uu="__i18next_supportNoticeShown",fg=()=>!!(typeof globalThis<"u"&&globalThis[uu]||typeof process<"u"&&Ea&&Ea.I18NEXT_NO_SUPPORT_NOTICE),hg=()=>{typeof globalThis<"u"&&(globalThis[uu]=!0)},gg=e=>!!(e?.modules?.backend?.name?.indexOf("Locize")>0||e?.modules?.backend?.constructor?.name?.indexOf("Locize")>0||e?.options?.backend?.backends&&e.options.backend.backends.some(t=>t?.name?.indexOf("Locize")>0||t?.constructor?.name?.indexOf("Locize")>0)||e?.options?.backend?.projectId||e?.options?.backend?.backendOptions&&e.options.backend.backendOptions.some(t=>t?.projectId));class bs extends Tr{constructor(t={},n){if(super(),this.options=Ba(t),this.services={},this.logger=wt,this.modules={external:[]},dg(this),n&&!this.isInitialized&&!t.isClone){if(!this.options.initAsync)return this.init(t,n),this;setTimeout(()=>{this.init(t,n)},0)}}init(t={},n){this.isInitializing=!0,typeof t=="function"&&(n=t,t={}),t.defaultNS==null&&t.ns&&(Y(t.ns)?t.defaultNS=t.ns:t.ns.indexOf("translation")<0&&(t.defaultNS=t.ns[0]));const s=si();this.options={...s,...this.options,...Ba(t)},this.options.interpolation={...s.interpolation,...this.options.interpolation},t.keySeparator!==void 0&&(this.options.userDefinedKeySeparator=t.keySeparator),t.nsSeparator!==void 0&&(this.options.userDefinedNsSeparator=t.nsSeparator),typeof this.options.overloadTranslationOptionHandler!="function"&&(this.options.overloadTranslationOptionHandler=s.overloadTranslationOptionHandler),this.options.showSupportNotice!==!1&&!gg(this)&&!fg()&&(typeof console<"u"&&typeof console.info<"u"&&console.info("🌐 i18next is made possible by our own product, Locize — consider powering your project with managed localization (AI, CDN, integrations): https://locize.com 💙"),hg());const r=c=>c?typeof c=="function"?new c:c:null;if(!this.options.isClone){this.modules.logger?wt.init(r(this.modules.logger),this.options):wt.init(null,this.options);let c;this.modules.formatter?c=this.modules.formatter:c=lg;const d=new Ma(this.options);this.store=new Ia(this.options.resources,this.options);const u=this.services;u.logger=wt,u.resourceStore=this.store,u.languageUtils=d,u.pluralResolver=new ig(d,{prepend:this.options.pluralSeparator,simplifyPluralSuffix:this.options.simplifyPluralSuffix}),this.options.interpolation.format&&this.options.interpolation.format!==s.interpolation.format&&this.logger.deprecate("init: you are still using the legacy format function, please use the new approach: https://www.i18next.com/translation-function/formatting"),c&&(!this.options.interpolation.format||this.options.interpolation.format===s.interpolation.format)&&(u.formatter=r(c),u.formatter.init&&u.formatter.init(u,this.options),this.options.interpolation.format=u.formatter.format.bind(u.formatter)),u.interpolator=new ja(this.options),u.utils={hasLoadedNamespace:this.hasLoadedNamespace.bind(this)},u.backendConnector=new ug(r(this.modules.backend),u.resourceStore,u,this.options),u.backendConnector.on("*",(h,...g)=>{this.emit(h,...g)}),this.modules.languageDetector&&(u.languageDetector=r(this.modules.languageDetector),u.languageDetector.init&&u.languageDetector.init(u,this.options.detection,this.options)),this.modules.i18nFormat&&(u.i18nFormat=r(this.modules.i18nFormat),u.i18nFormat.init&&u.i18nFormat.init(this)),this.translator=new hr(this.services,this.options),this.translator.on("*",(h,...g)=>{this.emit(h,...g)}),this.modules.external.forEach(h=>{h.init&&h.init(this)})}if(this.format=this.options.interpolation.format,n||(n=Hs),this.options.fallbackLng&&!this.services.languageDetector&&!this.options.lng){const c=this.services.languageUtils.getFallbackCodes(this.options.fallbackLng);c.length>0&&c[0]!=="dev"&&(this.options.lng=c[0])}!this.services.languageDetector&&!this.options.lng&&this.logger.warn("init: no languageDetector is used and no lng is defined"),["getResource","hasResourceBundle","getResourceBundle","getDataByLanguage"].forEach(c=>{this[c]=(...d)=>this.store[c](...d)}),["addResource","addResources","addResourceBundle","removeResourceBundle"].forEach(c=>{this[c]=(...d)=>(this.store[c](...d),this)});const a=rs(),l=()=>{const c=(d,u)=>{this.isInitializing=!1,this.isInitialized&&!this.initializedStoreOnce&&this.logger.warn("init: i18next is already initialized. You should call init just once!"),this.isInitialized=!0,this.options.isClone||this.logger.log("initialized",this.options),this.emit("initialized",this.options),a.resolve(u),n(d,u)};if(this.languages&&!this.isInitialized)return c(null,this.t.bind(this));this.changeLanguage(this.options.lng,c)};return this.options.resources||!this.options.initAsync?l():setTimeout(l,0),a}loadResources(t,n=Hs){let s=n;const r=Y(t)?t:this.language;if(typeof t=="function"&&(s=t),!this.options.resources||this.options.partialBundledLanguages){if(r?.toLowerCase()==="cimode"&&(!this.options.preload||this.options.preload.length===0))return s();const i=[],o=a=>{if(!a||a==="cimode")return;this.services.languageUtils.toResolveHierarchy(a).forEach(c=>{c!=="cimode"&&i.indexOf(c)<0&&i.push(c)})};r?o(r):this.services.languageUtils.getFallbackCodes(this.options.fallbackLng).forEach(l=>o(l)),this.options.preload?.forEach?.(a=>o(a)),this.services.backendConnector.load(i,this.options.ns,a=>{!a&&!this.resolvedLanguage&&this.language&&this.setResolvedLanguage(this.language),s(a)})}else s(null)}reloadResources(t,n,s){const r=rs();return typeof t=="function"&&(s=t,t=void 0),typeof n=="function"&&(s=n,n=void 0),t||(t=this.languages),n||(n=this.options.ns),s||(s=Hs),this.services.backendConnector.reload(t,n,i=>{r.resolve(),s(i)}),r}use(t){if(!t)throw new Error("You are passing an undefined module! Please check the object you are passing to i18next.use()");if(!t.type)throw new Error("You are passing a wrong module! Please check the object you are passing to i18next.use()");return t.type==="backend"&&(this.modules.backend=t),(t.type==="logger"||t.log&&t.warn&&t.error)&&(this.modules.logger=t),t.type==="languageDetector"&&(this.modules.languageDetector=t),t.type==="i18nFormat"&&(this.modules.i18nFormat=t),t.type==="postProcessor"&&lu.addPostProcessor(t),t.type==="formatter"&&(this.modules.formatter=t),t.type==="3rdParty"&&this.modules.external.push(t),this}setResolvedLanguage(t){if(!(!t||!this.languages)&&!(["cimode","dev"].indexOf(t)>-1)){for(let n=0;n-1)&&this.store.hasLanguageSomeTranslations(s)){this.resolvedLanguage=s;break}}!this.resolvedLanguage&&this.languages.indexOf(t)<0&&this.store.hasLanguageSomeTranslations(t)&&(this.resolvedLanguage=t,this.languages.unshift(t))}}changeLanguage(t,n){this.isLanguageChangingTo=t;const s=rs();this.emit("languageChanging",t);const r=a=>{this.language=a,this.languages=this.services.languageUtils.toResolveHierarchy(a),this.resolvedLanguage=void 0,this.setResolvedLanguage(a)},i=(a,l)=>{l?this.isLanguageChangingTo===t&&(r(l),this.translator.changeLanguage(l),this.isLanguageChangingTo=void 0,this.emit("languageChanged",l),this.logger.log("languageChanged",l)):this.isLanguageChangingTo=void 0,s.resolve((...c)=>this.t(...c)),n&&n(a,(...c)=>this.t(...c))},o=a=>{!t&&!a&&this.services.languageDetector&&(a=[]);const l=Y(a)?a:a&&a[0],c=this.store.hasLanguageSomeTranslations(l)?l:this.services.languageUtils.getBestMatchFromCodes(Y(a)?[a]:a);c&&(this.language||r(c),this.translator.language||this.translator.changeLanguage(c),this.services.languageDetector?.cacheUserLanguage?.(c)),this.loadResources(c,d=>{i(d,c)})};return!t&&this.services.languageDetector&&!this.services.languageDetector.async?o(this.services.languageDetector.detect()):!t&&this.services.languageDetector&&this.services.languageDetector.async?this.services.languageDetector.detect.length===0?this.services.languageDetector.detect().then(o):this.services.languageDetector.detect(o):o(t),s}getFixedT(t,n,s){const r=(i,o,...a)=>{let l;typeof o!="object"?l=this.options.overloadTranslationOptionHandler([i,o].concat(a)):l={...o},l.lng=l.lng||r.lng,l.lngs=l.lngs||r.lngs,l.ns=l.ns||r.ns,l.keyPrefix!==""&&(l.keyPrefix=l.keyPrefix||s||r.keyPrefix);const c={...this.options,...l};typeof l.keyPrefix=="function"&&(l.keyPrefix=Bn(l.keyPrefix,c));const d=this.options.keySeparator||".";let u;return l.keyPrefix&&Array.isArray(i)?u=i.map(f=>(typeof f=="function"&&(f=Bn(f,c)),`${l.keyPrefix}${d}${f}`)):(typeof i=="function"&&(i=Bn(i,c)),u=l.keyPrefix?`${l.keyPrefix}${d}${i}`:i),this.t(u,l)};return Y(t)?r.lng=t:r.lngs=t,r.ns=n,r.keyPrefix=s,r}t(...t){return this.translator?.translate(...t)}exists(...t){return this.translator?.exists(...t)}setDefaultNamespace(t){this.options.defaultNS=t}hasLoadedNamespace(t,n={}){if(!this.isInitialized)return this.logger.warn("hasLoadedNamespace: i18next was not initialized",this.languages),!1;if(!this.languages||!this.languages.length)return this.logger.warn("hasLoadedNamespace: i18n.languages were undefined or empty",this.languages),!1;const s=n.lng||this.resolvedLanguage||this.languages[0],r=this.options?this.options.fallbackLng:!1,i=this.languages[this.languages.length-1];if(s.toLowerCase()==="cimode")return!0;const o=(a,l)=>{const c=this.services.backendConnector.state[`${a}|${l}`];return c===-1||c===0||c===2};if(n.precheck){const a=n.precheck(this,o);if(a!==void 0)return a}return!!(this.hasResourceBundle(s,t)||!this.services.backendConnector.backend||this.options.resources&&!this.options.partialBundledLanguages||o(s,t)&&(!r||o(i,t)))}loadNamespaces(t,n){const s=rs();return this.options.ns?(Y(t)&&(t=[t]),t.forEach(r=>{this.options.ns.indexOf(r)<0&&this.options.ns.push(r)}),this.loadResources(r=>{s.resolve(),n&&n(r)}),s):(n&&n(),Promise.resolve())}loadLanguages(t,n){const s=rs();Y(t)&&(t=[t]);const r=this.options.preload||[],i=t.filter(o=>r.indexOf(o)<0&&this.services.languageUtils.isSupportedCode(o));return i.length?(this.options.preload=r.concat(i),this.loadResources(o=>{s.resolve(),n&&n(o)}),s):(n&&n(),Promise.resolve())}dir(t){if(t||(t=this.resolvedLanguage||(this.languages?.length>0?this.languages[0]:this.language)),!t)return"rtl";try{const r=new Intl.Locale(t);if(r&&r.getTextInfo){const i=r.getTextInfo();if(i&&i.direction)return i.direction}}catch{}const n=["ar","shu","sqr","ssh","xaa","yhd","yud","aao","abh","abv","acm","acq","acw","acx","acy","adf","ads","aeb","aec","afb","ajp","apc","apd","arb","arq","ars","ary","arz","auz","avl","ayh","ayl","ayn","ayp","bbz","pga","he","iw","ps","pbt","pbu","pst","prp","prd","ug","ur","ydd","yds","yih","ji","yi","hbo","men","xmn","fa","jpr","peo","pes","prs","dv","sam","ckb"],s=this.services?.languageUtils||new Ma(si());return t.toLowerCase().indexOf("-latn")>1?"ltr":n.indexOf(s.getLanguagePartFromCode(t))>-1||t.toLowerCase().indexOf("-arab")>1?"rtl":"ltr"}static createInstance(t={},n){const s=new bs(t,n);return s.createInstance=bs.createInstance,s}cloneInstance(t={},n=Hs){const s=t.forkResourceStore;s&&delete t.forkResourceStore;const r={...this.options,...t,isClone:!0},i=new bs(r);if((t.debug!==void 0||t.prefix!==void 0)&&(i.logger=i.logger.clone(t)),["store","services","language"].forEach(a=>{i[a]=this[a]}),i.services={...this.services},i.services.utils={hasLoadedNamespace:i.hasLoadedNamespace.bind(i)},s){const a=Object.keys(this.store.data).reduce((l,c)=>(l[c]={...this.store.data[c]},l[c]=Object.keys(l[c]).reduce((d,u)=>(d[u]={...l[c][u]},d),l[c]),l),{});i.store=new Ia(a,r),i.services.resourceStore=i.store}if(t.interpolation){const l={...si().interpolation,...this.options.interpolation,...t.interpolation},c={...r,interpolation:l};i.services.interpolator=new ja(c)}return i.translator=new hr(i.services,r),i.translator.on("*",(a,...l)=>{i.emit(a,...l)}),i.init(r,n),i.translator.options=r,i.translator.backendConnector.services.utils={hasLoadedNamespace:i.hasLoadedNamespace.bind(i)},i}toJSON(){return{options:this.options,store:this.store,language:this.language,languages:this.languages,resolvedLanguage:this.resolvedLanguage}}}const be=bs.createInstance();be.createInstance;be.dir;be.init;be.loadResources;be.reloadResources;be.use;be.changeLanguage;be.getFixedT;be.t;be.exists;be.setDefaultNamespace;be.hasLoadedNamespace;be.loadNamespaces;be.loadLanguages;const{slice:pg,forEach:mg}=[];function vg(e){return mg.call(pg.call(arguments,1),t=>{if(t)for(const n in t)e[n]===void 0&&(e[n]=t[n])}),e}function yg(e){return typeof e!="string"?!1:[/<\s*script.*?>/i,/<\s*\/\s*script\s*>/i,/<\s*img.*?on\w+\s*=/i,/<\s*\w+\s*on\w+\s*=.*?>/i,/javascript\s*:/i,/vbscript\s*:/i,/expression\s*\(/i,/eval\s*\(/i,/alert\s*\(/i,/document\.cookie/i,/document\.write\s*\(/i,/window\.location/i,/innerHTML/i].some(n=>n.test(e))}const Ua=/^[\u0009\u0020-\u007e\u0080-\u00ff]+$/,bg=function(e,t){const s=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{path:"/"},r=encodeURIComponent(t);let i=`${e}=${r}`;if(s.maxAge>0){const o=s.maxAge-0;if(Number.isNaN(o))throw new Error("maxAge should be a Number");i+=`; Max-Age=${Math.floor(o)}`}if(s.domain){if(!Ua.test(s.domain))throw new TypeError("option domain is invalid");i+=`; Domain=${s.domain}`}if(s.path){if(!Ua.test(s.path))throw new TypeError("option path is invalid");i+=`; Path=${s.path}`}if(s.expires){if(typeof s.expires.toUTCString!="function")throw new TypeError("option expires is invalid");i+=`; Expires=${s.expires.toUTCString()}`}if(s.httpOnly&&(i+="; HttpOnly"),s.secure&&(i+="; Secure"),s.sameSite)switch(typeof s.sameSite=="string"?s.sameSite.toLowerCase():s.sameSite){case!0:i+="; SameSite=Strict";break;case"lax":i+="; SameSite=Lax";break;case"strict":i+="; SameSite=Strict";break;case"none":i+="; SameSite=None";break;default:throw new TypeError("option sameSite is invalid")}return s.partitioned&&(i+="; Partitioned"),i},Va={create(e,t,n,s){let r=arguments.length>4&&arguments[4]!==void 0?arguments[4]:{path:"/",sameSite:"strict"};n&&(r.expires=new Date,r.expires.setTime(r.expires.getTime()+n*60*1e3)),s&&(r.domain=s),document.cookie=bg(e,t,r)},read(e){const t=`${e}=`,n=document.cookie.split(";");for(let s=0;s-1&&(s=window.location.hash.substring(window.location.hash.indexOf("?")));const i=s.substring(1).split("&");for(let o=0;o0&&i[o].substring(0,a)===t&&(n=i[o].substring(a+1))}}return n}},Sg={name:"hash",lookup(e){let{lookupHash:t,lookupFromHashIndex:n}=e,s;if(typeof window<"u"){const{hash:r}=window.location;if(r&&r.length>2){const i=r.substring(1);if(t){const o=i.split("&");for(let a=0;a0&&o[a].substring(0,l)===t&&(s=o[a].substring(l+1))}}if(s)return s;if(!s&&n>-1){const o=r.match(/\/([a-zA-Z-]*)/g);return Array.isArray(o)?o[typeof n=="number"?n:0]?.replace("/",""):void 0}}}return s}};let An=null;const qa=()=>{if(An!==null)return An;try{if(An=typeof window<"u"&&window.localStorage!==null,!An)return!1;const e="i18next.translate.boo";window.localStorage.setItem(e,"foo"),window.localStorage.removeItem(e)}catch{An=!1}return An};var xg={name:"localStorage",lookup(e){let{lookupLocalStorage:t}=e;if(t&&qa())return window.localStorage.getItem(t)||void 0},cacheUserLanguage(e,t){let{lookupLocalStorage:n}=t;n&&qa()&&window.localStorage.setItem(n,e)}};let Mn=null;const Ha=()=>{if(Mn!==null)return Mn;try{if(Mn=typeof window<"u"&&window.sessionStorage!==null,!Mn)return!1;const e="i18next.translate.boo";window.sessionStorage.setItem(e,"foo"),window.sessionStorage.removeItem(e)}catch{Mn=!1}return Mn};var $g={name:"sessionStorage",lookup(e){let{lookupSessionStorage:t}=e;if(t&&Ha())return window.sessionStorage.getItem(t)||void 0},cacheUserLanguage(e,t){let{lookupSessionStorage:n}=t;n&&Ha()&&window.sessionStorage.setItem(n,e)}},Cg={name:"navigator",lookup(e){const t=[];if(typeof navigator<"u"){const{languages:n,userLanguage:s,language:r}=navigator;if(n)for(let i=0;i0?t:void 0}},Pg={name:"htmlTag",lookup(e){let{htmlTag:t}=e,n;const s=t||(typeof document<"u"?document.documentElement:null);return s&&typeof s.getAttribute=="function"&&(n=s.getAttribute("lang")),n}},kg={name:"path",lookup(e){let{lookupFromPathIndex:t}=e;if(typeof window>"u")return;const n=window.location.pathname.match(/\/([a-zA-Z-]*)/g);return Array.isArray(n)?n[typeof t=="number"?t:0]?.replace("/",""):void 0}},Eg={name:"subdomain",lookup(e){let{lookupFromSubdomainIndex:t}=e;const n=typeof t=="number"?t+1:1,s=typeof window<"u"&&window.location?.hostname?.match(/^(\w{2,5})\.(([a-z0-9-]{1,63}\.[a-z]{2,6})|localhost)/i);if(s)return s[n]}};let du=!1;try{document.cookie,du=!0}catch{}const fu=["querystring","cookie","localStorage","sessionStorage","navigator","htmlTag"];du||fu.splice(1,1);const Rg=()=>({order:fu,lookupQuerystring:"lng",lookupCookie:"i18next",lookupLocalStorage:"i18nextLng",lookupSessionStorage:"i18nextLng",caches:["localStorage"],excludeCacheFor:["cimode"],convertDetectedLanguage:e=>e});class hu{constructor(t){let n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};this.type="languageDetector",this.detectors={},this.init(t,n)}init(){let t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{languageUtils:{}},n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{},s=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};this.services=t,this.options=vg(n,this.options||{},Rg()),typeof this.options.convertDetectedLanguage=="string"&&this.options.convertDetectedLanguage.indexOf("15897")>-1&&(this.options.convertDetectedLanguage=r=>r.replace("-","_")),this.options.lookupFromUrlIndex&&(this.options.lookupFromPathIndex=this.options.lookupFromUrlIndex),this.i18nOptions=s,this.addDetector(_g),this.addDetector(wg),this.addDetector(xg),this.addDetector($g),this.addDetector(Cg),this.addDetector(Pg),this.addDetector(kg),this.addDetector(Eg),this.addDetector(Sg)}addDetector(t){return this.detectors[t.name]=t,this}detect(){let t=arguments.length>0&&arguments[0]!==void 0?arguments[0]:this.options.order,n=[];return t.forEach(s=>{if(this.detectors[s]){let r=this.detectors[s].lookup(this.options);r&&typeof r=="string"&&(r=[r]),r&&(n=n.concat(r))}}),n=n.filter(s=>s!=null&&!yg(s)).map(s=>this.options.convertDetectedLanguage(s)),this.services&&this.services.languageUtils&&this.services.languageUtils.getBestMatchFromCodes?n:n.length>0?n[0]:null}cacheUserLanguage(t){let n=arguments.length>1&&arguments[1]!==void 0?arguments[1]:this.options.caches;n&&(this.options.excludeCacheFor&&this.options.excludeCacheFor.indexOf(t)>-1||n.forEach(s=>{this.detectors[s]&&this.detectors[s].cacheUserLanguage(t,this.options)}))}}hu.type="languageDetector";function gu(e,t,n){function s(a){return a&&a.indexOf("###")>-1?a.replace(/###/g,"."):a}function r(){return!e||typeof e=="string"}for(var i=typeof t!="string"?[].concat(t):t.split(".");i.length>1;){if(r())return{};var o=s(i.shift());!e[o]&&n&&(e[o]=new n),e=e[o]}return r()?{}:{obj:e,k:s(i.shift())}}function Og(e,t,n){var s=gu(e,t,Object),r=s.obj,i=s.k;r[i]=n}function za(e,t){var n=gu(e,t),s=n.obj,r=n.k;if(s)return s[r]}var pu=[],Lg=pu.forEach,Tg=pu.slice;function Ig(e){return Lg.call(Tg.call(arguments,1),function(t){if(t)for(var n in t)e[n]===void 0&&(e[n]=t[n])}),e}class Ir{constructor(t,n){this.value=t,this.opts=n}valueOf(){return this.value}toString(){throw new Error("Subclasses of FluentType must implement toString.")}}class Le extends Ir{valueOf(){return null}toString(){return`{${this.value||"???"}}`}}class vn extends Ir{constructor(t,n){super(parseFloat(t),n)}toString(t){try{return t._memoizeIntlObject(Intl.NumberFormat,this.opts).format(this.value)}catch{return this.value}}}class qi extends Ir{constructor(t,n){super(new Date(t),n)}toString(t){try{return t._memoizeIntlObject(Intl.DateTimeFormat,this.opts).format(this.value)}catch{return this.value}}}function mu(e,t){return Object.assign({},e,Ag(t))}function Ag(e){const t={};for(const[n,s]of Object.entries(e))t[n]=s.valueOf();return t}function Mg([e],t){return e instanceof Le?e:e instanceof vn?new vn(e.valueOf(),mu(e.opts,t)):new Le("NUMBER()")}function Fg([e],t){return e instanceof Le?e:e instanceof qi?new qi(e.valueOf(),mu(e.opts,t)):new Le("DATETIME()")}const Dg=Object.freeze(Object.defineProperty({__proto__:null,DATETIME:Fg,NUMBER:Mg},Symbol.toStringTag,{value:"Module"})),ri=2500,Ng="⁨",jg="⁩";function Kg(e,t,n){if(n===t||n instanceof vn&&t instanceof vn&&n.value===t.value)return!0;if(t instanceof vn&&typeof n=="string"){let s=e._memoizeIntlObject(Intl.PluralRules,t.opts).select(t.value);if(n===s)return!0}return!1}function Wa(e,t,n){return t[n]?Qe(e,t[n]):(e.errors.push(new RangeError("No default")),new Le)}function vu(e,t){const n=[],s={};for(const r of t)r.type==="narg"?s[r.name]=Qe(e,r.value):n.push(Qe(e,r));return[n,s]}function Qe(e,t){if(typeof t=="string")return e.bundle._transform(t);if(t instanceof Le)return t;if(Array.isArray(t))return zg(e,t);switch(t.type){case"str":return t.value;case"num":return new vn(t.value,{minimumFractionDigits:t.precision});case"var":return Bg(e,t);case"mesg":return Ug(e,t);case"term":return Vg(e,t);case"func":return qg(e,t);case"select":return Hg(e,t);case void 0:return t.value!==null&&t.value!==void 0?Qe(e,t.value):(e.errors.push(new RangeError("No value")),new Le);default:return new Le}}function Bg(e,{name:t}){if(!e.args||!e.args.hasOwnProperty(t))return e.insideTermReference===!1&&e.errors.push(new ReferenceError(`Unknown variable: ${t}`)),new Le(`$${t}`);const n=e.args[t];if(n instanceof Ir)return n;switch(typeof n){case"string":return n;case"number":return new vn(n);case"object":if(n instanceof Date)return new qi(n);default:return e.errors.push(new TypeError(`Unsupported variable type: ${t}, ${typeof n}`)),new Le(`$${t}`)}}function Ug(e,{name:t,attr:n}){const s=e.bundle._messages.get(t);if(!s){const r=new ReferenceError(`Unknown message: ${t}`);return e.errors.push(r),new Le(t)}if(n){const r=s.attrs&&s.attrs[n];return r?Qe(e,r):(e.errors.push(new ReferenceError(`Unknown attribute: ${n}`)),new Le(`${t}.${n}`))}return Qe(e,s)}function Vg(e,{name:t,attr:n,args:s}){const r=`-${t}`,i=e.bundle._terms.get(r);if(!i){const l=new ReferenceError(`Unknown term: ${r}`);return e.errors.push(l),new Le(r)}const[,o]=vu(e,s),a={...e,args:o,insideTermReference:!0};if(n){const l=i.attrs&&i.attrs[n];return l?Qe(a,l):(e.errors.push(new ReferenceError(`Unknown attribute: ${n}`)),new Le(`${r}.${n}`))}return Qe(a,i)}function qg(e,{name:t,args:n}){const s=e.bundle._functions[t]||Dg[t];if(!s)return e.errors.push(new ReferenceError(`Unknown function: ${t}()`)),new Le(`${t}()`);if(typeof s!="function")return e.errors.push(new TypeError(`Function ${t}() is not callable`)),new Le(`${t}()`);try{return s(...vu(e,n))}catch{return new Le(`${t}()`)}}function Hg(e,{selector:t,variants:n,star:s}){let r=Qe(e,t);if(r instanceof Le){const o=Wa(e,n,s);return Qe(e,o)}for(const o of n){const a=Qe(e,o.key);if(Kg(e.bundle,r,a))return Qe(e,o)}const i=Wa(e,n,s);return Qe(e,i)}function zg(e,t){if(e.dirty.has(t))return e.errors.push(new RangeError("Cyclic reference")),new Le;e.dirty.add(t);const n=[],s=e.bundle._useIsolating&&t.length>1;for(const r of t){if(typeof r=="string"){n.push(e.bundle._transform(r));continue}const i=Qe(e,r).toString(e.bundle);s&&n.push(Ng),i.length>ri?(e.errors.push(new RangeError(`Too many characters in placeable (${i.length}, max allowed is ${ri})`)),n.push(i.slice(ri))):n.push(i),s&&n.push(jg)}return e.dirty.delete(t),n.join("")}function Wg(e,t,n,s=[]){return Qe({bundle:e,args:t,errors:s,dirty:new WeakSet,insideTermReference:!1},n).toString(e)}class Oe extends Error{}const ii=/^(-?[a-zA-Z][\w-]*) *= */mg,Qa=/\.([a-zA-Z][\w-]*) *= */y,Qg=/\*?\[/y,oi=/(-?[0-9]+(?:\.([0-9]+))?)/y,Gg=/([a-zA-Z][\w-]*)/y,Ga=/([$-])?([a-zA-Z][\w-]*)(?:\.([a-zA-Z][\w-]*))?/y,Jg=/^[A-Z][A-Z0-9_-]*$/,zs=/([^{}\n\r]+)/y,Yg=/([^\\"\n\r]*)/y,Ja=/\\([\\"])/y,Ya=/\\u([a-fA-F0-9]{4})|\\U([a-fA-F0-9]{6})/y,Xg=/^\n+/,Xa=/ +$/,Zg=/ *\r?\n/g,ep=/( *)$/,tp=/{\s*/y,Za=/\s*}/y,np=/\[\s*/y,sp=/\s*] */y,rp=/\s*\(\s*/y,ip=/\s*->\s*/y,op=/\s*:\s*/y,ap=/\s*,?\s*/y,lp=/\s+/y,cp=100;class up extends Map{static fromString(t){ii.lastIndex=0;let n=new this,s=0;for(;;){let $=ii.exec(t);if($===null)break;s=ii.lastIndex;try{n.set($[1],c())}catch(C){if(C instanceof Oe)continue;throw C}}return n;function r($){return $.lastIndex=s,$.test(t)}function i($,C){if(t[s]===$)return s++,!0;if(C)throw new C(`Expected ${$}`);return!1}function o($,C){if(r($))return s=$.lastIndex,!0;if(C)throw new C(`Expected ${$.toString()}`);return!1}function a($){$.lastIndex=s;let C=$.exec(t);if(C===null)throw new Oe(`Expected ${$.toString()}`);return s=$.lastIndex,C}function l($){return a($)[1]}function c(){let $=u(),C=d();if(C===null){if($===null)throw new Oe("Expected message value or attributes");return $}return{value:$,attrs:C}}function d(){let $={};for(;r(Qa);){let C=l(Qa),I=u();if(I===null)throw new Oe("Expected attribute value");$[C]=I}return Object.keys($).length>0?$:null}function u(){if(r(zs))var $=l(zs);if(t[s]==="{"||t[s]==="}")return f($?[$]:[],1/0);let C=P();return C?$?f([$,C],C.length):(C.value=E(C.value,Xg),f([C],C.length)):$?E($,Xa):null}function f($=[],C){let I=0;for(;;){if(r(zs)){$.push(l(zs));continue}if(t[s]==="{"){if(++I>cp)throw new Oe("Too many placeables");$.push(h());continue}if(t[s]==="}")throw new Oe("Unbalanced closing brace");let M=P();if(M){$.push(M),C=Math.min(C,M.length);continue}break}let T=$.length-1;typeof $[T]=="string"&&($[T]=E($[T],Xa));let L=[];for(let M of $)M.type==="indent"?M=M.value.slice(0,M.value.length-C):M.type==="str"&&(M=M.value),M&&L.push(M);return L}function h(){o(tp,Oe);let $=g();if(o(Za))return $;if(o(ip)){let C=v();return o(Za,Oe),{type:"select",selector:$,...C}}throw new Oe("Unclosed placeable")}function g(){if(t[s]==="{")return h();if(r(Ga)){let[,$,C,I=null]=a(Ga);if($==="$")return{type:"var",name:C};if(o(rp)){let T=m();if($==="-")return{type:"term",name:C,attr:I,args:T};if(Jg.test(C))return{type:"func",name:C,args:T};throw new Oe("Function names must be all upper-case")}return $==="-"?{type:"term",name:C,attr:I,args:[]}:{type:"mesg",name:C,attr:I}}return w()}function m(){let $=[];for(;;){switch(t[s]){case")":return s++,$;case void 0:throw new Oe("Unclosed argument list")}$.push(y()),o(ap)}}function y(){let $=g();return $.type!=="mesg"?$:o(op)?{type:"narg",name:$.name,value:w()}:$}function v(){let $=[],C=0,I;for(;r(Qg);){i("*")&&(I=C);let T=b(),L=u();if(L===null)throw new Oe("Expected variant value");$[C++]={key:T,value:L}}if(C===0)return null;if(I===void 0)throw new Oe("Expected default variant");return{variants:$,star:I}}function b(){o(np,Oe);let $=r(oi)?S():l(Gg);return o(sp,Oe),$}function w(){if(r(oi))return S();if(t[s]==='"')return x();throw new Oe("Invalid expression")}function S(){let[,$,C=""]=a(oi),I=C.length;return{type:"num",value:parseFloat($),precision:I}}function x(){i('"',Oe);let $="";for(;;){if($+=l(Yg),t[s]==="\\"){$+=k();continue}if(i('"'))return{type:"str",value:$};throw new Oe("Unclosed string literal")}}function k(){if(r(Ja))return l(Ja);if(r(Ya)){let[,$,C]=a(Ya),I=parseInt($||C,16);return I<=55295||57344<=I?String.fromCodePoint(I):"�"}throw new Oe("Unknown escape sequence")}function P(){let $=s;switch(o(lp),t[s]){case".":case"[":case"*":case"}":case void 0:return!1;case"{":return O(t.slice($,s))}return t[s-1]===" "?O(t.slice($,s)):!1}function E($,C){return $.replace(C,"")}function O($){let C=$.replace(Zg,` +`),I=ep.exec($)[1].length;return{type:"indent",value:C,length:I}}}}class dp{constructor(t,{functions:n={},useIsolating:s=!0,transform:r=i=>i}={}){this.locales=Array.isArray(t)?t:[t],this._terms=new Map,this._messages=new Map,this._functions=n,this._useIsolating=s,this._transform=r,this._intls=new WeakMap}get messages(){return this._messages[Symbol.iterator]()}hasMessage(t){return this._messages.has(t)}getMessage(t){return this._messages.get(t)}addMessages(t,n){const s=up.fromString(t);return this.addResource(s,n)}addResource(t,{allowOverrides:n=!1}={}){const s=[];for(const[r,i]of t)if(r.startsWith("-")){if(n===!1&&this._terms.has(r)){s.push(`Attempt to override an existing term: "${r}"`);continue}this._terms.set(r,i)}else{if(n===!1&&this._messages.has(r)){s.push(`Attempt to override an existing message: "${r}"`);continue}this._messages.set(r,i)}return s}format(t,n,s){return typeof t=="string"?this._transform(t):t===null||t.value===null?null:typeof t.value=="string"?this._transform(t.value):Wg(this,n,t,s)}_memoizeIntlObject(t,n){const s=this._intls.get(t)||{},r=JSON.stringify(n);return s[r]||(s[r]=new t(this.locales,n),this._intls.set(t,s)),s[r]}}class Hn{equals(t,n=["span"]){const s=new Set(Object.keys(this)),r=new Set(Object.keys(t));if(n)for(const i of n)s.delete(i),r.delete(i);if(s.size!==r.size)return!1;for(const i of s){if(!r.has(i))return!1;const o=this[i],a=t[i];if(typeof o!=typeof a)return!1;if(o instanceof Array&&a instanceof Array){if(o.length!==a.length)return!1;for(let l=0;l0?this.value.length-n-1:0;return{value:t,precision:s}}}class li extends Ee{constructor(t,n=null){super(),this.type="MessageReference",this.id=t,this.attribute=n}}class ci extends Ee{constructor(t,n=null,s=null){super(),this.type="TermReference",this.id=t,this.attribute=n,this.arguments=s}}class mp extends Ee{constructor(t){super(),this.type="VariableReference",this.id=t}}class vp extends Ee{constructor(t,n){super(),this.type="FunctionReference",this.id=t,this.arguments=n}}class yp extends Ee{constructor(t,n){super(),this.type="SelectExpression",this.selector=t,this.variants=n}}class bp extends Ee{constructor(t=[],n=[]){super(),this.type="CallArguments",this.positional=t,this.named=n}}class _p extends Ee{constructor(t,n){super(),this.type="Attribute",this.id=t,this.value=n}}class wp extends Ee{constructor(t,n,s){super(),this.type="Variant",this.key=t,this.value=n,this.default=s}}class sl extends Ee{constructor(t,n){super(),this.type="NamedArgument",this.name=t,this.value=n}}class Sp extends Ee{constructor(t){super(),this.type="Identifier",this.name=t}}class bo extends Ee{constructor(t){super(),this.content=t}}class rl extends bo{constructor(){super(...arguments),this.type="Comment"}}class xp extends bo{constructor(){super(...arguments),this.type="GroupComment"}}class $p extends bo{constructor(){super(...arguments),this.type="ResourceComment"}}class il extends Ee{constructor(t){super(),this.type="Junk",this.annotations=[],this.content=t}addAnnotation(t){this.annotations.push(t)}}class bu extends Hn{constructor(t,n){super(),this.type="Span",this.start=t,this.end=n}}class Cp extends Ee{constructor(t,n=[],s){super(),this.type="Annotation",this.code=t,this.arguments=n,this.message=s}}class ge extends Error{constructor(t,...n){super(),this.code=t,this.args=n,this.message=Pp(t,n)}}function Pp(e,t){switch(e){case"E0001":return"Generic error";case"E0002":return"Expected an entry start";case"E0003":{const[n]=t;return`Expected token: "${n}"`}case"E0004":{const[n]=t;return`Expected a character from range: "${n}"`}case"E0005":{const[n]=t;return`Expected message "${n}" to have a value or attributes`}case"E0006":{const[n]=t;return`Expected term "-${n}" to have a value`}case"E0007":return"Keyword cannot end with a whitespace";case"E0008":return"The callee has to be an upper-case identifier or a term";case"E0009":return"The argument name has to be a simple identifier";case"E0010":return"Expected one of the variants to be marked as default (*)";case"E0011":return'Expected at least one variant after "->"';case"E0012":return"Expected value";case"E0013":return"Expected variant key";case"E0014":return"Expected literal";case"E0015":return"Only one variant can be marked as default (*)";case"E0016":return"Message references cannot be used as selectors";case"E0017":return"Terms cannot be used as selectors";case"E0018":return"Attributes of messages cannot be used as selectors";case"E0019":return"Attributes of terms cannot be used as placeables";case"E0020":return"Unterminated string expression";case"E0021":return"Positional arguments must not follow named arguments";case"E0022":return"Named arguments must be unique";case"E0024":return"Cannot access variants of a message.";case"E0025":{const[n]=t;return`Unknown escape sequence: \\${n}.`}case"E0026":{const[n]=t;return`Invalid Unicode escape sequence: ${n}.`}case"E0027":return"Unbalanced closing brace in TextElement.";case"E0028":return"Expected an inline expression";case"E0029":return"Expected simple expression as selector";default:return e}}class kp{constructor(t){this.string=t,this.index=0,this.peekOffset=0}charAt(t){return this.string[t]==="\r"&&this.string[t+1]===` +`?` +`:this.string[t]}currentChar(){return this.charAt(this.index)}currentPeek(){return this.charAt(this.index+this.peekOffset)}next(){return this.peekOffset=0,this.string[this.index]==="\r"&&this.string[this.index+1]===` +`&&this.index++,this.index++,this.string[this.index]}peek(){return this.string[this.index+this.peekOffset]==="\r"&&this.string[this.index+this.peekOffset+1]===` +`&&this.peekOffset++,this.peekOffset++,this.string[this.index+this.peekOffset]}resetPeek(t=0){this.peekOffset=t}skipToPeek(){this.index+=this.peekOffset,this.peekOffset=0}}const ze=` +`,Et=void 0,Ep=["}",".","[","*"];class ol extends kp{peekBlankInline(){const t=this.index+this.peekOffset;for(;this.currentPeek()===" ";)this.peek();return this.string.slice(t,this.index+this.peekOffset)}skipBlankInline(){const t=this.peekBlankInline();return this.skipToPeek(),t}peekBlankBlock(){let t="";for(;;){const n=this.peekOffset;if(this.peekBlankInline(),this.currentPeek()===ze){t+=ze,this.peek();continue}return this.currentPeek()===Et||this.resetPeek(n),t}}skipBlankBlock(){const t=this.peekBlankBlock();return this.skipToPeek(),t}peekBlank(){for(;this.currentPeek()===" "||this.currentPeek()===ze;)this.peek()}skipBlank(){this.peekBlank(),this.skipToPeek()}expectChar(t){if(this.currentChar()===t){this.next();return}throw new ge("E0003",t)}expectLineEnd(){if(this.currentChar()!==Et){if(this.currentChar()===ze){this.next();return}throw new ge("E0003","␤")}}takeChar(t){const n=this.currentChar();return n===Et?Et:t(n)?(this.next(),n):null}isCharIdStart(t){if(t===Et)return!1;const n=t.charCodeAt(0);return n>=97&&n<=122||n>=65&&n<=90}isIdentifierStart(){return this.isCharIdStart(this.currentPeek())}isNumberStart(){const t=this.currentChar()==="-"?this.peek():this.currentChar();if(t===Et)return this.resetPeek(),!1;const n=t.charCodeAt(0),s=n>=48&&n<=57;return this.resetPeek(),s}isCharPatternContinuation(t){return t===Et?!1:!Ep.includes(t)}isValueStart(){const t=this.currentPeek();return t!==ze&&t!==Et}isValueContinuation(){const t=this.peekOffset;return this.peekBlankInline(),this.currentPeek()==="{"?(this.resetPeek(t),!0):this.peekOffset-t===0?!1:this.isCharPatternContinuation(this.currentPeek())?(this.resetPeek(t),!0):!1}isNextLineComment(t=-1){if(this.currentChar()!==ze)return!1;let n=0;for(;n<=t||t===-1&&n<3;){if(this.peek()!=="#"){if(n<=t&&t!==-1)return this.resetPeek(),!1;break}n++}const s=this.peek();return s===" "||s===ze?(this.resetPeek(),!0):(this.resetPeek(),!1)}isVariantStart(){const t=this.peekOffset;return this.currentPeek()==="*"&&this.peek(),this.currentPeek()==="["?(this.resetPeek(t),!0):(this.resetPeek(t),!1)}isAttributeStart(){return this.currentPeek()==="."}skipToNextEntryStart(t){let n=this.string.lastIndexOf(ze,this.index);for(t{const s=n.charCodeAt(0);return s>=97&&s<=122||s>=65&&s<=90||s>=48&&s<=57||s===95||s===45};return this.takeChar(t)}takeDigit(){const t=n=>{const s=n.charCodeAt(0);return s>=48&&s<=57};return this.takeChar(t)}takeHexDigit(){const t=n=>{const s=n.charCodeAt(0);return s>=48&&s<=57||s>=65&&s<=70||s>=97&&s<=102};return this.takeChar(t)}}const Rp=/[ \n\r]+$/;function Me(e){return function(t,...n){if(!this.withSpans)return e.call(this,t,...n);const s=t.index,r=e.call(this,t,...n);if(r.span)return r;const i=t.index;return r.addSpan(s,i),r}}class Op{constructor({withSpans:t=!0}={}){this.withSpans=t,this.getComment=Me(this.getComment),this.getMessage=Me(this.getMessage),this.getTerm=Me(this.getTerm),this.getAttribute=Me(this.getAttribute),this.getIdentifier=Me(this.getIdentifier),this.getVariant=Me(this.getVariant),this.getNumber=Me(this.getNumber),this.getPattern=Me(this.getPattern),this.getTextElement=Me(this.getTextElement),this.getPlaceable=Me(this.getPlaceable),this.getExpression=Me(this.getExpression),this.getInlineExpression=Me(this.getInlineExpression),this.getCallArgument=Me(this.getCallArgument),this.getCallArguments=Me(this.getCallArguments),this.getString=Me(this.getString),this.getLiteral=Me(this.getLiteral),this.getComment=Me(this.getComment)}parse(t){const n=new ol(t);n.skipBlankBlock();const s=[];let r=null;for(;n.currentChar();){const o=this.getEntryOrJunk(n),a=n.skipBlankBlock();if(o instanceof rl&&a.length===0&&n.currentChar()){r=o;continue}r&&(o instanceof tl||o instanceof nl?(o.comment=r,this.withSpans&&(o.span.start=o.comment.span.start)):s.push(r),r=null),s.push(o)}const i=new fp(s);return this.withSpans&&i.addSpan(0,n.index),i}parseEntry(t){const n=new ol(t);for(n.skipBlankBlock();n.currentChar()==="#";){const s=this.getEntryOrJunk(n);if(s instanceof il)return s;n.skipBlankBlock()}return this.getEntryOrJunk(n)}getEntryOrJunk(t){const n=t.index;try{const s=this.getEntry(t);return t.expectLineEnd(),s}catch(s){if(!(s instanceof ge))throw s;let r=t.index;t.skipToNextEntryStart(n);const i=t.index;ia!==ze);)s+=o}if(t.isNextLineComment(n))s+=t.currentChar(),t.next();else break}let r;switch(n){case 0:r=rl;break;case 1:r=xp;break;default:r=$p}return new r(s)}getMessage(t){const n=this.getIdentifier(t);t.skipBlankInline(),t.expectChar("=");const s=this.maybeGetPattern(t),r=this.getAttributes(t);if(s===null&&r.length===0)throw new ge("E0005",n.name);return new tl(n,s,r)}getTerm(t){t.expectChar("-");const n=this.getIdentifier(t);t.skipBlankInline(),t.expectChar("=");const s=this.maybeGetPattern(t);if(s===null)throw new ge("E0006",n.name);const r=this.getAttributes(t);return new nl(n,s,r)}getAttribute(t){t.expectChar(".");const n=this.getIdentifier(t);t.skipBlankInline(),t.expectChar("=");const s=this.maybeGetPattern(t);if(s===null)throw new ge("E0012");return new _p(n,s)}getAttributes(t){const n=[];for(t.peekBlank();t.isAttributeStart();){t.skipToPeek();const s=this.getAttribute(t);n.push(s),t.peekBlank()}return n}getIdentifier(t){let n=t.takeIDStart(),s;for(;s=t.takeIDChar();)n+=s;return new Sp(n)}getVariantKey(t){const n=t.currentChar();if(n===Et)throw new ge("E0013");const s=n.charCodeAt(0);return s>=48&&s<=57||s===45?this.getNumber(t):this.getIdentifier(t)}getVariant(t,n=!1){let s=!1;if(t.currentChar()==="*"){if(n)throw new ge("E0015");t.next(),s=!0}t.expectChar("["),t.skipBlank();const r=this.getVariantKey(t);t.skipBlank(),t.expectChar("]");const i=this.maybeGetPattern(t);if(i===null)throw new ge("E0012");return new wp(r,i,s)}getVariants(t){const n=[];let s=!1;for(t.skipBlank();t.isVariantStart();){const r=this.getVariant(t,s);r.default&&(s=!0),n.push(r),t.expectLineEnd(),t.skipBlank()}if(n.length===0)throw new ge("E0011");if(!s)throw new ge("E0010");return n}getDigits(t){let n="",s;for(;s=t.takeDigit();)n+=s;if(n.length===0)throw new ge("E0004","0-9");return n}getNumber(t){let n="";return t.currentChar()==="-"?(t.next(),n+=`-${this.getDigits(t)}`):n+=this.getDigits(t),t.currentChar()==="."&&(t.next(),n+=`.${this.getDigits(t)}`),new pp(n)}maybeGetPattern(t){return t.peekBlankInline(),t.isValueStart()?(t.skipToPeek(),this.getPattern(t,!1)):(t.peekBlankBlock(),t.isValueContinuation()?(t.skipToPeek(),this.getPattern(t,!0)):null)}getPattern(t,n){const s=[];let r;if(n){const a=t.index,l=t.skipBlankInline();s.push(this.getIndent(t,l,a)),r=l.length}else r=1/0;let i;e:for(;i=t.currentChar();)switch(i){case ze:{const a=t.index,l=t.peekBlankBlock();if(t.isValueContinuation()){t.skipToPeek();const c=t.skipBlankInline();r=Math.min(r,c.length),s.push(this.getIndent(t,l+c,a));continue e}t.resetPeek();break e}case"{":s.push(this.getPlaceable(t));continue e;case"}":throw new ge("E0027");default:s.push(this.getTextElement(t))}const o=this.dedent(s,r);return new hp(o)}getIndent(t,n,s){return new ui(n,s,t.index)}dedent(t,n){const s=[];for(let i of t){if(i instanceof ai){s.push(i);continue}if(i instanceof ui&&(i.value=i.value.slice(0,i.value.length-n),i.value.length===0))continue;let o=s[s.length-1];if(o&&o instanceof on){const a=new on(o.value+i.value);this.withSpans&&a.addSpan(o.span.start,i.span.end),s[s.length-1]=a;continue}if(i instanceof ui){const a=new on(i.value);this.withSpans&&a.addSpan(i.span.start,i.span.end),i=a}s.push(i)}const r=s[s.length-1];return r instanceof on&&(r.value=r.value.replace(Rp,""),r.value.length===0&&s.pop()),s}getTextElement(t){let n="",s;for(;s=t.currentChar();){if(s==="{"||s==="}")return new on(n);if(s===ze)return new on(n);n+=s,t.next()}return new on(n)}getEscapeSequence(t){const n=t.currentChar();switch(n){case"\\":case'"':return t.next(),`\\${n}`;case"u":return this.getUnicodeEscapeSequence(t,n,4);case"U":return this.getUnicodeEscapeSequence(t,n,6);default:throw new ge("E0025",n)}}getUnicodeEscapeSequence(t,n,s){t.expectChar(n);let r="";for(let i=0;i")return t.resetPeek(),n;if(n instanceof li)throw n.attribute===null?new ge("E0016"):new ge("E0018");if(n instanceof ci){if(n.attribute===null)throw new ge("E0017")}else if(n instanceof ai)throw new ge("E0029");t.next(),t.next(),t.skipBlankInline(),t.expectLineEnd();const s=this.getVariants(t);return new yp(n,s)}if(n instanceof ci&&n.attribute!==null)throw new ge("E0019");return n}getInlineExpression(t){if(t.currentChar()==="{")return this.getPlaceable(t);if(t.isNumberStart())return this.getNumber(t);if(t.currentChar()==='"')return this.getString(t);if(t.currentChar()==="$"){t.next();const n=this.getIdentifier(t);return new mp(n)}if(t.currentChar()==="-"){t.next();const n=this.getIdentifier(t);let s;t.currentChar()==="."&&(t.next(),s=this.getIdentifier(t));let r;return t.peekBlank(),t.currentPeek()==="("&&(t.skipToPeek(),r=this.getCallArguments(t)),new ci(n,s,r)}if(t.isIdentifierStart()){const n=this.getIdentifier(t);if(t.peekBlank(),t.currentPeek()==="("){if(!/^[A-Z][A-Z0-9_-]*$/.test(n.name))throw new ge("E0008");t.skipToPeek();let r=this.getCallArguments(t);return new vp(n,r)}let s;return t.currentChar()==="."&&(t.next(),s=this.getIdentifier(t)),new li(n,s)}throw new ge("E0028")}getCallArgument(t){const n=this.getInlineExpression(t);if(t.skipBlank(),t.currentChar()!==":")return n;if(n instanceof li&&n.attribute===null){t.next(),t.skipBlank();const s=this.getLiteral(t);return new sl(n.id,s)}throw new ge("E0009")}getCallArguments(t){const n=[],s=[],r=new Set;for(t.expectChar("("),t.skipBlank();t.currentChar()!==")";){const i=this.getCallArgument(t);if(i instanceof sl){if(r.has(i.name.name))throw new ge("E0022");s.push(i),r.add(i.name.name)}else{if(r.size>0)throw new ge("E0021");n.push(i)}if(t.skipBlank(),t.currentChar()===","){t.next(),t.skipBlank();continue}break}return t.expectChar(")"),new bp(n,s)}getString(t){t.expectChar('"');let n="",s;for(;s=t.takeChar(r=>r!=='"'&&r!==ze);)s==="\\"?n+=this.getEscapeSequence(t):n+=s;if(t.currentChar()===ze)throw new ge("E0020");return t.expectChar('"'),new gp(n)}getLiteral(t){if(t.isNumberStart())return this.getNumber(t);if(t.currentChar()==='"')return this.getString(t);throw new ge("E0014")}}class ui{constructor(t,n,s){this.type="Indent",this.value=t,this.span=new bu(n,s)}}function Lp(e,t){return new Op(t).parse(e)}var he=function(t){return"get"+t.type};const Tp={serialize:function(t){if(this[he(t)])return this[he(t)](t);console.warn("unknown type:",t.type,t)},getComment:function(t){return{key:"comment",value:t.content}},getGroupComment:function(){return null},getResourceComment:function(){return null},getMessage:function(t){var n=this;return{key:this[he(t.id)](t.id),value:this[he(t.value)](t.value),comment:t.comment&&this[he(t.comment)](t.comment),attributes:t.attributes&&t.attributes.map(function(s){return n.serialize(s)})}},getAttribute:function(t){return{key:this[he(t.id)](t.id),value:this[he(t.value)](t.value)}},getTerm:function(t){var n=this;return{key:"-".concat(this[he(t.id)](t.id)),value:this[he(t.value)](t.value),comment:t.comment&&this[he(t.comment)](t.comment),attributes:t.attributes&&t.attributes.map(function(s){return n.serialize(s)})}},getIdentifier:function(t){return t.name},getStringLiteral:function(t){return t.value},getPattern:function(t){var n=this,s=t.elements.map(function(r){return r.expression?n[he(r.expression)]?n[he(r.expression)](r.expression):console.log("unknown1",he(r.expression),r.expression):n[he(r)]?n[he(r)](r):console.log("unknown2",he(r),r)});return s.join("")},getCallExpression:function(t){var n=this,s=t.callee.name,r=t.positional.map(function(o){return n[he(o)](o,!0)}),i=t.named.map(function(o){return n[he(o)](o)});return"{ "+s+"($"+r.join(" ")+(i.length?", "+i.join(", "):"")+") }"},getNamedArgument:function(t){return this[he(t.name)](t.name)+': "'+this[he(t.value)](t.value)+'"'},getTextElement:function(t){return t.value},getSelectExpression:function(t){var n=this,s=this[he(t.selector)](t.selector,!0),r=t.variants.map(function(i){return n[he(i)](i)});return t.selector.type==="FunctionReference"?"{ "+s+` -> +`+r.join(` +`)+` +}`:"{ $"+s+` -> +`+r.join(` +`)+` +}`},getVariantExpression:function(t){var n=this[he(t.ref)](t.ref,!0),s=this[he(t.key)](t.key);return s?"{ "+n+"["+s+"] }":" { "+n+" } "},getVariableReference:function(t,n){return n?this[he(t.id)](t.id):"{ $"+this[he(t.id)](t.id)+" }"},getTermReferences:function(t,n){return n?this[he(t.id)](t.id):"{ "+this[he(t.id)](t.id)+" }"},getVariantName:function(t){return t.name},getVariantList:function(t){var n=this,s=t.variants.map(function(r){return n[he(r)](r)});return`{ +`+s.join(` +`)+` +}`},getVariant:function(t){var n=t.key.name?t.key.name:t.key.value,s=t.default,r=this[he(t.value)](t.value),i="["+n+"] "+r;return s?" *"+i:" "+i},getFunctionReference:function(t,n){var s="";return t.arguments.positional.forEach(function(r,i){i>0&&(s+=", "),s+="$".concat(r.id.name)}),t.arguments.named.forEach(function(r,i){(i>0||s!=="")&&(s+=", "),s+="".concat(r.name.name,': "').concat(r.value.value,'"')}),n?"".concat(t.id.name,"(").concat(s,")"):"{ ".concat(t.id.name,"(").concat(s,") }")},getTermReference:function(t){return"{ -".concat(t.id.name," }")},getMessageReference:function(t){return"{ ".concat(t.id.name," }")},getJunk:function(t){var n=t.content.split("="),s=n.shift().trim(),r=n.join("=").trim().replace(/\n {3}/g,` + `).replace(/\n {2}}/g,` +}`);return{key:s,value:r}}};function Ip(e,t){var n=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{respectComments:!0};if(typeof e!="string"){if(!t)throw new Error("The first parameter was not a string");return t(new Error("The first parameter was not a string"))}var s=Lp(e,{withSpans:!1}),r=s.body.reduce(function(i,o){var a=Tp.serialize(o);if(!a)return i;if(a.attributes&&a.attributes.length||a.comment&&n.respectComments){var l={};a.comment&&(l[a.comment.key]=a.comment.value),a.attributes&&a.attributes.length&&a.attributes.forEach(function(c){l[c.key]=c.value}),l.val=a.value,i[a.key]=l}else i[a.key]=a.value;return i},{});return t&&t(null,r),r}function di(e,t){var n="";return n=n+e+" =",t&&t.indexOf(` +`)>-1?(n=n+` + `,n=n+t.split(` +`).join(` + `)):n=n+" "+t,n}function Ap(e){var t="";return t=t+"# "+e.split(` +`).join(` +# `),t=t+` +`,t}function Mp(e,t){var n="";return Object.keys(e).forEach(function(s){var r=e[s];typeof r=="string"?(n=n+di(s,r),n=n+` + +`):(r.comment&&(n=n+Ap(r.comment)),n=n+di(s,r.val),Object.keys(r).forEach(function(i){if(!(i==="comment"||i==="val")){var o=r[i];n=n+di(` + .`+i,o)}}),n=n+` + +`)}),t&&t(null,n),n}function _u(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function Fp(e,t){for(var n=0;ne.length)&&(t=e.length);for(var n=0,s=new Array(t);n-1;if(!n)return o;var d=c?n.attrs[o.split(".")[1]]:n;return l?l.format(d,s):o}},{key:"getResource",value:function(n,s,r,i){var o=this.store.getBundle(n,s),a=r.indexOf(".")>-1?r.split(".")[0]:r;if(o)return o.getMessage(a)}},{key:"addLookupKeys",value:function(n,s,r,i,o){return n}}]),e})();xu.type="i18nFormat";var $u=[],Gp=$u.forEach,Jp=$u.slice;function Yp(e){return Gp.call(Jp.call(arguments,1),function(t){if(t)for(var n in t)e[n]===void 0&&(e[n]=t[n])}),e}function gr(e){"@babel/helpers - typeof";return gr=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(t){return typeof t}:function(t){return t&&typeof Symbol=="function"&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},gr(e)}function ll(e,t){if(t&&gr(t)==="object"){var n="",s=encodeURIComponent;for(var r in t)n+="&"+s(r)+"="+s(t[r]);if(!n)return e;e=e+(e.indexOf("?")!==-1?"&":"?")+n.slice(1)}return e}function Xp(e,t,n,s,r){s&&gr(s)==="object"&&(r||(s._t=new Date),s=ll("",s).slice(1)),t.queryStringParams&&(e=ll(e,t.queryStringParams));try{var i;XMLHttpRequest?i=new XMLHttpRequest:i=new ActiveXObject("MSXML2.XMLHTTP.3.0"),i.open(s?"POST":"GET",e,1),t.crossDomain||i.setRequestHeader("X-Requested-With","XMLHttpRequest"),i.withCredentials=!!t.withCredentials,s&&i.setRequestHeader("Content-type","application/x-www-form-urlencoded"),i.overrideMimeType&&i.overrideMimeType("application/json");var o=t.customHeaders;if(o)for(var a in o)i.setRequestHeader(a,o[a]);i.onreadystatechange=function(){i.readyState>3&&n&&n(i.responseText,i)},i.send(s)}catch(l){console&&console.log(l)}}function Ps(e){"@babel/helpers - typeof";return Ps=typeof Symbol=="function"&&typeof Symbol.iterator=="symbol"?function(t){return typeof t}:function(t){return t&&typeof Symbol=="function"&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},Ps(e)}function Zp(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function em(e,t){for(var n=0;n1&&arguments[1]!==void 0?arguments[1]:{};Zp(this,e),this.init(t,n),this.type="backend"}return tm(e,[{key:"init",value:function(n){var s=arguments.length>1&&arguments[1]!==void 0?arguments[1]:{};this.services=n,this.options=Yp(s,this.options||{},rm())}},{key:"read",value:function(n,s,r){var i=this.options.loadPath;typeof this.options.loadPath=="function"&&(i=this.options.loadPath([n],[s]));var o=this.services.interpolator.interpolate(i,{lng:n,ns:s});this.loadUrl(o,r)}},{key:"loadUrl",value:function(n,s){var r=this;this.options.ajax(n,this.options,function(i,o){if(o.status>=500&&o.status<600)return s("failed loading "+n,!0);if(o.status>=400&&o.status<500)return s("failed loading "+n,!1);var a,l;try{a=r.options.parse(i,n)}catch{l="failed parsing "+n+" to json"}if(l)return s(l,!1);s(null,a)})}},{key:"create",value:function(n,s,r,i){var o=this;typeof n=="string"&&(n=[n]);var a={};a[r]=i||"",n.forEach(function(l){var c=o.services.interpolator.interpolate(o.options.addPath,{lng:l,ns:s});o.options.ajax(c,o.options,function(d,u){},a)})}}]),e})();Cu.type="backend";function _o(e){const t=e&&e.length>0?e:"/",n=t.startsWith("/")?t:`/${t}`;return n.endsWith("/")?n:`${n}/`}function Pu(e,t){const n=_o(e),s=t.replace(/^\/+/,"");return s.length===0?n:`${n}${s}`}const wo=[{code:"en-GB",label:"English (UK)",nativeLabel:"English (UK)"},{code:"fr",label:"French",nativeLabel:"Français"},{code:"de",label:"German",nativeLabel:"Deutsch"},{code:"it",label:"Italian",nativeLabel:"Italiano"},{code:"nl",label:"Dutch",nativeLabel:"Nederlands"},{code:"pl",label:"Polish",nativeLabel:"Polski"},{code:"hi",label:"Hindi",nativeLabel:"हिन्दी"},{code:"ja",label:"Japanese",nativeLabel:"日本語"},{code:"zh-CN",label:"Chinese (Simplified)",nativeLabel:"简体中文"},{code:"ar",label:"Arabic",nativeLabel:"العربية",direction:"rtl"}],im=["en-GB","fr","de","it","nl","pl","hi","ja","zh-CN","ar"],om=new Set(im),ku=wo.filter(e=>om.has(e.code)),$n=wo[0].code,am=["querystring","localStorage","navigator"],zi=wo.reduce((e,t)=>{const n=t.code.toLowerCase();e[n]=t;const[s]=n.split("-");return s&&!e[s]&&(e[s]=t),e},{}),cl=(()=>{const e=zi[$n.toLowerCase()];if(!e)throw new Error(`DEFAULT_LOCALE '${$n}' is not present in SUPPORTED_LOCALES`);return e})();function Eu(e){if(!e)return cl;const t=e.toLowerCase(),[n]=t.split("-");return zi[t]??(n?zi[n]:void 0)??cl}function lm(e){return Eu(e).direction??"ltr"}const cm=ku.map(e=>e.code);function um(e,t={},n){const s={credentials:t.withCredentials?"include":"same-origin",method:t.method??"GET",headers:t.headers};t.body!=null&&(s.body=t.body),fetch(e,s).then(async r=>{if(!r.ok){const i=r.statusText||`Request failed with status ${r.status}`;n(new Error(i),{status:r.status,statusText:i});return}n(await r.text(),{status:r.status,statusText:r.statusText})}).catch(r=>{const i=r instanceof Error?r:new Error("Unexpected i18n fetch failure");n(i,{status:500,statusText:i.message})})}function dm(e){return`${_o(e)}locales/{{lng}}/{{ns}}.ftl`}function Ru(e){if(typeof document>"u")return;const t=Eu(e??$n),n=lm(t.code);document.documentElement.lang=t.code,document.documentElement.dir=n,document.documentElement.dataset.direction=n,document.body&&(document.body.dataset.direction=n)}const So=be.use(Cu).use(hu).use(xu).init({backend:{loadPath:dm("/"),ajax:um},fallbackLng:$n,supportedLngs:cm,ns:["common"],defaultNS:"common",interpolation:{escapeValue:!1},detection:{order:[...am],lookupQuerystring:"lng",caches:["localStorage"]},returnNull:!1,i18nFormat:{fluentBundleOptions:{useIsolating:!1}}});So.then(()=>{Ru(be.resolvedLanguage??be.language??$n)}).catch(e=>{console.error("[i18n] Failed to initialize runtime locale",e)});be.on("languageChanged",e=>{Ru(e)});const Ou=He(),fm=e=>{const[t,n]=A($n),s=i=>{n(i)};So.then(()=>{n(be.resolvedLanguage??be.language??$n)}).catch(i=>{console.error("[i18n] Failed to initialize provider language state",i)}),be.on("languageChanged",s),Z(()=>{be.off("languageChanged",s)});const r=(i,o)=>(t(),be.t(i,o));return _(Ou.Provider,{value:{language:t,locales:ku,t:r,changeLanguage:async i=>{await be.changeLanguage(i)}},get children(){return e.children}})};function ve(){const e=Te(Ou);if(!e)throw new Error("I18nProvider is missing");return e}const hm=new jh({defaultOptions:{queries:{retry:1,staleTime:15e3}}}),gm=e=>_(Th,{client:hm,get children(){return _(fm,{get children(){return _(zh,{get children(){return e.children}})}})}});var pm=!1;function ks(e){return e[e.length-1]}function mm(e){return typeof e=="function"}function Kt(e,t){return mm(e)?e(t):e}var vm=Object.prototype.hasOwnProperty,ul=Object.prototype.propertyIsEnumerable,ym=()=>Object.create(null),an=(e,t)=>fn(e,t,ym);function fn(e,t,n=()=>({}),s=0){if(e===t)return e;if(s>500)return t;const r=t,i=hl(e)&&hl(r);if(!i&&!(pr(e)&&pr(r)))return r;const o=i?e:dl(e);if(!o)return r;const a=i?r:dl(r);if(!a)return r;const l=o.length,c=a.length,d=i?new Array(c):n();let u=0;for(let f=0;f"u")return!0;const n=t.prototype;return!(!fl(n)||!n.hasOwnProperty("isPrototypeOf"))}function fl(e){return Object.prototype.toString.call(e)==="[object Object]"}function hl(e){return Array.isArray(e)&&e.length===Object.keys(e).length}function yn(e,t,n){if(e===t)return!0;if(typeof e!=typeof t)return!1;if(Array.isArray(e)&&Array.isArray(t)){if(e.length!==t.length)return!1;for(let s=0,r=e.length;sr||!yn(e[o],t[o],n)))return!1;return r===i}return!1}function zn(e){let t,n;const s=new Promise((r,i)=>{t=r,n=i});return s.status="pending",s.resolve=r=>{s.status="resolved",s.value=r,t(r),e?.(r)},s.reject=r=>{s.status="rejected",n(r)},s}function Es(e){return!!(e&&typeof e=="object"&&typeof e.then=="function")}function bm(e){return e.replace(/[\x00-\x1f\x7f]/g,"")}function gl(e){let t;try{t=decodeURI(e)}catch{t=e.replaceAll(/%[0-9A-F]{2}/gi,n=>{try{return decodeURI(n)}catch{return n}})}return bm(t)}var _m=["http:","https:","mailto:","tel:"];function mr(e,t){if(!e)return!1;try{const n=new URL(e);return!t.has(n.protocol)}catch{return!1}}function is(e){if(!e)return{path:e,handledProtocolRelativeURL:!1};if(!/[%\\\x00-\x1f\x7f]/.test(e)&&!e.startsWith("//"))return{path:e,handledProtocolRelativeURL:!1};const t=/%25|%5C/gi;let n=0,s="",r;for(;(r=t.exec(e))!==null;)s+=gl(e.slice(n,r.index))+r[0],n=t.lastIndex;s=s+gl(n?e.slice(n):e);let i=!1;return s.startsWith("//")&&(i=!0,s="/"+s.replace(/^\/+/,"")),{path:s,handledProtocolRelativeURL:i}}function wm(e){return/\s|[^\u0000-\u007F]/.test(e)?e.replace(/\s|[^\u0000-\u007F]/gu,encodeURIComponent):e}function Sm(e,t){if(e===t)return!0;if(e.length!==t.length)return!1;for(let n=0;n{i.next&&(i.prev?(i.prev.next=i.next,i.next.prev=i.prev,i.next=void 0,s&&(s.next=i,i.prev=s)):(i.next.prev=void 0,n=i.next,i.next=void 0,s&&(i.prev=s,s.next=i)),s=i)};return{get(i){const o=t.get(i);if(o)return r(o),o.value},set(i,o){if(t.size>=e&&n){const l=n;t.delete(l.key),l.next&&(n=l.next,l.next.prev=void 0),l===s&&(s=void 0)}const a=t.get(i);if(a)a.value=o,r(a);else{const l={key:i,value:o,prev:s};s&&(s.next=l),s=l,n||(n=l),t.set(i,l)}},clear(){t.clear(),n=void 0,s=void 0}}}var hn=4,Lu=5;function xm(e){const t=e.indexOf("{");if(t===-1)return null;const n=e.indexOf("}",t);return n===-1||t+1>=e.length?null:[t,n]}function xo(e,t,n=new Uint16Array(6)){const s=e.indexOf("/",t),r=s===-1?e.length:s,i=e.substring(t,r);if(!i||!i.includes("$"))return n[0]=0,n[1]=t,n[2]=t,n[3]=r,n[4]=r,n[5]=r,n;if(i==="$"){const a=e.length;return n[0]=2,n[1]=t,n[2]=t,n[3]=a,n[4]=a,n[5]=a,n}if(i.charCodeAt(0)===36)return n[0]=1,n[1]=t,n[2]=t+1,n[3]=r,n[4]=r,n[5]=r,n;const o=xm(i);if(o){const[a,l]=o,c=i.charCodeAt(a+1);if(c===45){if(a+2!P.skipOnParamError&&P.caseSensitive===w&&P.prefix===S&&P.suffix===x);if(k)g=k;else{const P=hi(1,n.fullPath??n.from,w,S,x);g=P,P.depth=i,P.parent=r,r.dynamic??=[],r.dynamic.push(P)}break}case 3:{const v=l.substring(m,h[1]),b=l.substring(h[4],y),w=d&&!!(v||b),S=v?w?v:v.toLowerCase():void 0,x=b?w?b:b.toLowerCase():void 0,k=!u&&r.optional?.find(P=>!P.skipOnParamError&&P.caseSensitive===w&&P.prefix===S&&P.suffix===x);if(k)g=k;else{const P=hi(3,n.fullPath??n.from,w,S,x);g=P,P.parent=r,P.depth=i,r.optional??=[],r.optional.push(P)}break}case 2:{const v=l.substring(m,h[1]),b=l.substring(h[4],y),w=d&&!!(v||b),S=v?w?v:v.toLowerCase():void 0,x=b?w?b:b.toLowerCase():void 0,k=hi(2,n.fullPath??n.from,w,S,x);g=k,k.parent=r,k.depth=i,r.wildcard??=[],r.wildcard.push(k)}}r=g}if(u&&n.children&&!n.isRoot&&n.id&&n.id.charCodeAt(n.id.lastIndexOf("/")+1)===95){const h=gn(n.fullPath??n.from);h.kind=Lu,h.parent=r,i++,h.depth=i,r.pathless??=[],r.pathless.push(h),r=h}const f=(n.path||!n.children)&&!n.isRoot;if(f&&l.endsWith("/")){const h=gn(n.fullPath??n.from);h.kind=hn,h.parent=r,i++,h.depth=i,r.index=h,r=h}r.parse=n.options?.params?.parse??null,r.skipOnParamError=u,r.parsingPriority=n.options?.skipRouteOnParseError?.priority??0,f&&!r.route&&(r.route=n,r.fullPath=n.fullPath??n.from)}if(n.children)for(const l of n.children)Ar(e,t,l,a,r,i,o)}function fi(e,t){if(e.skipOnParamError&&!t.skipOnParamError)return-1;if(!e.skipOnParamError&&t.skipOnParamError)return 1;if(e.skipOnParamError&&t.skipOnParamError&&(e.parsingPriority||t.parsingPriority))return t.parsingPriority-e.parsingPriority;if(e.prefix&&t.prefix&&e.prefix!==t.prefix){if(e.prefix.startsWith(t.prefix))return-1;if(t.prefix.startsWith(e.prefix))return 1}if(e.suffix&&t.suffix&&e.suffix!==t.suffix){if(e.suffix.endsWith(t.suffix))return-1;if(t.suffix.endsWith(e.suffix))return 1}return e.prefix&&!t.prefix?-1:!e.prefix&&t.prefix?1:e.suffix&&!t.suffix?-1:!e.suffix&&t.suffix?1:e.caseSensitive&&!t.caseSensitive?-1:!e.caseSensitive&&t.caseSensitive?1:0}function jt(e){if(e.pathless)for(const t of e.pathless)jt(t);if(e.static)for(const t of e.static.values())jt(t);if(e.staticInsensitive)for(const t of e.staticInsensitive.values())jt(t);if(e.dynamic?.length){e.dynamic.sort(fi);for(const t of e.dynamic)jt(t)}if(e.optional?.length){e.optional.sort(fi);for(const t of e.optional)jt(t)}if(e.wildcard?.length){e.wildcard.sort(fi);for(const t of e.wildcard)jt(t)}}function gn(e){return{kind:0,depth:0,pathless:null,index:null,static:null,staticInsensitive:null,dynamic:null,optional:null,wildcard:null,route:null,fullPath:e,parent:null,parse:null,skipOnParamError:!1,parsingPriority:0}}function hi(e,t,n,s,r){return{kind:e,depth:0,pathless:null,index:null,static:null,staticInsensitive:null,dynamic:null,optional:null,wildcard:null,route:null,fullPath:t,parent:null,parse:null,skipOnParamError:!1,parsingPriority:0,caseSensitive:n,prefix:s,suffix:r}}function $m(e,t){const n=gn("/"),s=new Uint16Array(6);for(const r of e)Ar(!1,s,r,1,n,0);jt(n),t.masksTree=n,t.flatCache=Rs(1e3)}function Cm(e,t){e||="/";const n=t.flatCache.get(e);if(n)return n;const s=$o(e,t.masksTree);return t.flatCache.set(e,s),s}function Pm(e,t,n,s,r){e||="/",s||="/";const i=t?`case\0${e}`:e;let o=r.singleCache.get(i);return o||(o=gn("/"),Ar(t,new Uint16Array(6),{from:e},1,o,0),r.singleCache.set(i,o)),$o(s,o,n)}function km(e,t,n=!1){const s=n?e:`nofuzz\0${e}`,r=t.matchCache.get(s);if(r!==void 0)return r;e||="/";let i;try{i=$o(e,t.segmentTree,n)}catch(o){if(o instanceof URIError)i=null;else throw o}return i&&(i.branch=Om(i.route)),t.matchCache.set(s,i),i}function Em(e){return e==="/"?e:e.replace(/\/{1,}$/,"")}function Rm(e,t=!1,n){const s=gn(e.fullPath),r=new Uint16Array(6),i={},o={};let a=0;return Ar(t,r,e,1,s,0,l=>{if(n?.(l,a),l.id in i&&Wn(),i[l.id]=l,a!==0&&l.path){const c=Em(l.fullPath);(!o[c]||l.fullPath.endsWith("/"))&&(o[c]=l)}a++}),jt(s),{processedTree:{segmentTree:s,singleCache:Rs(1e3),matchCache:Rs(1e3),flatCache:null,masksTree:null},routesById:i,routesByPath:o}}function $o(e,t,n=!1){const s=e.split("/"),r=Tm(e,s,t,n);if(!r)return null;const[i]=Tu(e,s,r);return{route:r.node.route,rawParams:i,parsedParams:r.parsedParams}}function Tu(e,t,n){const s=Lm(n.node);let r=null;const i=Object.create(null);let o=n.extract?.part??0,a=n.extract?.node??0,l=n.extract?.path??0,c=n.extract?.segment??0;for(;a=0;C--){const I=f.optional[C];a.push({node:I,index:h,skipped:O,depth:$,statics:y,dynamics:v,optionals:b,extract:w,rawParams:S,parsedParams:x})}if(!k)for(let C=f.optional.length-1;C>=0;C--){const I=f.optional[C],{prefix:T,suffix:L}=I;if(T||L){const M=I.caseSensitive?P:E??=P.toLowerCase();if(T&&!M.startsWith(T)||L&&!M.endsWith(L))continue}a.push({node:I,index:h+1,skipped:g,depth:$,statics:y,dynamics:v,optionals:b+1,extract:w,rawParams:S,parsedParams:x})}}if(!k&&f.dynamic&&P)for(let O=f.dynamic.length-1;O>=0;O--){const $=f.dynamic[O],{prefix:C,suffix:I}=$;if(C||I){const T=$.caseSensitive?P:E??=P.toLowerCase();if(C&&!T.startsWith(C)||I&&!T.endsWith(I))continue}a.push({node:$,index:h+1,skipped:g,depth:m+1,statics:y,dynamics:v+1,optionals:b,extract:w,rawParams:S,parsedParams:x})}if(!k&&f.staticInsensitive){const O=f.staticInsensitive.get(E??=P.toLowerCase());O&&a.push({node:O,index:h+1,skipped:g,depth:m+1,statics:y+1,dynamics:v,optionals:b,extract:w,rawParams:S,parsedParams:x})}if(!k&&f.static){const O=f.static.get(P);O&&a.push({node:O,index:h+1,skipped:g,depth:m+1,statics:y+1,dynamics:v,optionals:b,extract:w,rawParams:S,parsedParams:x})}if(f.pathless){const O=m+1;for(let $=f.pathless.length-1;$>=0;$--){const C=f.pathless[$];a.push({node:C,index:h,skipped:g,depth:O,statics:y,dynamics:v,optionals:b,extract:w,rawParams:S,parsedParams:x})}}}if(d&&l)return os(l,d)?d:l;if(d)return d;if(l)return l;if(s&&c){let u=c.index;for(let h=0;he.statics||t.statics===e.statics&&(t.dynamics>e.dynamics||t.dynamics===e.dynamics&&(t.optionals>e.optionals||t.optionals===e.optionals&&((t.node.kind===hn)>(e.node.kind===hn)||t.node.kind===hn==(e.node.kind===hn)&&t.depth>e.depth))):!0}function Zs(e){return Co(e.filter(t=>t!==void 0).join("/"))}function Co(e){return e.replace(/\/{2,}/g,"/")}function Iu(e){return e==="/"?e:e.replace(/^\/{1,}/,"")}function Vt(e){const t=e.length;return t>1&&e[t-1]==="/"?e.replace(/\/{1,}$/,""):e}function Au(e){return Vt(Iu(e))}function vr(e,t){return e?.endsWith("/")&&e!=="/"&&e!==`${t}/`?e.slice(0,-1):e}function Im(e,t,n){return vr(e,n)===vr(t,n)}function Am({base:e,to:t,trailingSlash:n="never",cache:s}){const r=t.startsWith("/"),i=!r&&t===".";let o;if(s){o=r?t:i?e:e+"\0"+t;const u=s.get(o);if(u)return u}let a;if(i)a=e.split("/");else if(r)a=t.split("/");else{for(a=e.split("/");a.length>1&&ks(a)==="";)a.pop();const u=t.split("/");for(let f=0,h=u.length;f1&&(ks(a)===""?n==="never"&&a.pop():n==="always"&&a.push(""));let l,c="";for(let u=0;u0&&(c+="/");const f=a[u];if(!f)continue;l=xo(f,0,l);const h=l[0];if(h===0){c+=f;continue}const g=l[5],m=f.substring(0,l[1]),y=f.substring(l[4],g),v=f.substring(l[2],l[3]);h===1?c+=m||y?`${m}{$${v}}${y}`:`$${v}`:h===2?c+=m||y?`${m}{$}${y}`:"$":c+=`${m}{-$${v}}${y}`}c=Co(c);const d=c||"/";return o&&s&&s.set(o,d),d}function Mm(e){const t=new Map(e.map(r=>[encodeURIComponent(r),r])),n=Array.from(t.keys()).map(r=>r.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")).join("|"),s=new RegExp(n,"g");return r=>r.replace(s,i=>t.get(i)??i)}function pi(e,t,n){const s=t[e];return typeof s!="string"?s:e==="_splat"?/^[a-zA-Z0-9\-._~!/]*$/.test(s)?s:s.split("/").map(r=>ml(r,n)).join("/"):ml(s,n)}function pl({path:e,params:t,decoder:n,...s}){let r=!1;const i=Object.create(null);if(!e||e==="/")return{interpolatedPath:"/",usedParams:i,isMissingParams:r};if(!e.includes("$"))return{interpolatedPath:e,usedParams:i,isMissingParams:r};const o=e.length;let a=0,l,c="";for(;a{let n;return(...s)=>{n||(n=setTimeout(()=>{e(...s),n=null},t))}};function Nm(){const e=Fm();if(!e)return null;const t=e.getItem(Wi);let n=t?JSON.parse(t):{};return{state:n,set:s=>{n=Kt(s,n)||n;try{e.setItem(Wi,JSON.stringify(n))}catch{console.warn("[ts-router] Could not persist scroll restoration state to sessionStorage.")}}}}var Ws=Nm(),jm=e=>e.state.__TSR_key||e.href;function Km(e){const t=[];let n;for(;n=e.parentNode;)t.push(`${e.tagName}:nth-child(${Array.prototype.indexOf.call(n.children,e)+1})`),e=n;return`${t.reverse().join(" > ")}`.toLowerCase()}var yr=!1;function Bm({storageKey:e,key:t,behavior:n,shouldScrollRestoration:s,scrollToTopSelectors:r,location:i}){let o;try{o=JSON.parse(sessionStorage.getItem(e)||"{}")}catch(c){console.error(c);return}const a=t||window.history.state?.__TSR_key,l=o[a];yr=!0;e:{if(s&&l&&Object.keys(l).length>0){for(const u in l){const f=l[u];if(u==="window")window.scrollTo({top:f.scrollY,left:f.scrollX,behavior:n});else if(u){const h=document.querySelector(u);h&&(h.scrollLeft=f.scrollX,h.scrollTop=f.scrollY)}}break e}const c=(i??window.location).hash.split("#",2)[1];if(c){const u=window.history.state?.__hashScrollIntoViewOptions??!0;if(u){const f=document.getElementById(c);f&&f.scrollIntoView(u)}break e}const d={top:0,left:0,behavior:n};if(window.scrollTo(d),r)for(const u of r){if(u==="window")continue;const f=typeof u=="function"?u():document.querySelector(u);f&&f.scrollTo(d)}}yr=!1}function Um(e,t){if(!Ws||((e.options.scrollRestoration??!1)&&(e.isScrollRestoring=!0),e.isScrollRestorationSetup||!Ws))return;e.isScrollRestorationSetup=!0,yr=!1;const n=e.options.getScrollRestorationKey||jm;window.history.scrollRestoration="manual";const s=r=>{if(yr||!e.isScrollRestoring)return;let i="";if(r.target===document||r.target===window)i="window";else{const a=r.target.getAttribute("data-scroll-restoration-id");a?i=`[data-scroll-restoration-id="${a}"]`:i=Km(r.target)}const o=n(e.stores.location.state);Ws.set(a=>{const l=a[o]||={},c=l[i]||={};if(i==="window")c.scrollX=window.scrollX||0,c.scrollY=window.scrollY||0;else if(i){const d=document.querySelector(i);d&&(c.scrollX=d.scrollLeft||0,c.scrollY=d.scrollTop||0)}return a})};typeof document<"u"&&document.addEventListener("scroll",Dm(s,100),!0),e.subscribe("onRendered",r=>{const i=n(r.toLocation);if(!e.resetNextScroll){e.resetNextScroll=!0;return}typeof e.options.scrollRestoration=="function"&&!e.options.scrollRestoration({location:e.latestLocation})||(Bm({storageKey:Wi,key:i,behavior:e.options.scrollRestorationBehavior,shouldScrollRestoration:e.isScrollRestoring,scrollToTopSelectors:e.options.scrollToTopSelectors,location:e.history.location}),e.isScrollRestoring&&Ws.set(o=>(o[i]||={},o)))})}function Vm(e){if(typeof document<"u"&&document.querySelector){const t=e.stores.location.state,n=t.state.__hashScrollIntoViewOptions??!0;if(n&&t.hash!==""){const s=document.getElementById(t.hash);s&&s.scrollIntoView(n)}}}function qm(e,t=String){const n=new URLSearchParams;for(const s in e){const r=e[s];r!==void 0&&n.set(s,t(r))}return n.toString()}function mi(e){return e?e==="false"?!1:e==="true"?!0:+e*0===0&&+e+""===e?+e:e:""}function Hm(e){const t=new URLSearchParams(e),n=Object.create(null);for(const[s,r]of t.entries()){const i=n[s];i==null?n[s]=mi(r):Array.isArray(i)?i.push(mi(r)):n[s]=[i,mi(r)]}return n}var zm=Qm(JSON.parse),Wm=Gm(JSON.stringify,JSON.parse);function Qm(e){return t=>{t[0]==="?"&&(t=t.substring(1));const n=Hm(t);for(const s in n){const r=n[s];if(typeof r=="string")try{n[s]=e(r)}catch{}}return n}}function Gm(e,t){const n=typeof t=="function";function s(r){if(typeof r=="object"&&r!==null)try{return e(r)}catch{}else if(n&&typeof r=="string")try{return t(r),e(r)}catch{}return r}return r=>{const i=qm(r,s);return i?`?${i}`:""}}var xt="__root__";function Jm(e){if(e.statusCode=e.statusCode||e.code||307,!e._builtLocation&&!e.reloadDocument&&typeof e.href=="string")try{new URL(e.href),e.reloadDocument=!0}catch{}const t=new Headers(e.headers);e.href&&t.get("Location")===null&&t.set("Location",e.href);const n=new Response(null,{status:e.statusCode,headers:t});if(n.options=e,e.throw)throw n;return n}function Je(e){return e instanceof Response&&!!e.options}function Ym(e){return{input:({url:t})=>{for(const n of e)t=Qi(n,t);return t},output:({url:t})=>{for(let n=e.length-1;n>=0;n--)t=Mu(e[n],t);return t}}}function Xm(e){const t=Au(e.basepath),n=`/${t}`,s=`${n}/`,r=e.caseSensitive?n:n.toLowerCase(),i=e.caseSensitive?s:s.toLowerCase();return{input:({url:o})=>{const a=e.caseSensitive?o.pathname:o.pathname.toLowerCase();return a===r?o.pathname="/":a.startsWith(i)&&(o.pathname=o.pathname.slice(n.length)),o},output:({url:o})=>(o.pathname=Zs(["/",t,o.pathname]),o)}}function Qi(e,t){const n=e?.input?.({url:t});if(n){if(typeof n=="string")return new URL(n);if(n instanceof URL)return n}return t}function Mu(e,t){const n=e?.output?.({url:t});if(n){if(typeof n=="string")return new URL(n);if(n instanceof URL)return n}return t}function Zm(e,t){const{createMutableStore:n,createReadonlyStore:s,batch:r,init:i}=t,o=new Map,a=new Map,l=new Map,c=n(e.status),d=n(e.loadedAt),u=n(e.isLoading),f=n(e.isTransitioning),h=n(e.location),g=n(e.resolvedLocation),m=n(e.statusCode),y=n(e.redirect),v=n([]),b=n([]),w=n([]),S=s(()=>vi(o,v.state)),x=s(()=>vi(a,b.state)),k=s(()=>vi(l,w.state)),P=s(()=>v.state[0]),E=s(()=>v.state.some(H=>o.get(H)?.state.status==="pending")),O=s(()=>({locationHref:h.state.href,resolvedLocationHref:g.state?.href,status:c.state})),$=s(()=>({status:c.state,loadedAt:d.state,isLoading:u.state,isTransitioning:f.state,matches:S.state,location:h.state,resolvedLocation:g.state,statusCode:m.state,redirect:y.state})),C=Rs(64);function I(H){let se=C.get(H);return se||(se=s(()=>{const ne=v.state;for(const _e of ne){const V=o.get(_e);if(V&&V.routeId===H)return V.state}}),C.set(H,se)),se}const T={status:c,loadedAt:d,isLoading:u,isTransitioning:f,location:h,resolvedLocation:g,statusCode:m,redirect:y,matchesId:v,pendingMatchesId:b,cachedMatchesId:w,activeMatchesSnapshot:S,pendingMatchesSnapshot:x,cachedMatchesSnapshot:k,firstMatchId:P,hasPendingMatches:E,matchRouteReactivity:O,activeMatchStoresById:o,pendingMatchStoresById:a,cachedMatchStoresById:l,__store:$,getMatchStoreByRouteId:I,setActiveMatches:L,setPendingMatches:M,setCachedMatches:W};L(e.matches),i?.(T);function L(H){yi(H,o,v,n,r)}function M(H){yi(H,a,b,n,r)}function W(H){yi(H,l,w,n,r)}return T}function vi(e,t){const n=[];for(const s of t){const r=e.get(s);r&&n.push(r.state)}return n}function yi(e,t,n,s,r){const i=e.map(a=>a.id),o=new Set(i);r(()=>{for(const a of t.keys())o.has(a)||t.delete(a);for(const a of e){const l=t.get(a.id);if(!l){const c=s(a);c.routeId=a.routeId,t.set(a.id,c);continue}l.routeId=a.routeId,l.state!==a&&l.setState(()=>a)}Sm(n.state,i)||n.setState(()=>i)})}var Gi=e=>{if(!e.rendered)return e.rendered=!0,e.onReady?.()},ev=e=>e.stores.matchesId.state.some(t=>e.stores.activeMatchStoresById.get(t)?.state._forcePending),Mr=(e,t)=>!!(e.preload&&!e.router.stores.activeMatchStoresById.has(t)),bn=(e,t,n=!0)=>{const s={...e.router.options.context??{}},r=n?t:t-1;for(let i=0;i<=r;i++){const o=e.matches[i];if(!o)continue;const a=e.router.getMatch(o.id);a&&Object.assign(s,a.__routeContext,a.__beforeLoadContext)}return s},vl=(e,t)=>{if(!e.matches.length)return;const n=t.routeId,s=e.matches.findIndex(o=>o.routeId===e.router.routeTree.id),r=s>=0?s:0;let i=n?e.matches.findIndex(o=>o.routeId===n):e.firstBadMatchIndex??e.matches.length-1;i<0&&(i=r);for(let o=i;o>=0;o--){const a=e.matches[o];if(e.router.looseRoutesById[a.routeId].options.notFoundComponent)return o}return n?i:r},Bt=(e,t,n)=>{if(!(!Je(n)&&!Ge(n)))throw Je(n)&&n.redirectHandled&&!n.options.reloadDocument||(t&&(t._nonReactive.beforeLoadPromise?.resolve(),t._nonReactive.loaderPromise?.resolve(),t._nonReactive.beforeLoadPromise=void 0,t._nonReactive.loaderPromise=void 0,t._nonReactive.error=n,e.updateMatch(t.id,s=>({...s,status:Je(n)?"redirected":s.status==="pending"?"success":s.status,context:bn(e,t.index),isFetching:!1,error:n})),Ge(n)&&!n.routeId&&(n.routeId=t.routeId),t._nonReactive.loadPromise?.resolve()),Je(n)&&(e.rendered=!0,n.options._fromLocation=e.location,n.redirectHandled=!0,n=e.router.resolveRedirect(n))),n},Fu=(e,t)=>{const n=e.router.getMatch(t);return!!(!n||n._nonReactive.dehydrated)},yl=(e,t,n)=>{const s=bn(e,n);e.updateMatch(t,r=>({...r,context:s}))},as=(e,t,n,s)=>{const{id:r,routeId:i}=e.matches[t],o=e.router.looseRoutesById[i];if(n instanceof Promise)throw n;n.routerCode=s,e.firstBadMatchIndex??=t,Bt(e,e.router.getMatch(r),n);try{o.options.onError?.(n)}catch(a){n=a,Bt(e,e.router.getMatch(r),n)}e.updateMatch(r,a=>(a._nonReactive.beforeLoadPromise?.resolve(),a._nonReactive.beforeLoadPromise=void 0,a._nonReactive.loadPromise?.resolve(),{...a,error:n,status:"error",isFetching:!1,updatedAt:Date.now(),abortController:new AbortController})),!e.preload&&!Je(n)&&!Ge(n)&&(e.serialError??=n)},Du=(e,t,n,s)=>{if(s._nonReactive.pendingTimeout!==void 0)return;const r=n.options.pendingMs??e.router.options.defaultPendingMs;if(e.onReady&&!Mr(e,t)&&(n.options.loader||n.options.beforeLoad||ju(n))&&typeof r=="number"&&r!==1/0&&(n.options.pendingComponent??e.router.options?.defaultPendingComponent)){const i=setTimeout(()=>{Gi(e)},r);s._nonReactive.pendingTimeout=i}},tv=(e,t,n)=>{const s=e.router.getMatch(t);if(!s._nonReactive.beforeLoadPromise&&!s._nonReactive.loaderPromise)return;Du(e,t,n,s);const r=()=>{const i=e.router.getMatch(t);i.preload&&(i.status==="redirected"||i.status==="notFound")&&Bt(e,i,i.error)};return s._nonReactive.beforeLoadPromise?s._nonReactive.beforeLoadPromise.then(r):r()},nv=(e,t,n,s)=>{const r=e.router.getMatch(t);let i=r._nonReactive.loadPromise;r._nonReactive.loadPromise=zn(()=>{i?.resolve(),i=void 0});const{paramsError:o,searchError:a}=r;o&&as(e,n,o,"PARSE_PARAMS"),a&&as(e,n,a,"VALIDATE_SEARCH"),Du(e,t,s,r);const l=new AbortController;let c=!1;const d=()=>{c||(c=!0,e.updateMatch(t,S=>({...S,isFetching:"beforeLoad",fetchCount:S.fetchCount+1,abortController:l})))},u=()=>{r._nonReactive.beforeLoadPromise?.resolve(),r._nonReactive.beforeLoadPromise=void 0,e.updateMatch(t,S=>({...S,isFetching:!1}))};if(!s.options.beforeLoad){e.router.batch(()=>{d(),u()});return}r._nonReactive.beforeLoadPromise=zn();const f={...bn(e,n,!1),...r.__routeContext},{search:h,params:g,cause:m}=r,y=Mr(e,t),v={search:h,abortController:l,params:g,preload:y,context:f,location:e.location,navigate:S=>e.router.navigate({...S,_fromLocation:e.location}),buildLocation:e.router.buildLocation,cause:y?"preload":m,matches:e.matches,routeId:s.id,...e.router.options.additionalContext},b=S=>{if(S===void 0){e.router.batch(()=>{d(),u()});return}(Je(S)||Ge(S))&&(d(),as(e,n,S,"BEFORE_LOAD")),e.router.batch(()=>{d(),e.updateMatch(t,x=>({...x,__beforeLoadContext:S})),u()})};let w;try{if(w=s.options.beforeLoad(v),Es(w))return d(),w.catch(S=>{as(e,n,S,"BEFORE_LOAD")}).then(b)}catch(S){d(),as(e,n,S,"BEFORE_LOAD")}b(w)},sv=(e,t)=>{const{id:n,routeId:s}=e.matches[t],r=e.router.looseRoutesById[s],i=()=>a(),o=()=>nv(e,n,t,r),a=()=>{if(Fu(e,n))return;const l=tv(e,n,r);return Es(l)?l.then(o):o()};return i()},rv=(e,t,n)=>{const s=e.router.getMatch(t);if(!s||!n.options.head&&!n.options.scripts&&!n.options.headers)return;const r={ssr:e.router.options.ssr,matches:e.matches,match:s,params:s.params,loaderData:s.loaderData};return Promise.all([n.options.head?.(r),n.options.scripts?.(r),n.options.headers?.(r)]).then(([i,o,a])=>({meta:i?.meta,links:i?.links,headScripts:i?.scripts,headers:a,scripts:o,styles:i?.styles}))},Nu=(e,t,n,s,r)=>{const i=t[s-1],{params:o,loaderDeps:a,abortController:l,cause:c}=e.router.getMatch(n),d=bn(e,s),u=Mr(e,n);return{params:o,deps:a,preload:!!u,parentMatchPromise:i,abortController:l,context:d,location:e.location,navigate:f=>e.router.navigate({...f,_fromLocation:e.location}),cause:u?"preload":c,route:r,...e.router.options.additionalContext}},bl=async(e,t,n,s,r)=>{try{const i=e.router.getMatch(n);try{(!(pm??e.router.isServer)||i.ssr===!0)&&Os(r);const o=r.options.loader,a=typeof o=="function"?o:o?.handler,l=a?.(Nu(e,t,n,s,r)),c=!!a&&Es(l);if((c||r._lazyPromise||r._componentsPromise||r.options.head||r.options.scripts||r.options.headers||i._nonReactive.minPendingPromise)&&e.updateMatch(n,u=>({...u,isFetching:"loader"})),a){const u=c?await l:l;Bt(e,e.router.getMatch(n),u),u!==void 0&&e.updateMatch(n,f=>({...f,loaderData:u}))}r._lazyPromise&&await r._lazyPromise;const d=i._nonReactive.minPendingPromise;d&&await d,r._componentsPromise&&await r._componentsPromise,e.updateMatch(n,u=>({...u,error:void 0,context:bn(e,s),status:"success",isFetching:!1,updatedAt:Date.now()}))}catch(o){let a=o;if(a?.name==="AbortError"){if(i.abortController.signal.aborted){i._nonReactive.loaderPromise?.resolve(),i._nonReactive.loaderPromise=void 0;return}e.updateMatch(n,c=>({...c,status:c.status==="pending"?"success":c.status,isFetching:!1,context:bn(e,s)}));return}const l=i._nonReactive.minPendingPromise;l&&await l,Ge(o)&&await r.options.notFoundComponent?.preload?.(),Bt(e,e.router.getMatch(n),o);try{r.options.onError?.(o)}catch(c){a=c,Bt(e,e.router.getMatch(n),c)}!Je(a)&&!Ge(a)&&await Os(r,["errorComponent"]),e.updateMatch(n,c=>({...c,error:a,context:bn(e,s),status:"error",isFetching:!1}))}}catch(i){const o=e.router.getMatch(n);o&&(o._nonReactive.loaderPromise=void 0),Bt(e,o,i)}},iv=async(e,t,n)=>{async function s(h,g,m,y,v){const b=Date.now()-g.updatedAt,w=h?v.options.preloadStaleTime??e.router.options.defaultPreloadStaleTime??3e4:v.options.staleTime??e.router.options.defaultStaleTime??0,S=v.options.shouldReload,x=typeof S=="function"?S(Nu(e,t,r,n,v)):S,{status:k,invalid:P}=y,E=b>=w&&(!!e.forceStaleReload||y.cause==="enter"||m!==void 0&&m!==y.id);o=k==="success"&&(P||(x??E)),h&&v.options.preload===!1||(o&&!e.sync&&d?(a=!0,(async()=>{try{await bl(e,t,r,n,v);const O=e.router.getMatch(r);O._nonReactive.loaderPromise?.resolve(),O._nonReactive.loadPromise?.resolve(),O._nonReactive.loaderPromise=void 0,O._nonReactive.loadPromise=void 0}catch(O){Je(O)&&await e.router.navigate(O.options)}})()):k!=="success"||o?await bl(e,t,r,n,v):yl(e,r,n))}const{id:r,routeId:i}=e.matches[n];let o=!1,a=!1;const l=e.router.looseRoutesById[i],c=l.options.loader,d=((typeof c=="function"?void 0:c?.staleReloadMode)??e.router.options.defaultStaleReloadMode)!=="blocking";if(Fu(e,r)){if(!e.router.getMatch(r))return e.matches[n];yl(e,r,n)}else{const h=e.router.getMatch(r),g=e.router.stores.matchesId.state[n],m=(g&&e.router.stores.activeMatchStoresById.get(g)||null)?.routeId===i?g:e.router.stores.activeMatchesSnapshot.state.find(v=>v.routeId===i)?.id,y=Mr(e,r);if(h._nonReactive.loaderPromise){if(h.status==="success"&&!e.sync&&!h.preload&&d)return h;await h._nonReactive.loaderPromise;const v=e.router.getMatch(r),b=v._nonReactive.error||v.error;b&&Bt(e,v,b),v.status==="pending"&&await s(y,h,m,v,l)}else{const v=y&&!e.router.stores.activeMatchStoresById.has(r),b=e.router.getMatch(r);b._nonReactive.loaderPromise=zn(),v!==b.preload&&e.updateMatch(r,w=>({...w,preload:v})),await s(y,h,m,b,l)}}const u=e.router.getMatch(r);a||(u._nonReactive.loaderPromise?.resolve(),u._nonReactive.loadPromise?.resolve(),u._nonReactive.loadPromise=void 0),clearTimeout(u._nonReactive.pendingTimeout),u._nonReactive.pendingTimeout=void 0,a||(u._nonReactive.loaderPromise=void 0),u._nonReactive.dehydrated=void 0;const f=a?u.isFetching:!1;return f!==u.isFetching||u.invalid!==!1?(e.updateMatch(r,h=>({...h,isFetching:f,invalid:!1})),e.router.getMatch(r)):u};async function _l(e){const t=e,n=[];ev(t.router)&&Gi(t);let s;for(let f=0;f({...v,...y?{status:"success",globalNotFound:!0,error:void 0}:{status:"notFound",error:c},isFetching:!1})),d=f,await Os(g,["notFoundComponent"])}else if(!t.preload){const f=t.matches[0];f.globalNotFound||t.router.getMatch(f.id)?.globalNotFound&&t.updateMatch(f.id,h=>({...h,globalNotFound:!1,error:void 0}))}if(t.serialError&&t.firstBadMatchIndex!==void 0){const f=t.router.looseRoutesById[t.matches[t.firstBadMatchIndex].routeId];await Os(f,["errorComponent"])}for(let f=0;f<=d;f++){const{id:h,routeId:g}=t.matches[f],m=t.router.looseRoutesById[g];try{const y=rv(t,h,m);if(y){const v=await y;t.updateMatch(h,b=>({...b,...v}))}}catch(y){console.error(`Error executing head for route ${g}:`,y)}}const u=Gi(t);if(Es(u)&&await u,c)throw c;if(t.serialError&&!t.preload&&!t.onReady)throw t.serialError;return t.matches}function wl(e,t){const n=t.map(s=>e.options[s]?.preload?.()).filter(Boolean);if(n.length!==0)return Promise.all(n)}function Os(e,t=er){!e._lazyLoaded&&e._lazyPromise===void 0&&(e.lazyFn?e._lazyPromise=e.lazyFn().then(s=>{const{id:r,...i}=s.options;Object.assign(e.options,i),e._lazyLoaded=!0,e._lazyPromise=void 0}):e._lazyLoaded=!0);const n=()=>e._componentsLoaded?void 0:t===er?(()=>{if(e._componentsPromise===void 0){const s=wl(e,er);s?e._componentsPromise=s.then(()=>{e._componentsLoaded=!0,e._componentsPromise=void 0}):e._componentsLoaded=!0}return e._componentsPromise})():wl(e,t);return e._lazyPromise?e._lazyPromise.then(n):n()}function ju(e){for(const t of er)if(e.options[t]?.preload)return!0;return!1}var er=["component","errorComponent","pendingComponent","notFoundComponent"],qt="__TSR_index",Sl="popstate",xl="beforeunload";function ov(e){let t=e.getLocation();const n=new Set,s=o=>{t=e.getLocation(),n.forEach(a=>a({location:t,action:o}))},r=o=>{e.notifyOnIndexChange??!0?s(o):t=e.getLocation()},i=async({task:o,navigateOpts:a,...l})=>{if(a?.ignoreBlocker??!1){o();return}const c=e.getBlockers?.()??[],d=l.type==="PUSH"||l.type==="REPLACE";if(typeof document<"u"&&c.length&&d)for(const u of c){const f=br(l.path,l.state);if(await u.blockerFn({currentLocation:t,nextLocation:f,action:l.type})){e.onBlocked?.();return}}o()};return{get location(){return t},get length(){return e.getLength()},subscribers:n,subscribe:o=>(n.add(o),()=>{n.delete(o)}),push:(o,a,l)=>{const c=t.state[qt];a=$l(c+1,a),i({task:()=>{e.pushState(o,a),s({type:"PUSH"})},navigateOpts:l,type:"PUSH",path:o,state:a})},replace:(o,a,l)=>{const c=t.state[qt];a=$l(c,a),i({task:()=>{e.replaceState(o,a),s({type:"REPLACE"})},navigateOpts:l,type:"REPLACE",path:o,state:a})},go:(o,a)=>{i({task:()=>{e.go(o),r({type:"GO",index:o})},navigateOpts:a,type:"GO"})},back:o=>{i({task:()=>{e.back(o?.ignoreBlocker??!1),r({type:"BACK"})},navigateOpts:o,type:"BACK"})},forward:o=>{i({task:()=>{e.forward(o?.ignoreBlocker??!1),r({type:"FORWARD"})},navigateOpts:o,type:"FORWARD"})},canGoBack:()=>t.state[qt]!==0,createHref:o=>e.createHref(o),block:o=>{if(!e.setBlockers)return()=>{};const a=e.getBlockers?.()??[];return e.setBlockers([...a,o]),()=>{const l=e.getBlockers?.()??[];e.setBlockers?.(l.filter(c=>c!==o))}},flush:()=>e.flush?.(),destroy:()=>e.destroy?.(),notify:s}}function $l(e,t){t||(t={});const n=Po();return{...t,key:n,__TSR_key:n,[qt]:e}}function av(e){const t=typeof document<"u"?window:void 0,n=t.history.pushState,s=t.history.replaceState;let r=[];const i=()=>r,o=E=>r=E,a=(E=>E),l=(()=>br(`${t.location.pathname}${t.location.search}${t.location.hash}`,t.history.state));if(!t.history.state?.__TSR_key&&!t.history.state?.key){const E=Po();t.history.replaceState({[qt]:0,key:E,__TSR_key:E},"")}let c=l(),d,u=!1,f=!1,h=!1,g=!1;const m=()=>c;let y,v;const b=()=>{y&&(P._ignoreSubscribers=!0,(y.isPush?t.history.pushState:t.history.replaceState)(y.state,"",y.href),P._ignoreSubscribers=!1,y=void 0,v=void 0,d=void 0)},w=(E,O,$)=>{const C=a(O);v||(d=c),c=br(O,$),y={href:C,state:$,isPush:y?.isPush||E==="push"},v||(v=Promise.resolve().then(()=>b()))},S=E=>{c=l(),P.notify({type:E})},x=async()=>{if(f){f=!1;return}const E=l(),O=E.state[qt]-c.state[qt],$=O===1,C=O===-1,I=!$&&!C||u;u=!1;const T=I?"GO":C?"BACK":"FORWARD",L=I?{type:"GO",index:O}:{type:C?"BACK":"FORWARD"};if(h)h=!1;else{const M=i();if(typeof document<"u"&&M.length){for(const W of M)if(await W.blockerFn({currentLocation:c,nextLocation:E,action:T})){f=!0,t.history.go(1),P.notify(L);return}}}c=l(),P.notify(L)},k=E=>{if(g){g=!1;return}let O=!1;const $=i();if(typeof document<"u"&&$.length)for(const C of $){const I=C.enableBeforeUnload??!0;if(I===!0){O=!0;break}if(typeof I=="function"&&I()===!0){O=!0;break}}if(O)return E.preventDefault(),E.returnValue=""},P=ov({getLocation:m,getLength:()=>t.history.length,pushState:(E,O)=>w("push",E,O),replaceState:(E,O)=>w("replace",E,O),back:E=>(E&&(h=!0),g=!0,t.history.back()),forward:E=>{E&&(h=!0),g=!0,t.history.forward()},go:E=>{u=!0,t.history.go(E)},createHref:E=>a(E),flush:b,destroy:()=>{t.history.pushState=n,t.history.replaceState=s,t.removeEventListener(xl,k,{capture:!0}),t.removeEventListener(Sl,x)},onBlocked:()=>{d&&c!==d&&(c=d)},getBlockers:i,setBlockers:o,notifyOnIndexChange:!1});return t.addEventListener(xl,k,{capture:!0}),t.addEventListener(Sl,x),t.history.pushState=function(...E){const O=n.apply(t.history,E);return P._ignoreSubscribers||S("PUSH"),O},t.history.replaceState=function(...E){const O=s.apply(t.history,E);return P._ignoreSubscribers||S("REPLACE"),O},P}function lv(e){let t=e.replace(/[\x00-\x1f\x7f]/g,"");return t.startsWith("//")&&(t="/"+t.replace(/^\/+/,"")),t}function br(e,t){const n=lv(e),s=n.indexOf("#"),r=n.indexOf("?"),i=Po();return{href:n,pathname:n.substring(0,s>0?r>0?Math.min(s,r):s:r>0?r:n.length),hash:s>-1?n.substring(s):"",search:r>-1?n.slice(r,s===-1?void 0:s):"",state:t||{[qt]:0,key:i,__TSR_key:i}}}function Po(){return(Math.random()+1).toString(36).substring(7)}function Un(e,t){const n=t,s=e;return{fromLocation:n,toLocation:s,pathChanged:n?.pathname!==s.pathname,hrefChanged:n?.href!==s.href,hashChanged:n?.hash!==s.hash}}var cv=class{constructor(e,t){this.tempLocationKey=`${Math.round(Math.random()*1e7)}`,this.resetNextScroll=!0,this.shouldViewTransition=void 0,this.isViewTransitionTypesSupported=void 0,this.subscribers=new Set,this.isScrollRestoring=!1,this.isScrollRestorationSetup=!1,this.startTransition=n=>n(),this.update=n=>{const s=this.options,r=this.basepath??s?.basepath??"/",i=this.basepath===void 0,o=s?.rewrite;if(this.options={...s,...n},this.isServer=this.options.isServer??typeof document>"u",this.protocolAllowlist=new Set(this.options.protocolAllowlist),this.options.pathParamsAllowedCharacters&&(this.pathParamsDecoder=Mm(this.options.pathParamsAllowedCharacters)),(!this.history||this.options.history&&this.options.history!==this.history)&&(this.options.history?this.history=this.options.history:this.history=av()),this.origin=this.options.origin,this.origin||(window?.origin&&window.origin!=="null"?this.origin=window.origin:this.origin="http://localhost"),this.history&&this.updateLatestLocation(),this.options.routeTree!==this.routeTree){this.routeTree=this.options.routeTree;let d;this.resolvePathCache=Rs(1e3),d=this.buildRouteTree(),this.setRoutes(d)}if(!this.stores&&this.latestLocation){const d=this.getStoreConfig(this);this.batch=d.batch,this.stores=Zm(dv(this.latestLocation),d),Um(this)}let a=!1;const l=this.options.basepath??"/",c=this.options.rewrite;if(i||r!==l||o!==c){this.basepath=l;const d=[],u=Au(l);u&&u!=="/"&&d.push(Xm({basepath:l})),c&&d.push(c),this.rewrite=d.length===0?void 0:d.length===1?d[0]:Ym(d),this.history&&this.updateLatestLocation(),a=!0}a&&this.stores&&this.stores.location.setState(()=>this.latestLocation),typeof window<"u"&&"CSS"in window&&typeof window.CSS?.supports=="function"&&(this.isViewTransitionTypesSupported=window.CSS.supports("selector(:active-view-transition-type(a)"))},this.updateLatestLocation=()=>{this.latestLocation=this.parseLocation(this.history.location,this.latestLocation)},this.buildRouteTree=()=>{const n=Rm(this.routeTree,this.options.caseSensitive,(s,r)=>{s.init({originalIndex:r})});return this.options.routeMasks&&$m(this.options.routeMasks,n.processedTree),n},this.subscribe=(n,s)=>{const r={eventType:n,fn:s};return this.subscribers.add(r),()=>{this.subscribers.delete(r)}},this.emit=n=>{this.subscribers.forEach(s=>{s.eventType===n.type&&s.fn(n)})},this.parseLocation=(n,s)=>{const r=({pathname:l,search:c,hash:d,href:u,state:f})=>{if(!this.rewrite&&!/[ \x00-\x1f\x7f\u0080-\uffff]/.test(l)){const v=this.options.parseSearch(c),b=this.options.stringifySearch(v);return{href:l+b+d,publicHref:u,pathname:is(l).path,external:!1,searchStr:b,search:an(s?.search,v),hash:is(d.slice(1)).path,state:fn(s?.state,f)}}const h=new URL(u,this.origin),g=Qi(this.rewrite,h),m=this.options.parseSearch(g.search),y=this.options.stringifySearch(m);return g.search=y,{href:g.href.replace(g.origin,""),publicHref:u,pathname:is(g.pathname).path,external:!!this.rewrite&&g.origin!==this.origin,searchStr:y,search:an(s?.search,m),hash:is(g.hash.slice(1)).path,state:fn(s?.state,f)}},i=r(n),{__tempLocation:o,__tempKey:a}=i.state;if(o&&(!a||a===this.tempLocationKey)){const l=r(o);return l.state.key=i.state.key,l.state.__TSR_key=i.state.__TSR_key,delete l.state.__tempLocation,{...l,maskedLocation:i}}return i},this.resolvePathWithBase=(n,s)=>Am({base:n,to:Co(s),trailingSlash:this.options.trailingSlash,cache:this.resolvePathCache}),this.matchRoutes=(n,s,r)=>typeof n=="string"?this.matchRoutesInternal({pathname:n,search:s},r):this.matchRoutesInternal(n,s),this.getMatchedRoutes=n=>fv({pathname:n,routesById:this.routesById,processedTree:this.processedTree}),this.cancelMatch=n=>{const s=this.getMatch(n);s&&(s.abortController.abort(),clearTimeout(s._nonReactive.pendingTimeout),s._nonReactive.pendingTimeout=void 0)},this.cancelMatches=()=>{this.stores.pendingMatchesId.state.forEach(n=>{this.cancelMatch(n)}),this.stores.matchesId.state.forEach(n=>{if(this.stores.pendingMatchStoresById.has(n))return;const s=this.stores.activeMatchStoresById.get(n)?.state;s&&(s.status==="pending"||s.isFetching==="loader")&&this.cancelMatch(n)})},this.buildLocation=n=>{const s=(i={})=>{const o=i._fromLocation||this.pendingBuiltLocation||this.latestLocation,a=this.matchRoutesLightweight(o);i.from;const l=i.unsafeRelative==="path"?o.pathname:i.from??a.fullPath,c=this.resolvePathWithBase(l,"."),d=a.search,u=Object.assign(Object.create(null),a.params),f=i.to?this.resolvePathWithBase(c,`${i.to}`):this.resolvePathWithBase(c,"."),h=i.params===!1||i.params===null?Object.create(null):(i.params??!0)===!0?u:Object.assign(u,Kt(i.params,u)),g=this.getMatchedRoutes(f);let m=g.matchedRoutes;if((!g.foundRoute||g.foundRoute.path!=="/"&&g.routeParams["**"])&&this.options.notFoundRoute&&(m=[...m,this.options.notFoundRoute]),Object.keys(h).length>0)for(const $ of m){const C=$.options.params?.stringify??$.options.stringifyParams;if(C)try{Object.assign(h,C(h))}catch{}}const y=n.leaveParams?f:is(pl({path:f,params:h,decoder:this.pathParamsDecoder,server:this.isServer}).interpolatedPath).path;let v=d;if(n._includeValidateSearch&&this.options.search?.strict){const $={};m.forEach(C=>{if(C.options.validateSearch)try{Object.assign($,tr(C.options.validateSearch,{...$,...v}))}catch{}}),v=$}v=hv({search:v,dest:i,destRoutes:m,_includeValidateSearch:n._includeValidateSearch}),v=an(d,v);const b=this.options.stringifySearch(v),w=i.hash===!0?o.hash:i.hash?Kt(i.hash,o.hash):void 0,S=w?`#${w}`:"";let x=i.state===!0?o.state:i.state?Kt(i.state,o.state):{};x=fn(o.state,x);const k=`${y}${b}${S}`;let P,E,O=!1;if(this.rewrite){const $=new URL(k,this.origin),C=Mu(this.rewrite,$);P=$.href.replace($.origin,""),C.origin!==this.origin?(E=C.href,O=!0):E=C.pathname+C.search+C.hash}else P=wm(k),E=P;return{publicHref:E,href:P,pathname:y,search:v,searchStr:b,state:x,hash:w??"",external:O,unmaskOnReload:i.unmaskOnReload}},r=(i={},o)=>{const a=s(i);let l=o?s(o):void 0;if(!l){const c=Object.create(null);if(this.options.routeMasks){const d=Cm(a.pathname,this.processedTree);if(d){Object.assign(c,d.rawParams);const{from:u,params:f,...h}=d.route,g=f===!1||f===null?Object.create(null):(f??!0)===!0?c:Object.assign(c,Kt(f,c));o={from:n.from,...h,params:g},l=s(o)}}}return l&&(a.maskedLocation=l),a};return n.mask?r(n,{from:n.from,...n.mask}):r(n)},this.commitLocation=async({viewTransition:n,ignoreBlocker:s,...r})=>{const i=()=>{const l=["key","__TSR_key","__TSR_index","__hashScrollIntoViewOptions"];l.forEach(d=>{r.state[d]=this.latestLocation.state[d]});const c=yn(r.state,this.latestLocation.state);return l.forEach(d=>{delete r.state[d]}),c},o=Vt(this.latestLocation.href)===Vt(r.href);let a=this.commitLocationPromise;if(this.commitLocationPromise=zn(()=>{a?.resolve(),a=void 0}),o&&i())this.load();else{let{maskedLocation:l,hashScrollIntoView:c,...d}=r;l&&(d={...l,state:{...l.state,__tempKey:void 0,__tempLocation:{...d,search:d.searchStr,state:{...d.state,__tempKey:void 0,__tempLocation:void 0,__TSR_key:void 0,key:void 0}}}},(d.unmaskOnReload??this.options.unmaskOnReload??!1)&&(d.state.__tempKey=this.tempLocationKey)),d.state.__hashScrollIntoViewOptions=c??this.options.defaultHashScrollIntoView??!0,this.shouldViewTransition=n,this.history[r.replace?"replace":"push"](d.publicHref,d.state,{ignoreBlocker:s})}return this.resetNextScroll=r.resetScroll??!0,this.history.subscribers.size||this.load(),this.commitLocationPromise},this.buildAndCommitLocation=({replace:n,resetScroll:s,hashScrollIntoView:r,viewTransition:i,ignoreBlocker:o,href:a,...l}={})=>{if(a){const u=this.history.location.state.__TSR_index,f=br(a,{__TSR_index:n?u:u+1}),h=new URL(f.pathname,this.origin);l.to=Qi(this.rewrite,h).pathname,l.search=this.options.parseSearch(f.search),l.hash=f.hash.slice(1)}const c=this.buildLocation({...l,_includeValidateSearch:!0});this.pendingBuiltLocation=c;const d=this.commitLocation({...c,viewTransition:i,replace:n,resetScroll:s,hashScrollIntoView:r,ignoreBlocker:o});return Promise.resolve().then(()=>{this.pendingBuiltLocation===c&&(this.pendingBuiltLocation=void 0)}),d},this.navigate=async({to:n,reloadDocument:s,href:r,publicHref:i,...o})=>{let a=!1;if(r)try{new URL(`${r}`),a=!0}catch{}if(a&&!s&&(s=!0),s){if(n!==void 0||!r){const c=this.buildLocation({to:n,...o});r=r??c.publicHref,i=i??c.publicHref}const l=!a&&i?i:r;if(mr(l,this.protocolAllowlist))return Promise.resolve();if(!o.ignoreBlocker){const c=this.history.getBlockers?.()??[];for(const d of c)if(d?.blockerFn&&await d.blockerFn({currentLocation:this.latestLocation,nextLocation:this.latestLocation,action:"PUSH"}))return Promise.resolve()}return o.replace?window.location.replace(l):window.location.href=l,Promise.resolve()}return this.buildAndCommitLocation({...o,href:r,to:n,_isNavigate:!0})},this.beforeLoad=()=>{this.cancelMatches(),this.updateLatestLocation();const n=this.matchRoutes(this.latestLocation),s=this.stores.cachedMatchesSnapshot.state.filter(r=>!n.some(i=>i.id===r.id));this.batch(()=>{this.stores.status.setState(()=>"pending"),this.stores.statusCode.setState(()=>200),this.stores.isLoading.setState(()=>!0),this.stores.location.setState(()=>this.latestLocation),this.stores.setPendingMatches(n),this.stores.setCachedMatches(s)})},this.load=async n=>{let s,r,i;const o=this.stores.resolvedLocation.state??this.stores.location.state;for(i=new Promise(l=>{this.startTransition(async()=>{try{this.beforeLoad();const c=this.latestLocation,d=this.stores.resolvedLocation.state,u=Un(c,d);this.stores.redirect.state||this.emit({type:"onBeforeNavigate",...u}),this.emit({type:"onBeforeLoad",...u}),await _l({router:this,sync:n?.sync,forceStaleReload:o.href===c.href,matches:this.stores.pendingMatchesSnapshot.state,location:c,updateMatch:this.updateMatch,onReady:async()=>{this.startTransition(()=>{this.startViewTransition(async()=>{let f=null,h=null,g=null,m=null;this.batch(()=>{const y=this.stores.pendingMatchesSnapshot.state,v=y.length,b=this.stores.activeMatchesSnapshot.state;f=v?b.filter(x=>!this.stores.pendingMatchStoresById.has(x.id)):null;const w=new Set;for(const x of this.stores.pendingMatchStoresById.values())x.routeId&&w.add(x.routeId);const S=new Set;for(const x of this.stores.activeMatchStoresById.values())x.routeId&&S.add(x.routeId);h=v?b.filter(x=>!w.has(x.routeId)):null,g=v?y.filter(x=>!S.has(x.routeId)):null,m=v?y.filter(x=>S.has(x.routeId)):b,this.stores.isLoading.setState(()=>!1),this.stores.loadedAt.setState(()=>Date.now()),v&&(this.stores.setActiveMatches(y),this.stores.setPendingMatches([]),this.stores.setCachedMatches([...this.stores.cachedMatchesSnapshot.state,...f.filter(x=>x.status!=="error"&&x.status!=="notFound"&&x.status!=="redirected")]),this.clearExpiredCache())});for(const[y,v]of[[h,"onLeave"],[g,"onEnter"],[m,"onStay"]])if(y)for(const b of y)this.looseRoutesById[b.routeId].options[v]?.(b)})})}})}catch(c){Je(c)?(s=c,this.navigate({...s.options,replace:!0,ignoreBlocker:!0})):Ge(c)&&(r=c);const d=s?s.status:r?404:this.stores.activeMatchesSnapshot.state.some(u=>u.status==="error")?500:200;this.batch(()=>{this.stores.statusCode.setState(()=>d),this.stores.redirect.setState(()=>s)})}this.latestLoadPromise===i&&(this.commitLocationPromise?.resolve(),this.latestLoadPromise=void 0,this.commitLocationPromise=void 0),l()})}),this.latestLoadPromise=i,await i;this.latestLoadPromise&&i!==this.latestLoadPromise;)await this.latestLoadPromise;let a;this.hasNotFoundMatch()?a=404:this.stores.activeMatchesSnapshot.state.some(l=>l.status==="error")&&(a=500),a!==void 0&&this.stores.statusCode.setState(()=>a)},this.startViewTransition=n=>{const s=this.shouldViewTransition??this.options.defaultViewTransition;if(this.shouldViewTransition=void 0,s&&typeof document<"u"&&"startViewTransition"in document&&typeof document.startViewTransition=="function"){let r;if(typeof s=="object"&&this.isViewTransitionTypesSupported){const i=this.latestLocation,o=this.stores.resolvedLocation.state,a=typeof s.types=="function"?s.types(Un(i,o)):s.types;if(a===!1){n();return}r={update:n,types:a}}else r=n;document.startViewTransition(r)}else n()},this.updateMatch=(n,s)=>{this.startTransition(()=>{const r=this.stores.pendingMatchStoresById.get(n);if(r){r.setState(s);return}const i=this.stores.activeMatchStoresById.get(n);if(i){i.setState(s);return}const o=this.stores.cachedMatchStoresById.get(n);if(o){const a=s(o.state);a.status==="redirected"?this.stores.cachedMatchStoresById.delete(n)&&this.stores.cachedMatchesId.setState(l=>l.filter(c=>c!==n)):o.setState(()=>a)}})},this.getMatch=n=>this.stores.cachedMatchStoresById.get(n)?.state??this.stores.pendingMatchStoresById.get(n)?.state??this.stores.activeMatchStoresById.get(n)?.state,this.invalidate=n=>{const s=r=>n?.filter?.(r)??!0?{...r,invalid:!0,...n?.forcePending||r.status==="error"||r.status==="notFound"?{status:"pending",error:void 0}:void 0}:r;return this.batch(()=>{this.stores.setActiveMatches(this.stores.activeMatchesSnapshot.state.map(s)),this.stores.setCachedMatches(this.stores.cachedMatchesSnapshot.state.map(s)),this.stores.setPendingMatches(this.stores.pendingMatchesSnapshot.state.map(s))}),this.shouldViewTransition=!1,this.load({sync:n?.sync})},this.getParsedLocationHref=n=>n.publicHref||"/",this.resolveRedirect=n=>{const s=n.headers.get("Location");if(!n.options.href||n.options._builtLocation){const r=n.options._builtLocation??this.buildLocation(n.options),i=this.getParsedLocationHref(r);n.options.href=i,n.headers.set("Location",i)}else if(s)try{const r=new URL(s);if(this.origin&&r.origin===this.origin){const i=r.pathname+r.search+r.hash;n.options.href=i,n.headers.set("Location",i)}}catch{}if(n.options.href&&!n.options._builtLocation&&mr(n.options.href,this.protocolAllowlist))throw new Error("Redirect blocked: unsafe protocol");return n.headers.get("Location")||n.headers.set("Location",n.options.href),n},this.clearCache=n=>{const s=n?.filter;s!==void 0?this.stores.setCachedMatches(this.stores.cachedMatchesSnapshot.state.filter(r=>!s(r))):this.stores.setCachedMatches([])},this.clearExpiredCache=()=>{const n=Date.now(),s=r=>{const i=this.looseRoutesById[r.routeId];if(!i.options.loader)return!0;const o=(r.preload?i.options.preloadGcTime??this.options.defaultPreloadGcTime:i.options.gcTime??this.options.defaultGcTime)??300*1e3;return r.status==="error"?!0:n-r.updatedAt>=o};this.clearCache({filter:s})},this.loadRouteChunk=Os,this.preloadRoute=async n=>{const s=n._builtLocation??this.buildLocation(n);let r=this.matchRoutes(s,{throwOnError:!0,preload:!0,dest:n});const i=new Set([...this.stores.matchesId.state,...this.stores.pendingMatchesId.state]),o=new Set([...i,...this.stores.cachedMatchesId.state]),a=r.filter(l=>!o.has(l.id));if(a.length){const l=this.stores.cachedMatchesSnapshot.state;this.stores.setCachedMatches([...l,...a])}try{return r=await _l({router:this,matches:r,location:s,preload:!0,updateMatch:(l,c)=>{i.has(l)?r=r.map(d=>d.id===l?c(d):d):this.updateMatch(l,c)}}),r}catch(l){if(Je(l))return l.options.reloadDocument?void 0:await this.preloadRoute({...l.options,_fromLocation:s});Ge(l)||console.error(l);return}},this.matchRoute=(n,s)=>{const r={...n,to:n.to?this.resolvePathWithBase(n.from||"",n.to):void 0,params:n.params||{},leaveParams:!0},i=this.buildLocation(r);if(s?.pending&&this.stores.status.state!=="pending")return!1;const o=(s?.pending===void 0?!this.stores.isLoading.state:s.pending)?this.latestLocation:this.stores.resolvedLocation.state||this.stores.location.state,a=Pm(i.pathname,s?.caseSensitive??!1,s?.fuzzy??!1,o.pathname,this.processedTree);return!a||n.params&&!yn(a.rawParams,n.params,{partial:!0})?!1:s?.includeSearch??!0?yn(o.search,i.search,{partial:!0})?a.rawParams:!1:a.rawParams},this.hasNotFoundMatch=()=>this.stores.activeMatchesSnapshot.state.some(n=>n.status==="notFound"||n.globalNotFound),this.getStoreConfig=t,this.update({defaultPreloadDelay:50,defaultPendingMs:1e3,defaultPendingMinMs:500,context:void 0,...e,caseSensitive:e.caseSensitive??!1,notFoundMode:e.notFoundMode??"fuzzy",stringifySearch:e.stringifySearch??Wm,parseSearch:e.parseSearch??zm,protocolAllowlist:e.protocolAllowlist??_m}),typeof document<"u"&&(self.__TSR_ROUTER__=this)}isShell(){return!!this.options.isShell}isPrerendering(){return!!this.options.isPrerendering}get state(){return this.stores.__store.state}setRoutes({routesById:e,routesByPath:t,processedTree:n}){this.routesById=e,this.routesByPath=t,this.processedTree=n;const s=this.options.notFoundRoute;s&&(s.init({originalIndex:99999999999}),this.routesById[s.id]=s)}get looseRoutesById(){return this.routesById}getParentContext(e){return e?.id?e.context??this.options.context??void 0:this.options.context??void 0}matchRoutesInternal(e,t){const n=this.getMatchedRoutes(e.pathname),{foundRoute:s,routeParams:r,parsedParams:i}=n;let{matchedRoutes:o}=n,a=!1;(s?s.path!=="/"&&r["**"]:Vt(e.pathname))&&(this.options.notFoundRoute?o=[...o,this.options.notFoundRoute]:a=!0);const l=a?pv(this.options.notFoundMode,o):void 0,c=new Array(o.length),d=new Map;for(const u of this.stores.activeMatchStoresById.values())u.routeId&&d.set(u.routeId,u.state);for(let u=0;uthis.navigate({...w,_fromLocation:e}),buildLocation:this.buildLocation,cause:f.cause,abortController:f.abortController,preload:!!f.preload,matches:c,routeId:h.id};f.__routeContext=h.options.context(b)??void 0}f.context={...v,...f.__routeContext,...f.__beforeLoadContext}}}return c}matchRoutesLightweight(e){const{matchedRoutes:t,routeParams:n,parsedParams:s}=this.getMatchedRoutes(e.pathname),r=ks(t),i={...e.search};for(const d of t)try{Object.assign(i,tr(d.options.validateSearch,i))}catch{}const o=ks(this.stores.matchesId.state),a=o&&this.stores.activeMatchStoresById.get(o)?.state,l=a&&a.routeId===r.id&&a.pathname===e.pathname;let c;if(l)c=a.params;else{const d=Object.assign(Object.create(null),n);for(const u of t)try{Cl(u,n,s??{},d)}catch{}c=d}return{matchedRoutes:t,fullPath:r.fullPath,search:i,params:c}}},_r=class extends Error{},uv=class extends Error{};function dv(e){return{loadedAt:0,isLoading:!1,isTransitioning:!1,status:"idle",resolvedLocation:void 0,location:e,matches:[],statusCode:200}}function tr(e,t){if(e==null)return{};if("~standard"in e){const n=e["~standard"].validate(t);if(n instanceof Promise)throw new _r("Async validation not supported");if(n.issues)throw new _r(JSON.stringify(n.issues,void 0,2),{cause:n});return n.value}return"parse"in e?e.parse(t):typeof e=="function"?e(t):{}}function fv({pathname:e,routesById:t,processedTree:n}){const s=Object.create(null),r=Vt(e);let i,o;const a=km(r,n,!0);return a&&(i=a.route,Object.assign(s,a.rawParams),o=Object.assign(Object.create(null),a.parsedParams)),{matchedRoutes:a?.branch||[t.__root__],routeParams:s,foundRoute:i,parsedParams:o}}function hv({search:e,dest:t,destRoutes:n,_includeValidateSearch:s}){return gv(n)(e,t,s??!1)}function gv(e){const t={dest:null,_includeValidateSearch:!1,middlewares:[]};for(const r of e){if("search"in r.options)r.options.search?.middlewares&&t.middlewares.push(...r.options.search.middlewares);else if(r.options.preSearchFilters||r.options.postSearchFilters){const i=({search:o,next:a})=>{let l=o;"preSearchFilters"in r.options&&r.options.preSearchFilters&&(l=r.options.preSearchFilters.reduce((d,u)=>u(d),o));const c=a(l);return"postSearchFilters"in r.options&&r.options.postSearchFilters?r.options.postSearchFilters.reduce((d,u)=>u(d),c):c};t.middlewares.push(i)}if(r.options.validateSearch){const i=({search:o,next:a})=>{const l=a(o);if(!t._includeValidateSearch)return l;try{return{...l,...tr(r.options.validateSearch,l)??void 0}}catch{return l}};t.middlewares.push(i)}}const n=({search:r})=>{const i=t.dest;return i.search?i.search===!0?r:Kt(i.search,r):{}};t.middlewares.push(n);const s=(r,i,o)=>{if(r>=o.length)return i;const a=o[r];return a({search:i,next:c=>s(r+1,c,o)})};return function(i,o,a){return t.dest=o,t._includeValidateSearch=a,s(0,i,t.middlewares)}}function pv(e,t){if(e!=="root")for(let n=t.length-1;n>=0;n--){const s=t[n];if(s.children)return s.id}return xt}function Cl(e,t,n,s){const r=e.options.params?.parse??e.options.parseParams;if(r)if(e.options.skipRouteOnParseError)for(const i in t)i in n&&(s[i]=n[i]);else{const i=r(s);Object.assign(s,i)}}var mv="Error preloading route! ☝️",Ku=class{get to(){return this._to}get id(){return this._id}get path(){return this._path}get fullPath(){return this._fullPath}constructor(e){if(this.init=t=>{this.originalIndex=t.originalIndex;const n=this.options,s=!n?.path&&!n?.id;this.parentRoute=this.options.getParentRoute?.(),s?this._path=xt:this.parentRoute||Wn();let r=s?xt:n?.path;r&&r!=="/"&&(r=Iu(r));const i=n?.id||r;let o=s?xt:Zs([this.parentRoute.id==="__root__"?"":this.parentRoute.id,i]);r==="__root__"&&(r="/"),o!=="__root__"&&(o=Zs(["/",o]));const a=o==="__root__"?"/":Zs([this.parentRoute.fullPath,r]);this._path=r,this._id=o,this._fullPath=a,this._to=Vt(a)},this.addChildren=t=>this._addFileChildren(t),this._addFileChildren=t=>(Array.isArray(t)&&(this.children=t),typeof t=="object"&&t!==null&&(this.children=Object.values(t)),this),this._addFileTypes=()=>this,this.updateLoader=t=>(Object.assign(this.options,t),this),this.update=t=>(Object.assign(this.options,t),this),this.lazy=t=>(this.lazyFn=t,this),this.redirect=t=>Jm({from:this.fullPath,...t}),this.options=e||{},this.isRoot=!e?.getParentRoute,e?.id&&e?.path)throw new Error("Route cannot have both an 'id' and a 'path' option.")}},vv=class extends Ku{constructor(e){super(e)}},yv=R('
Something went wrong!
'),bv=R('
'),_v=R("");function ko(e){return _(Rf,{fallback:(t,n)=>(e.onCatch?.(t),q(Ve([e.getResetKey],()=>n(),{defer:!0})),_(ht,{get component(){return e.errorComponent??Eo},error:t,reset:n})),get children(){return e.children}})}function Eo({error:e}){const[t,n]=A(!1);return(()=>{var s=yv(),r=s.firstChild,i=r.firstChild,o=i.nextSibling;return r.nextSibling,o.$$click=()=>n(a=>!a),p(o,()=>t()?"Hide Error":"Show Error"),p(s,(()=>{var a=ee(()=>!!t());return()=>a()?(()=>{var l=bv(),c=l.firstChild;return p(c,(()=>{var d=ee(()=>!!e.message);return()=>d()?(()=>{var u=_v();return p(u,()=>e.message),u})():null})()),l})():null})(),null),s})()}Ae(["click"]);function wv(){const[e,t]=A(!1);return Mt(()=>{t(!0)}),e}const Sv=e=>e!=null,xv=e=>e.filter(Sv);function $v(e){return(...t)=>{for(const n of e)n&&n(...t)}}const U=e=>typeof e=="function"&&!e.length?e():e,Ji=e=>Array.isArray(e)?e:e?[e]:[];function Cv(e,...t){return typeof e=="function"?e(...t):e}const Pv=Z;function kv(e,t,n,s){const r=e.length,i=t.length;let o=0;if(!i){for(;o{const o=e();!o||!r||s.disabled||(i=new IntersectionObserver(([a])=>{t(a)},n),i.observe(o),Z(()=>{i?.disconnect()}))}),()=>i}var Rv=R(""),Ov=R("");const ls=new WeakMap;function Lv(e){const t=Ye(),[n,s]=A(!1),r=!!t.options.ssr,i=wv();let o=!1;const[a,l]=ae(re({activeProps:Pl,inactiveProps:kl},e),["activeProps","inactiveProps","activeOptions","to","preload","preloadDelay","hashScrollIntoView","replace","startTransition","resetScroll","viewTransition","target","disabled","style","class","onClick","onBlur","onFocus","onMouseEnter","onMouseLeave","onMouseOver","onMouseOut","onTouchStart","ignoreBlocker"]),[c,d]=ae(l,["params","search","hash","state","mask","reloadDocument","unsafeRelative"]),u=D(()=>t.stores.location.state,void 0,{equals:(V,z)=>V.href===z.href}),f=()=>e,h=D(()=>{const z={_fromLocation:u(),...f()};return $e(()=>t.buildLocation(z))}),g=D(()=>{if(f().disabled)return;const V=h().maskedLocation??h(),z=V.publicHref;return V.external?{href:z,external:!0}:{href:t.history.createHref(z)||"/",external:!1}}),m=D(()=>{const V=g();if(V?.external)return mr(V.href,t.protocolAllowlist)?void 0:V.href;const z=f().to;if(!jv(z)&&!(typeof z!="string"||z.indexOf(":")===-1))try{return new URL(z),mr(z,t.protocolAllowlist)?void 0:z}catch{}}),y=D(()=>f().reloadDocument||m()?!1:a.preload??t.options.defaultPreload),v=()=>a.preloadDelay??t.options.defaultPreloadDelay??0,b=D(()=>{if(m())return!1;const V=a.activeOptions,z=u(),ie=h();if(V?.exact){if(!Im(z.pathname,ie.pathname,t.basepath))return!1}else{const we=vr(z.pathname,t.basepath),Re=vr(ie.pathname,t.basepath);if(!(we.startsWith(Re)&&(we.length===Re.length||we[Re.length]==="/")))return!1}return(V?.includeSearch??!0)&&!yn(z.search,ie.search,{partial:!V?.exact,ignoreUndefined:!V?.explicitUndefined})?!1:V?.includeHash?(r&&!i()?"":z.hash)===ie.hash:!0}),w=()=>t.preloadRoute({...f(),_builtLocation:h()}).catch(V=>{console.warn(V),console.warn(mv)}),S=V=>{V?.isIntersecting&&w()},[x,k]=A(null);if(Ev(x,S,{rootMargin:"100px"},{disabled:!!a.disabled||y()!=="viewport"}),q(()=>{o||!a.disabled&&y()==="render"&&(w(),o=!0)}),m())return re(d,{ref:qe(k,f().ref),href:m()},ae(a,["target","disabled","style","class","onClick","onBlur","onFocus","onMouseEnter","onMouseLeave","onMouseOut","onMouseOver","onTouchStart"])[0]);const P=V=>{const z=V.currentTarget.getAttribute("target"),ie=a.target!==void 0?a.target:z;if(!a.disabled&&!Nv(V)&&!V.defaultPrevented&&(!ie||ie==="_self")&&V.button===0){V.preventDefault(),s(!0);const we=t.subscribe("onResolved",()=>{we(),s(!1)});t.navigate({...f(),replace:a.replace,resetScroll:a.resetScroll,hashScrollIntoView:a.hashScrollIntoView,startTransition:a.startTransition,viewTransition:a.viewTransition,ignoreBlocker:a.ignoreBlocker})}},E=V=>{if(a.disabled||y()!=="intent")return;if(!v()){w();return}const z=V.currentTarget||V.target;!z||ls.has(z)||ls.set(z,setTimeout(()=>{ls.delete(z),w()},v()))},O=V=>{a.disabled||y()!=="intent"||w()},$=V=>{if(a.disabled)return;const z=V.currentTarget||V.target;if(z){const ie=ls.get(z);clearTimeout(ie),ls.delete(z)}},C=D(()=>a.activeProps===Pl&&a.inactiveProps===kl&&a.class===void 0&&a.style===void 0),I=Nt(()=>a.onClick,P),T=Nt(()=>a.onBlur,$),L=Nt(()=>a.onFocus,E),M=Nt(()=>a.onMouseEnter,E),W=Nt(()=>a.onMouseOver,E),H=Nt(()=>a.onMouseLeave,$),se=Nt(()=>a.onMouseOut,$),ne=Nt(()=>a.onTouchStart,O),_e=D(()=>{const V=b(),z={href:g()?.href,ref:qe(k,f().ref),onClick:I,onBlur:T,onFocus:L,onMouseEnter:M,onMouseOver:W,onMouseLeave:H,onMouseOut:se,onTouchStart:ne,disabled:!!a.disabled,target:a.target,...a.disabled&&Av,...n()&&Fv};if(C())return{...z,...V&&Iv};const ie=V?Kt(a.activeProps,{})??nr:nr,we=V?nr:Kt(a.inactiveProps,{}),Re={...a.style,...ie.style,...we.style},de=[a.class,ie.class,we.class].filter(Boolean).join(" ");return{...ie,...we,...z,...Object.keys(Re).length?{style:Re}:void 0,...de?{class:de}:void 0,...V&&Mv}});return re(d,_e)}const Tv={class:"active"},Pl=()=>Tv,nr={},kl=()=>nr,Iv={class:"active","data-status":"active","aria-current":"page"},Av={role:"link","aria-disabled":!0},Mv={"data-status":"active","aria-current":"page"},Fv={"data-transitioning":"transitioning"};function Dv(e,t){return typeof t=="function"?t(e):t[0](t[1],e),e.defaultPrevented}function Nt(e,t){return n=>{const s=e();(!s||!Dv(n,s))&&t(n)}}const Ro=e=>{const[t,n]=ae(e,["_asChild","children"]),[s,r]=ae(Lv(n),["type"]),i=D(()=>{const o=t.children;return typeof o=="function"?o({get isActive(){return r["data-status"]==="active"},get isTransitioning(){return r["data-transitioning"]==="transitioning"}}):o});if(t._asChild==="svg"){const[o,a]=ae(r,["class"]);return(()=>{var l=Rv(),c=l.firstChild;return Ii(c,a,!1,!0),p(c,i),l})()}return t._asChild?_(ht,re({get component(){return t._asChild}},r,{get children(){return i()}})):(()=>{var o=Ov();return Ii(o,r,!1,!0),p(o,i),o})()};function Nv(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function jv(e){if(typeof e!="string")return!1;const t=e.charCodeAt(0);return t===47?e.charCodeAt(1)!==47:t===46}const Kv={matchId:()=>{},routeId:()=>{},match:()=>{},hasPending:()=>!1},Ds=He(Kv);function kn(e){const t=Ye(),n=e.from?void 0:Te(Ds),s=()=>e.from?t.stores.getMatchStoreByRouteId(e.from).state:n?.match();return q(()=>{if(s()!==void 0)return;!(e.from?t.stores.pendingRouteIds.state[e.from]:n?.hasPending()??!1)&&!t.stores.isTransitioning.state&&(e.shouldThrow??!0)&&Wn()}),D(r=>{const i=s();if(i===void 0)return;const o=e.select?e.select(i):i;return r===void 0?o:fn(r,o)})}function Uu(e){return kn({from:e.from,strict:e.strict,select:t=>e.select?e.select(t.loaderData):t.loaderData})}function Vu(e){return kn({...e,select:t=>e.select?e.select(t.loaderDeps):t.loaderDeps})}function qu(e){return kn({from:e.from,strict:e.strict,shouldThrow:e.shouldThrow,select:t=>{const n=e.strict===!1?t.params:t._strictParams;return e.select?e.select(n):n}})}function Hu(e){return kn({from:e.from,strict:e.strict,shouldThrow:e.shouldThrow,select:t=>{const n=t.search;return e.select?e.select(n):n}})}function zu(e){const t=Ye();return n=>t.navigate({...n,from:n.from??e?.from})}function Wu(e){const{navigate:t}=Ye();return Mt(()=>{t({...e})}),null}function Qu(e){return kn({...e,select:t=>e.select?e.select(t.context):t.context})}class Bv extends Ku{constructor(t){super(t),this.useMatch=n=>kn({select:n?.select,from:this.id}),this.useRouteContext=n=>Qu({...n,from:this.id}),this.useSearch=n=>Hu({select:n?.select,from:this.id}),this.useParams=n=>qu({select:n?.select,from:this.id}),this.useLoaderDeps=n=>Vu({...n,from:this.id}),this.useLoaderData=n=>Uu({...n,from:this.id}),this.useNavigate=()=>zu({from:this.fullPath}),this.Link=n=>{const s=this;return _(Ro,re({get from(){return s.fullPath}},n))}}}function Yt(e){return new Bv(e)}class Uv extends vv{constructor(t){super(t),this.useMatch=n=>kn({select:n?.select,from:this.id}),this.useRouteContext=n=>Qu({...n,from:this.id}),this.useSearch=n=>Hu({select:n?.select,from:this.id}),this.useParams=n=>qu({select:n?.select,from:this.id}),this.useLoaderDeps=n=>Vu({...n,from:this.id}),this.useLoaderData=n=>Uu({...n,from:this.id}),this.useNavigate=()=>zu({from:this.fullPath}),this.Link=n=>{const s=this;return _(Ro,re({get from(){return s.fullPath}},n))}}}function Vv(e){return new Uv(e)}function qv(){const e=Ye();let t={router:e,mounted:!1};const n=D(()=>e.stores.isLoading.state),[s,r]=bf(),i=D(()=>e.stores.hasPendingMatches.state),o=D(()=>n()||s()||i()),a=D(()=>n()||i());return e.startTransition=l=>{Ec(()=>{r(l)})},Mt(()=>{const l=e.history.subscribe(e.load),c=e.buildLocation({to:e.latestLocation.pathname,search:!0,params:!0,hash:!0,state:!0,_includeValidateSearch:!0});Vt(e.latestLocation.publicHref)!==Vt(c.publicHref)&&e.commitLocation({...c,replace:!0}),Z(()=>{l()})}),j(()=>{$e(()=>{if(typeof window<"u"&&e.ssr||t.router===e&&t.mounted)return;t={router:e,mounted:!0},(async()=>{try{await e.load()}catch(c){console.error(c)}})()})}),j((l=!1)=>{const c=n();return l&&!c&&e.emit({type:"onLoad",...Un(e.stores.location.state,e.stores.resolvedLocation.state)}),c}),Tt((l=!1)=>{const c=a();return l&&!c&&e.emit({type:"onBeforeRouteMount",...Un(e.stores.location.state,e.stores.resolvedLocation.state)}),c}),j((l=!1)=>{const c=o();if(l&&!c){const d=Un(e.stores.location.state,e.stores.resolvedLocation.state);e.emit({type:"onResolved",...d}),oo(()=>{e.stores.status.setState(()=>"idle"),e.stores.resolvedLocation.setState(()=>e.stores.location.state)}),d.hrefChanged&&Vm(e)}return c}),null}function pn(e){return ee(()=>e.children)}var Hv=R("

Not Found");function zv(e){const t=Ye(),n=D(()=>t.stores.location.state.pathname),s=D(()=>t.stores.status.state);return _(ko,{getResetKey:()=>`not-found-${n()}-${s()}`,onCatch:r=>{if(Ge(r))e.onCatch?.(r);else throw r},errorComponent:({error:r})=>{if(Ge(r))return e.fallback?.(r);throw r},get children(){return e.children}})}function Wv(){return Hv()}function Gu(e,t,n){return t.options.notFoundComponent?_(t.options.notFoundComponent,n):e.options.defaultNotFoundComponent?_(e.options.defaultNotFoundComponent,n):_(Wv,{})}function Qv(){return Ye().isScrollRestoring,null}const Yi=e=>{const t=Ye(),n=D(()=>{const o=e.matchId;if(o)return t.stores.activeMatchStoresById.get(o)?.state}),s=D(()=>{const o=n();if(!o)return null;const a=o.routeId,l=t.routesById[a]?.parentRoute?.id;return{matchId:o.id,routeId:a,ssr:o.ssr,_displayPending:o._displayPending,parentRouteId:l}}),r=D(()=>{const o=s()?.routeId;return o?!!t.stores.pendingRouteIds.state[o]:!1}),i={matchId:()=>s()?.matchId,routeId:()=>s()?.routeId,match:n,hasPending:r};return _(N,{get when(){return s()},children:o=>{const a=()=>t.routesById[o().routeId],l=()=>a().options.pendingComponent??t.options.defaultPendingComponent,c=()=>a().options.errorComponent??t.options.defaultErrorComponent,d=()=>a().options.onCatch??t.options.defaultOnCatch,u=()=>a().isRoot?a().options.notFoundComponent??t.options.notFoundRoute?.options.component:a().options.notFoundComponent,f=o().ssr===!1||o().ssr==="data-only",h=()=>lo,g=()=>c()?ko:pn,m=()=>u()?zv:pn,y=a().isRoot?a().options.shellComponent??pn:pn;return _(y,{get children(){return[_(Ds.Provider,{value:i,get children(){return _(ht,{get component(){return h()},get fallback(){return ee(()=>!1)()?void 0:_(ht,{get component(){return l()}})},get children(){return _(ht,{get component(){return g()},getResetKey:()=>t.stores.loadedAt.state,get errorComponent(){return c()||Eo},onCatch:v=>{if(Ge(v))throw v;d()?.(v)},get children(){return _(ht,{get component(){return m()},fallback:v=>{if(!u()||v.routeId&&v.routeId!==o().routeId||!v.routeId&&!a().isRoot)throw v;return _(ht,re({get component(){return u()}},v))},get children(){return _(ao,{get children(){return[_(at,{when:f,get children(){return _(N,{get when(){return!0},get fallback(){return _(ht,{get component(){return l()}})},get children(){return _(El,{})}})}}),_(at,{when:!f,get children(){return _(El,{})}})]}})}})}})}})}}),ee(()=>ee(()=>o().parentRouteId===xt)()?[_(Gv,{}),_(Qv,{})]:null)]}})}})};function Gv(){const e=Ye(),t=D(()=>e.stores.resolvedLocation.state?.state.__TSR_key);return q(Ve([t],()=>{e.emit({type:"onRendered",...Un(e.stores.location.state,e.stores.resolvedLocation.state)})})),null}const El=()=>{const e=Ye(),t=Te(Ds).match,n=D(()=>{const s=t();if(!s)return null;const r=s.routeId,o=(e.routesById[r].options.remountDeps??e.options.defaultRemountDeps)?.({routeId:r,loaderDeps:s.loaderDeps,params:s._strictParams,search:s._strictSearch});return{key:o?JSON.stringify(o):void 0,routeId:r,match:{id:s.id,status:s.status,error:s.error,_forcePending:s._forcePending??!1,_displayPending:s._displayPending??!1}}});return _(N,{get when(){return n()},children:s=>{const r=()=>e.routesById[s().routeId],i=()=>s().match,o=()=>s().key??s().match.id,a=()=>{const c=r().options.component??e.options.defaultComponent;return c?_(c,{}):_(Ju,{})},l=()=>_(N,{get when(){return o()},keyed:!0,children:c=>a()});return _(ao,{get children(){return[_(at,{get when(){return i()._displayPending},children:c=>{const[d]=hs(()=>e.getMatch(i().id)?._nonReactive.displayPendingPromise);return ee(d)}}),_(at,{get when(){return i()._forcePending},children:c=>{const[d]=hs(()=>e.getMatch(i().id)?._nonReactive.minPendingPromise);return ee(d)}}),_(at,{get when(){return i().status==="pending"},children:c=>{const d=r().options.pendingMinMs??e.options.defaultPendingMinMs;if(d){const h=e.getMatch(i().id);if(h&&!h._nonReactive.minPendingPromise){const g=zn();h._nonReactive.minPendingPromise=g,setTimeout(()=>{g.resolve(),h._nonReactive.minPendingPromise=void 0},d)}}const[u]=hs(async()=>(await new Promise(h=>setTimeout(h,0)),e.getMatch(i().id)?._nonReactive.loadPromise)),f=r().options.pendingComponent??e.options.defaultPendingComponent;return[f&&d>0?_(ht,{component:f}):null,ee(u)]}}),_(at,{get when(){return i().status==="notFound"},children:c=>(Ge(i().error)||Wn(),_(N,{get when(){return s().routeId},keyed:!0,children:d=>Gu(e,r(),i().error)}))}),_(at,{get when(){return i().status==="redirected"},children:c=>{Je(i().error)||Wn();const[d]=hs(async()=>(await new Promise(u=>setTimeout(u,0)),e.getMatch(i().id)?._nonReactive.loadPromise));return ee(d)}}),_(at,{get when(){return i().status==="error"},children:c=>{throw i().error}}),_(at,{get when(){return i().status==="success"},get children(){return l()}})]}})}})},Ju=()=>{const e=Ye(),t=Te(Ds),n=t.match,s=t.routeId,r=D(()=>s()?e.routesById[s()]:void 0),i=D(()=>n()?.globalNotFound??!1),o=D(()=>{const c=s();return c?e.stores.childMatchIdByRouteId.state[c]:void 0}),a=D(()=>{const c=o();if(c)return e.stores.activeMatchStoresById.get(c)?.state.status}),l=()=>a()!=="redirected"&&i();return _(N,{get when(){return ee(()=>!l())()&&o()},get fallback(){return _(N,{get when(){return ee(()=>!!l())()&&r()},children:c=>Gu(e,c(),void 0)})},children:c=>{const d=D(()=>c());return _(N,{get when(){return s()===xt},get fallback(){return _(Yi,{get matchId(){return d()}})},get children(){return _(lo,{get fallback(){return _(ht,{get component(){return e.options.defaultPendingComponent}})},get children(){return _(Yi,{get matchId(){return d()}})}})}})}})};function Jv(){const e=Ye(),t=typeof document<"u"&&e.ssr?pn:lo,s=e.routesById[xt].options.pendingComponent??e.options.defaultPendingComponent,r=e.options.InnerWrap||pn;return _(r,{get children(){return _(t,{get fallback(){return s?_(s,{}):null},get children(){return[_(qv,{}),_(Yv,{})]}})}})}function Yv(){const e=Ye(),t=()=>e.stores.firstMatchId.state,n=()=>t()?xt:void 0,s=()=>n()?e.stores.getMatchStoreByRouteId(xt).state:void 0,r=()=>n()?!!e.stores.pendingRouteIds.state[xt]:!1,i=()=>e.stores.loadedAt.state,o={matchId:t,routeId:n,match:s,hasPending:r},a=()=>_(N,{get when(){return t()},get children(){return _(Yi,{get matchId(){return t()}})}});return _(Ds.Provider,{value:o,get children(){return ee(()=>!!e.options.disableGlobalCatchBoundary)()?a():_(ko,{getResetKey:()=>i(),errorComponent:Eo,get onCatch(){},get children(){return a()}})}})}function Xv(e,t){e.childMatchIdByRouteId=t(()=>{const n=e.matchesId.state,s={};for(let r=0;r{const n=e.pendingMatchesId.state,s={};for(const r of n){const i=e.pendingMatchStoresById.get(r);i?.routeId&&(s[i.routeId]=!0)}return s})}function Zv(e){const[t,n]=A(e);return{get state(){return t()},setState:n}}let Yu=null;typeof globalThis<"u"&&"FinalizationRegistry"in globalThis&&(Yu=new FinalizationRegistry(e=>e()));function Rl(e){let t;const n=mn(r=>(t=r,D(e))),s={get state(){return n()}};return Yu?.register(s,t),s}const ey=e=>({createMutableStore:Zv,createReadonlyStore:Rl,batch:oo,init:t=>Xv(t,Rl)}),ty=e=>new ny(e);class ny extends cv{constructor(t){super(t,ey)}}function sy({router:e,children:t,...n}){e.update({...e.options,...n,context:{...e.options.context,...n.context}});const s=e.options.Wrap||pn;return _(s,{get children(){return _(Bu.Provider,{value:e,get children(){return t()}})}})}function ry({router:e,...t}){return _(sy,re({router:e},t,{children:()=>_(Jv,{})}))}function iy(e){const n=Ye();return()=>n.stores.__store.state}function oy(e,t,n,s){return e.addEventListener(t,n,s),Pv(e.removeEventListener.bind(e,t,n,s))}function ay(e,t,n,s){const r=()=>{Ji(U(e)).forEach(i=>{i&&Ji(U(t)).forEach(o=>oy(i,o,n,s))})};typeof e=="function"?q(r):j(r)}const ly=/((?:--)?(?:\w+-?)+)\s*:\s*([^;]*)/g;function Ol(e){const t={};let n;for(;n=ly.exec(e);)t[n[1]]=n[2];return t}function es(e,t){if(typeof e=="string"){if(typeof t=="string")return`${e};${t}`;e=Ol(e)}else typeof t=="string"&&(t=Ol(t));return{...e,...t}}function cy(e,t,n=-1){return n in e?[...e.slice(0,n),t,...e.slice(n)]:[...e,t]}function Ll(e,t){const n=[...e],s=n.indexOf(t);return s!==-1&&n.splice(s,1),n}function uy(e){return typeof e=="number"}function Dn(e){return Object.prototype.toString.call(e)==="[object String]"}function dy(e){return typeof e=="function"}function Oo(e){return t=>`${e()}-${t}`}function St(e,t){return e?e===t||e.contains(t):!1}function ps(e,t=!1){const{activeElement:n}=Ct(e);if(!n?.nodeName)return null;if(Xu(n)&&n.contentDocument)return ps(n.contentDocument.body,t);if(t){const s=n.getAttribute("aria-activedescendant");if(s){const r=Ct(n).getElementById(s);if(r)return r}}return n}function fy(e){return Ct(e).defaultView||window}function Ct(e){return e?e.ownerDocument||e:document}function Xu(e){return e.tagName==="IFRAME"}var Zu=(e=>(e.Escape="Escape",e.Enter="Enter",e.Tab="Tab",e.Space=" ",e.ArrowDown="ArrowDown",e.ArrowLeft="ArrowLeft",e.ArrowRight="ArrowRight",e.ArrowUp="ArrowUp",e.End="End",e.Home="Home",e.PageDown="PageDown",e.PageUp="PageUp",e))(Zu||{});function ed(e){return typeof window>"u"||window.navigator==null?!1:window.navigator.userAgentData?.brands.some(t=>e.test(t.brand))||e.test(window.navigator.userAgent)}function Lo(e){return typeof window<"u"&&window.navigator!=null?e.test(window.navigator.userAgentData?.platform||window.navigator.platform):!1}function Fr(){return Lo(/^Mac/i)}function hy(){return Lo(/^iPhone/i)}function gy(){return Lo(/^iPad/i)||Fr()&&navigator.maxTouchPoints>1}function py(){return hy()||gy()}function my(){return Fr()||py()}function vy(){return ed(/AppleWebKit/i)&&!yy()}function yy(){return ed(/Chrome/i)}function It(e,t){return t&&(dy(t)?t(e):t[0](t[1],e)),e?.defaultPrevented}function ct(e){return t=>{for(const n of e)It(t,n)}}function by(e){return Fr()?e.metaKey&&!e.ctrlKey:e.ctrlKey&&!e.metaKey}function Be(e){if(e)if(_y())e.focus({preventScroll:!0});else{const t=wy(e);e.focus(),Sy(t)}}var Qs=null;function _y(){if(Qs==null){Qs=!1;try{document.createElement("div").focus({get preventScroll(){return Qs=!0,!0}})}catch{}}return Qs}function wy(e){let t=e.parentNode;const n=[],s=document.scrollingElement||document.documentElement;for(;t instanceof HTMLElement&&t!==s;)(t.offsetHeight{if(Xu(r)&&r.contentDocument){const o=r.contentDocument.body,a=nd(o,!1);s.splice(i,1,...a)}}),s}function Tl(e){return sd(e)&&!Cy(e)}function sd(e){return e.matches(To)&&Io(e)}function Cy(e){return Number.parseInt(e.getAttribute("tabindex")||"0",10)<0}function Io(e,t){return e.nodeName!=="#comment"&&Py(e)&&ky(e,t)&&(!e.parentElement||Io(e.parentElement,e))}function Py(e){if(!(e instanceof HTMLElement)&&!(e instanceof SVGElement))return!1;const{display:t,visibility:n}=e.style;let s=t!=="none"&&n!=="hidden"&&n!=="collapse";if(s){if(!e.ownerDocument.defaultView)return s;const{getComputedStyle:r}=e.ownerDocument.defaultView,{display:i,visibility:o}=r(e);s=i!=="none"&&o!=="hidden"&&o!=="collapse"}return s}function ky(e,t){return!e.hasAttribute("hidden")&&(e.nodeName==="DETAILS"&&t&&t.nodeName!=="SUMMARY"?e.hasAttribute("open"):!0)}function rd(e,t,n){const s=t?.tabbable?$y:To,r=document.createTreeWalker(e,NodeFilter.SHOW_ELEMENT,{acceptNode(i){return t?.from?.contains(i)?NodeFilter.FILTER_REJECT:i.matches(s)&&Io(i)&&(!t?.accept||t.accept(i))?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_SKIP}});return t?.from&&(r.currentNode=t.from),r}function Ey(){}function De(e,t){return re(e,t)}var cs=new Map,Il=new Set;function Al(){if(typeof window>"u")return;const e=n=>{if(!n.target)return;let s=cs.get(n.target);s||(s=new Set,cs.set(n.target,s),n.target.addEventListener("transitioncancel",t)),s.add(n.propertyName)},t=n=>{if(!n.target)return;const s=cs.get(n.target);if(s&&(s.delete(n.propertyName),s.size===0&&(n.target.removeEventListener("transitioncancel",t),cs.delete(n.target)),cs.size===0)){for(const r of Il)r();Il.clear()}};document.body.addEventListener("transitionrun",e),document.body.addEventListener("transitionend",t)}typeof document<"u"&&(document.readyState!=="loading"?Al():document.addEventListener("DOMContentLoaded",Al));function Ml(e,t){const n=Fl(e,t,"left"),s=Fl(e,t,"top"),r=t.offsetWidth,i=t.offsetHeight;let o=e.scrollLeft,a=e.scrollTop;const l=o+e.offsetWidth,c=a+e.offsetHeight;n<=o?o=n:n+r>l&&(o+=n+r-l),s<=a?a=s:s+i>c&&(a+=s+i-c),e.scrollLeft=o,e.scrollTop=a}function Fl(e,t,n){const s=n==="left"?"offsetLeft":"offsetTop";let r=0;for(;t.offsetParent&&(r+=t[s],t.offsetParent!==e);){if(t.offsetParent.contains(e)){r-=e[s];break}t=t.offsetParent}return r}var Ry={border:"0",clip:"rect(0 0 0 0)","clip-path":"inset(50%)",height:"1px",margin:"0 -1px -1px 0",overflow:"hidden",padding:"0",position:"absolute",width:"1px","white-space":"nowrap"};function Dr(e){const[t,n]=A(e.defaultValue?.()),s=D(()=>e.value?.()!==void 0),r=D(()=>s()?e.value?.():t());return[r,o=>{$e(()=>{const a=Cv(o,r());return Object.is(a,r())||(s()||n(a),e.onChange?.(a)),a})}]}function Oy(e){const[t,n]=Dr(e);return[()=>t()??!1,n]}function Ly(e){const[t,n]=Dr(e);return[()=>t()??[],n]}function Ao(e={}){const[t,n]=Oy({value:()=>U(e.open),defaultValue:()=>!!U(e.defaultOpen),onChange:o=>e.onOpenChange?.(o)}),s=()=>{n(!0)},r=()=>{n(!1)};return{isOpen:t,setIsOpen:n,open:s,close:r,toggle:()=>{t()?r():s()}}}function Ty(e,t){const[n,s]=A(Dl(t?.()));return q(()=>{s(e()?.tagName.toLowerCase()||Dl(t?.()))}),n}function Dl(e){return Dn(e)?e:void 0}function Ne(e){const[t,n]=ae(e,["as"]);if(!t.as)throw new Error("[kobalte]: Polymorphic is missing the required `as` prop.");return _(ht,re(n,{get component(){return t.as}}))}var Iy=Object.defineProperty,ts=(e,t)=>{for(var n in t)Iy(e,n,{get:t[n],enumerable:!0})},Ay={};ts(Ay,{Button:()=>Dy,Root:()=>En});var My=["button","color","file","image","reset","submit"];function Fy(e){const t=e.tagName.toLowerCase();return t==="button"?!0:t==="input"&&e.type?My.indexOf(e.type)!==-1:!1}function En(e){let t;const n=De({type:"button"},e),[s,r]=ae(n,["ref","type","disabled"]),i=Ty(()=>t,()=>"button"),o=D(()=>{const c=i();return c==null?!1:Fy({tagName:c,type:s.type})}),a=D(()=>i()==="input"),l=D(()=>i()==="a"&&t?.getAttribute("href")!=null);return _(Ne,re({as:"button",ref(c){var d=qe(u=>t=u,s.ref);typeof d=="function"&&d(c)},get type(){return ee(()=>!!(o()||a()))()?s.type:void 0},get role(){return!o()&&!l()?"button":void 0},get tabIndex(){return!o()&&!l()&&!s.disabled?0:void 0},get disabled(){return ee(()=>!!(o()||a()))()?s.disabled:void 0},get"aria-disabled"(){return!o()&&!a()&&s.disabled?!0:void 0},get"data-disabled"(){return s.disabled?"":void 0}},r))}var Dy=En;function _n(e){return t=>(e(t),()=>e(void 0))}var je=e=>typeof e=="function"?e():e,Ny=e=>{const t=D(()=>{const o=je(e.element);if(o)return getComputedStyle(o)}),n=()=>t()?.animationName??"none",[s,r]=A(je(e.show)?"present":"hidden");let i="none";return q(o=>{const a=je(e.show);return $e(()=>{if(o===a)return a;const l=i,c=n();a?r("present"):c==="none"||t()?.display==="none"?r("hidden"):r(o===!0&&l!==c?"hiding":"hidden")}),a}),q(()=>{const o=je(e.element);if(!o)return;const a=c=>{c.target===o&&(i=n())},l=c=>{const u=n().includes(c.animationName);c.target===o&&u&&s()==="hiding"&&r("hidden")};o.addEventListener("animationstart",a),o.addEventListener("animationcancel",l),o.addEventListener("animationend",l),Z(()=>{o.removeEventListener("animationstart",a),o.removeEventListener("animationcancel",l),o.removeEventListener("animationend",l)})}),{present:()=>s()==="present"||s()==="hiding",state:s,setState:r}},jy=Ny,Ls=jy,Ky={};ts(Ky,{Collapsible:()=>sr,Content:()=>od,Root:()=>ad,Trigger:()=>ld,useCollapsibleContext:()=>Mo});var id=He();function Mo(){const e=Te(id);if(e===void 0)throw new Error("[kobalte]: `useCollapsibleContext` must be used within a `Collapsible.Root` component");return e}function od(e){const[t,n]=A(),s=Mo(),r=De({id:s.generateId("content")},e),[i,o]=ae(r,["ref","id","style"]),{present:a}=Ls({show:s.shouldMount,element:()=>t()??null}),[l,c]=A(0),[d,u]=A(0);let h=s.isOpen()||a();return Mt(()=>{const g=requestAnimationFrame(()=>{h=!1});Z(()=>{cancelAnimationFrame(g)})}),q(Ve(a,()=>{if(!t())return;t().style.transitionDuration="0s",t().style.animationName="none";const g=t().getBoundingClientRect();c(g.height),u(g.width),h||(t().style.transitionDuration="",t().style.animationName="")})),q(Ve(s.isOpen,g=>{!g&&t()&&(t().style.transitionDuration="",t().style.animationName="")},{defer:!0})),q(()=>Z(s.registerContentId(i.id))),_(N,{get when(){return a()},get children(){return _(Ne,re({as:"div",ref(g){var m=qe(n,i.ref);typeof m=="function"&&m(g)},get id(){return i.id},get style(){return es({"--kb-collapsible-content-height":l()?`${l()}px`:void 0,"--kb-collapsible-content-width":d()?`${d()}px`:void 0},i.style)}},()=>s.dataset(),o))}})}function ad(e){const t=`collapsible-${Jn()}`,n=De({id:t},e),[s,r]=ae(n,["open","defaultOpen","onOpenChange","disabled","forceMount"]),[i,o]=A(),a=Ao({open:()=>s.open,defaultOpen:()=>s.defaultOpen,onOpenChange:d=>s.onOpenChange?.(d)}),l=D(()=>({"data-expanded":a.isOpen()?"":void 0,"data-closed":a.isOpen()?void 0:"","data-disabled":s.disabled?"":void 0})),c={dataset:l,isOpen:a.isOpen,disabled:()=>s.disabled??!1,shouldMount:()=>s.forceMount||a.isOpen(),contentId:i,toggle:a.toggle,generateId:Oo(()=>r.id),registerContentId:_n(o)};return _(id.Provider,{value:c,get children(){return _(Ne,re({as:"div"},l,r))}})}function ld(e){const t=Mo(),[n,s]=ae(e,["onClick"]);return _(En,re({get"aria-expanded"(){return t.isOpen()},get"aria-controls"(){return ee(()=>!!t.isOpen())()?t.contentId():void 0},get disabled(){return t.disabled()},onClick:i=>{It(i,n.onClick),t.toggle()}},()=>t.dataset(),s))}var sr=Object.assign(ad,{Content:od,Trigger:ld}),By=R("

"),Uy=R("

"),Vy=R(""),qy=R('

·

");break}r="none"}function a(u){r!==u&&(o(),s.push(u==="ul"?"

    ":"
      "),r=u)}function l(u){const f=[];return u.replace(/`([^`]+)`/g,(m,y)=>{const v=f.length;return f.push(y),`�CODE${v}�`}).replace(/\*\*(.+?)\*\*/g,"$1").replace(/\*(.+?)\*/g,"$1").replace(/\uFFFDCODE(\d+)\uFFFD/g,(m,y)=>{const v=f[Number(y)];return v!==void 0?`${v}`:m})}function c(){i.length>0&&(s.push(`

      ${i.join(" ")}

      `),i=[])}function d(u){if(!(u.startsWith("|")&&u.endsWith("|")))return r==="table"&&o(),!1;c();const f=u.slice(1,-1).split("|").map(h=>h.trim());return f.every(h=>/^[-:]+$/.test(h))?!0:r!=="table"?(o(),s.push(""),s.push(`${f.map(h=>``).join("")}`),s.push(""),r="table",!0):(s.push(`${f.map(h=>``).join("")}`),!0)}for(const u of n){const f=u.trimEnd();if(d(f))continue;const h=f.match(/^(#{1,6})\s+(.*)/);if(h){c(),o();const m=h[1].length;s.push(`${l(h[2])}`);continue}if(f.match(/^[-*]\s+/)){c(),a("ul"),s.push(`
    1. ${l(f.replace(/^[-*]\s+/,""))}
    2. `);continue}const g=f.match(/^\d+\.\s+(.*)/);if(g){c(),a("ol"),s.push(`
    3. ${l(g[1])}
    4. `);continue}if(o(),f.length===0){c();continue}i.push(l(f))}return c(),o(),s.join(` +`)}let qn=null;function xw(e){qn=e}function $w(e){qn===e&&(qn=null)}function Cw(){if(typeof window>"u")return;const e={version:1,closeChatStream:()=>qn?.close(),reconnectChatStream:()=>qn?.reconnect(),emitChatEvent:t=>qn?.emit(t)};window.__axinite=e}var Pw=R("
      "),kw=R("

      '),fc=R("

      ${l(h)}
      ${l(h)}
      '),_S=R('
      '),wS=R("

      '),xS=R("

      "),$S=R('

      ");const Tx={message:"message",tool_use:"tool-use",tool_result:"tool-result",status:"status",result:"result"};function Ix(e){const t=No(e);return{close:()=>t.close()}}function Ax(e,t,n){if(!("job_id"in e)||e.job_id!==t)return null;const s=new Date().toISOString();switch(e.type){case"job_message":return{key:n,kind:"message",message:`${e.role}: ${e.content}`,timestamp:s};case"job_tool_use":return{key:n,kind:"tool_use",message:`${e.tool_name} — ${vc(typeof e.input=="string"?e.input:JSON.stringify(e.input??{}))}`,timestamp:s};case"job_tool_result":return{key:n,kind:"tool_result",message:`${e.tool_name} — ${vc(e.output)}`,timestamp:s};case"job_status":return{key:n,kind:"status",message:e.message,timestamp:s};case"job_result":return{key:n,kind:"result",message:e.status,timestamp:s};default:return null}}function Mx(e){return{key:e.id,kind:"log",level:e.level,message:e.message,timestamp:e.timestamp}}function Fx(e,t,n){q(()=>{const s=e();n([]);let r=0;const o=(t()??Ix)(a=>{const l=Ax(a,s,`live-${s}-${r}`);l&&(r+=1,n(c=>[...c,l]))});Z(()=>o.close())})}const Dx=e=>{const{t}=ve(),n=()=>{const r=e.job().browse_url;return r&&As(r)?r:void 0},s=r=>r&&r.length>0?r:t("jobs-meta-unset");return _(_t.Content,{class:"jobs-tabs__content",value:"overview",get children(){return[(()=>{var r=mx();return p(r,()=>e.job().description),r})(),(()=>{var r=vx(),i=r.firstChild,o=i.firstChild,a=o.nextSibling,l=i.nextSibling,c=l.firstChild,d=c.nextSibling,u=l.nextSibling,f=u.firstChild,h=f.nextSibling,g=u.nextSibling,m=g.firstChild,y=m.nextSibling,v=g.nextSibling,b=v.firstChild,w=b.nextSibling,S=v.nextSibling,x=S.firstChild,k=x.nextSibling;return p(o,()=>t("jobs-meta-created")),p(a,()=>Er(e.job().created_at,t("timestamp-pending"))),p(c,()=>t("jobs-meta-elapsed")),p(d,(()=>{var P=ee(()=>!!e.job().elapsed_secs);return()=>P()?`${e.job().elapsed_secs}s`:t("jobs-elapsed-pending")})()),p(f,()=>t("jobs-meta-mode")),p(h,()=>s(e.job().job_mode)),p(m,()=>t("jobs-meta-kind")),p(y,()=>s(e.job().job_kind)),p(b,()=>t("jobs-meta-project")),p(w,()=>s(e.job().project_dir)),p(x,()=>t("jobs-meta-guardrail")),p(k,()=>t("page-jobs-guardrail")),r})(),(()=>{var r=bx(),i=r.firstChild;return p(i,()=>t("jobs-transitions-title")),p(r,_(N,{get when(){return e.job().transitions.length>0},get fallback(){return(()=>{var o=Xo();return p(o,()=>t("jobs-transitions-empty")),o})()},get children(){var o=yx();return p(o,_(oe,{get each(){return e.job().transitions},children:a=>(()=>{var l=xx(),c=l.firstChild,d=c.nextSibling;return p(c,()=>a.to),p(d,()=>Er(a.timestamp,t("timestamp-pending"))),p(l,_(N,{get when(){return a.reason},get children(){var u=Sx();return p(u,()=>a.reason),u}}),null),j(()=>me(c,Yo[a.to]??"pill pill--neutral")),l})()})),o}}),null),r})(),_(N,{get when(){return n()},children:r=>(()=>{var i=$x();return p(i,()=>t("jobs-browse-link")),j(()=>Q(i,"href",r())),i})()}),_(N,{get when(){return e.restartVisible()},get children(){var r=wx(),i=r.firstChild;return p(r,_(N,{get when(){return e.job().can_restart},get children(){var o=_x();return o.$$click=()=>e.onRestart(),p(o,()=>t("jobs-action-restart")),o}}),i),i.$$click=()=>e.onCancel(),p(i,()=>t("jobs-action-cancel")),j(()=>i.disabled=e.job().state!=="in_progress"&&e.job().state!=="pending"),r}})]}})},Nx=e=>{const{t}=ve(),n=s=>s.kind==="log"?s.level??t("jobs-activity-kind-status"):t(`jobs-activity-kind-${Tx[s.kind]}`);return _(_t.Content,{class:"jobs-tabs__content",value:"activity",get children(){var s=Cx();return p(s,_(N,{get when(){return e.activity().length>0},get fallback(){return(()=>{var r=Xo();return p(r,()=>t("jobs-activity-empty")),r})()},get children(){return _(oe,{get each(){return e.activity()},children:r=>(()=>{var i=Px(),o=i.firstChild,a=o.nextSibling,l=a.firstChild,c=l.nextSibling;return p(o,()=>n(r)),p(l,()=>Er(r.timestamp,t("timestamp-pending"))),p(c,()=>r.message),i})()})}})),s}})},jx=e=>{const{t}=ve();return _(_t.Content,{class:"jobs-tabs__content",value:"files",get children(){return[(()=>{var n=kx();return p(n,_(N,{get when(){return e.files().length>0},get fallback(){return(()=>{var s=Xo();return p(s,()=>t("jobs-files-empty")),s})()},get children(){return _(px,{get activePath(){return e.activePath()},get entries(){return e.files()},get label(){return t("jobs-file-tree-label")},get onSelect(){return e.onSelectFile}})}})),n})(),_(N,{get when(){return e.fileContent()},get children(){var n=Ex();return p(n,()=>e.fileContent()),n}})]}})},Kx=e=>{const{t}=ve(),n=Zn(),[s,r]=A(!1),[i,o]=A([]),a=D(()=>e.job().id),l=()=>n.resolvedFlags().action_job_restart,c=()=>e.job().job_kind===NS;Fx(a,()=>e.connectLive,o);const d=D(()=>[...e.events().map(Mx).sort((h,g)=>(h.timestamp??"").localeCompare(g.timestamp??"")),...i()]),u=()=>e.promptText().trim().length===0;return[(()=>{var f=Rx(),h=f.firstChild,g=h.firstChild,m=g.nextSibling,y=h.nextSibling,v=y.firstChild,b=v.nextSibling;return p(g,()=>t("jobs-detail-eyebrow")),p(m,()=>e.job().title),p(v,()=>Rr(e.job())),p(b,()=>t(`jobs-status-${io(e.job().state)}`)),j(w=>{var S=zd[Rr(e.job())]??"pill pill--neutral",x=Yo[e.job().state]??"pill pill--neutral";return S!==w.e&&me(v,w.e=S),x!==w.t&&me(b,w.t=x),w},{e:void 0,t:void 0}),f})(),_(_t,{class:"jobs-tabs",defaultValue:"overview",get children(){return[_(_t.List,{class:"jobs-tabs__list",get children(){return[_(_t.Trigger,{class:"jobs-tabs__trigger",value:"overview",get children(){return t("jobs-tab-overview")}}),_(_t.Trigger,{class:"jobs-tabs__trigger",value:"activity",get children(){return t("jobs-tab-activity")}}),_(_t.Trigger,{class:"jobs-tabs__trigger",value:"files",get children(){return t("jobs-tab-files")}}),_(_t.Indicator,{class:"jobs-tabs__indicator"})]}}),_(Dx,{get job(){return e.job},get onCancel(){return e.onCancel},get onRestart(){return e.onRestart},restartVisible:l}),_(Nx,{activity:d}),_(jx,{get activePath(){return e.activePath},get fileContent(){return e.fileContent},get files(){return e.files},get onSelectFile(){return e.onSelectFile}})]}}),(()=>{var f=Lx(),h=f.firstChild,g=h.firstChild,m=g.firstChild,y=m.nextSibling,v=y.firstChild,b=g.nextSibling;return m.$$input=w=>e.onPromptInput(w.currentTarget.value),p(y,_(N,{get when(){return c()},get children(){var w=Ox(),S=w.firstChild;return S.addEventListener("change",x=>r(x.currentTarget.checked)),p(w,()=>t("jobs-prompt-done-label"),null),j(()=>S.checked=s()),w}}),v),v.$$click=()=>e.onSubmitPrompt(s()),p(v,()=>t("jobs-prompt-send")),p(b,()=>t("jobs-prompt-safety")),j(w=>{var S=t("jobs-prompt-label"),x=t("jobs-prompt-placeholder"),k=!e.job().can_prompt||u();return S!==w.e&&Q(m,"aria-label",w.e=S),x!==w.t&&Q(m,"placeholder",w.t=x),k!==w.a&&(v.disabled=w.a=k),w},{e:void 0,t:void 0,a:void 0}),j(()=>m.value=e.promptText()),f})()]};Ae(["click","input"]);function Bx(){return xe("/api/jobs")}function Ux(){return xe("/api/jobs/summary")}function Vx(e){return xe(`/api/jobs/${e}`)}function qx(e){return xe(`/api/jobs/${e}/events`)}function Hx(e){return xe(`/api/jobs/${e}/files/list`)}function zx(e,t){const n=new URL(`/api/jobs/${e}/files/read`,window.location.origin);return n.searchParams.set("path",t),xe(`${n.pathname}${n.search}`)}function Wx(e){return ke(`/api/jobs/${e}/restart`)}function Qx(e){return ke(`/api/jobs/${e}/cancel`)}function Gx(e,t){return ke(`/api/jobs/${e}/prompt`,t)}var Jx=R("

      '),Xx=R("

      "),Zx=R("